var关键字——它到底干了什么!

思考题

开始这篇文章前,请先思考一下下面代码的输出分别是什么?

// example 1
function foo() {
    
    
  console.log(a);
  a = 1;
}
foo(); // ???

// example 2
function bar() {
    
    
  b = 2;
}
bar();
console.log(b); // ???

// example 3
function baz() {
    
    
  console.log(c);
  var c = 3;
}
baz(); // ???
console.log(c); // ???

前置知识:执行上下文和变量对象

可以阅读@冴羽大神的博客JavaScript 深入之变量对象

执行上下文(execution context):当 JavaScript 代码执行一段可执行代码(executable code)时,其所创建的对应的执行环境。可执行代码包括全局代码函数代码eval 代码,所以也分成全局执行上下文函数执行上下文eval 执行上下文

每个执行上下文中包含了三部分:

  • 变量对象(Variable Object, OV):与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。在函数执行上下文中又叫做激活对象(Activated Object, AO)。
  • 作用域链(Scope Chain):由多个执行上下文的变量对象构成的链表。每个作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限
  • this

var 关键字做了什么?

  1. 使用 var 关键字声明变量时,如var a = 'a'当进入该执行上下文时,会在其执行上下文中的变量对象中声明该变量a: undefined,即声明提升(hoist),等解析到赋值语句时再更新这个变量的值

  2. 假如执行上下文是函数执行上下文,则这个变量是局部变量,如果是全局执行上下文,则其声明的是一个全局变量。

  3. 访问这些变量时,会严格按照作用域链访问

比如例子 3,c 是局部变量,当 baz 的执行上下文弹出后,在全局执行上下文的变量对象中并没有声明 c,且作用域链未指向 baz 的变量对象,所以报错c is not defined

不使用 var 声明变量

  1. 如果未使用 var 关键字,则隐性声明一个全局对象属性,如b = 'b',则是在全局对象上增加一个属性b,其值为'b'

  2. 全局执行上下文的变量对象寄宿在全局变量对象上,所以不使用 var 声明的对象在所有执行上下文中都可以访问

  3. 不会声明提升,遇到声明赋值语句时,直接在全局对象上添加该属性

比如例子 1 未使用隐性声明变量 a ,不存在变量声明提升,所以执行到 console.log(a) 语句时,foo 函数执行上下文中的激活对象中没有声明变量 a,所以报错a is not defined

全局变量对象与全局对象

全局对象在不同实现中表示也不同,比如浏览器端就是 windownode.js 中就是 global,全局对象是全局变量对象的宿主,所以在全局变量对象中可以访问全局对象中的属性

全局变量对象中的属性无法通过 delete 删除,而全局对象的属性可以通过 delete 删除。其实质是全局变量上的属性描述符 configurable 被设置为了 false,所以无法删除、

在这里插入图片描述

解答

  • 例子 1 的输出是a is not defined
  • 例子 2 的输出是2
  • 例子 3 的输出是undefinedc is not defined

参考

猜你喜欢

转载自blog.csdn.net/sinat_36521655/article/details/107942764