Skip to content

Latest commit

 

History

History
217 lines (156 loc) · 5.52 KB

let与作用域.md

File metadata and controls

217 lines (156 loc) · 5.52 KB

var声明

1.var声明的变量会进行比变量提升

2.var声明的变量会在window上添加一个属性

let与const声明

1.let/const不可重复定义

2.const值不可改变,但如果是引用类型,则其地址不可更改,但其内部属性可变

let/const有没有作用域提升

词法环境被实例化时(执行上下文被创建时),let/const声明的变量就已经创建了,但不能访问,直到被词法绑定时被求值。

以下代码,在代码解析时,执行到console.log(foo),foo已经被创建了,但是不能被访问。

console.log(foo)
let foo = 'flten'

let/const与window

1.letconst如何进行保存

结论:变量保存在VaraiableMap

每一个执行上下文会关联到一个变量环境VariableEnvrionment,在执行代码中变量和函数声明会作为环境记录添加到变量环境中。

2.(新)VE 与 VO(旧) 的不同:

对于 V8 引擎来说,VE 对应的变量会存放在variable_:VaraiableMap,是一个哈希表的结构

而之前的实现中,var声明的变量会存放在GO中,而GO会关联到window上,因此早期GOwindow是等价的。现在的实现中,window是浏览器添加的全局变量,并且会保持window上挂载的属性和var声明变量的相等性,但二者应该不再是同一个对象,而仅仅是为了兼容之前的浏览器实现。

创建作用域的文件:scopes.cc

块级作用域

1.ES5中没有块级作用域,只有全局作用域函数作用域。内层作用域会通过作用域链访问外层作用域的变量.

2.ES6 中提供了块级作用域,但要注意代码块声明的块级作用域对var无效,对let/const/function/class声明有效

{
   var f1 = 'f1'
   let f2 = 'f2'
}
console.log(f1)  // f1
console.log(f1) // 报错

3.注意📢:对函数的声明,大部分浏览器为了兼容旧代码,使得function声明无块级作用域

{
   function foo(){
       console.log('foo')
   }
}
foo() // foo

4.但块级作用域对class声明有效,因为早期标准中没有class声明,不存在历史负担

{
   class Person{}
}
let p = new Person() // ReferenceError: Person is not defined

5.if/switch/for代码块也是块级作用域

if语句

if(true){
   var f1 = 'flten'
   let f2 = 'me'
}
console.log(f1) // flten
console.log(f2) // f2 is not defined

switch语句

switch(true){
    case true:
        var f1 = 'flten'
        let f2 = 'me'
}

console.log(f1)  // flten
console.log(f2) // ReferenceError: f2 is not defined

for语句:

(1)var声明

for (var index = 0; index < 10; index++) {
    console.log(index)
}
console.log(index)  // 10

(2)let 声明

for (let index = 0; index < 10; index++) {
    console.log(index)
} 
console.log(index)  // ReferenceError: index is not defined

块级作用域的应用

下面👇的每个按钮点击时,因为var没有块级作用域,因为向上层作用域(全局作用域)去查找变量index,而全局的index已经变为5

<body>
    <button>1</button>
    <button>2</button>
    <button>3</button>
    <button>4</button>
    <button>5</button>
    <script>
        const btns = document.getElementsByTagName('button')
        for (var index = 0; index < btns.length; index++) {
            const element = btns[index];
            element.onclick = function(){
                console.log(index) 
            }
        }
    </script>
</body>

使用立即执行函数生成函数作用域,每一层循环就多形成一个函数作用域,保持对应的变量值index。点击按钮时,执行回调函数,向上层作用域(这时候是函数作用域)找到变量index

const btns = document.getElementsByTagName('button')
for (var index = 0; index < btns.length; index++) {
    (function(index){
        const element = btns[index];
        element.onclick = function(){
            console.log(index)
        }
    })(index)
}

使用let增加块级作用域,每一次循环都会增加一个块级作用域保持变量值index。点击按钮时,执行回调函数,直接在对应的块级作用域中找到index

const btns = document.getElementsByTagName('button')
for (let index = 0; index < btns.length; index++) {
    const element = btns[index];
    element.onclick = function(){
        console.log(index)
    }
}

但是上面的代码是不能使用const定义index的,因为const定义的变量不能够进行自增操作。

但在不自增的情况下,是可以进行遍历循环的

const arr = [1,2,3]

for (const iterator of arr) {
    console.log(iterator)
}

暂时性死区

使用let/const声明的变量,在声明之前,是不可使用的。

var foo = 'flten'

if(true){
    console.log(foo) // flten
}

使用let声明后会出现暂时性死区。下面的代码中,尽管全局作用域中声明了变量foo,但是在if块级作用域中,使用了let声明,这里便形成了暂时性死区,if代码块内,foo不能在声明前被使用。

var foo = 'flten'

if(true){
    console.log(foo) 
    let foo = 'wall' // ReferenceError: Cannot access 'foo' before initialization
}

var/let/const的使用选择

1.不使用var

2.优先使用const

var与let的区别总结

1.作用域提升

2.是否挂载到了window

3.块级作用域