JS:全局环境var a=1,函数环境a=3。请问函数里面的a是修改全局的a,还是新建一个变量a?

<script>
    var a=1;//全局环境下,用var声明一个全局变量
    var b=function(){a=3}();//函数内的a没有用var声明
    alert(a);//a的值为3
    alert(delete a)//false
</script>

“如果你没有使用严格模式并给一个未声明的变量赋值的话,JS会自动创建一个全局变量。以这种方式创建的变量,是全局对象的正常可配置属性,并可以删除它们。”--------犀牛书6版,58页。

var b=function(){a=3}() 内的a没有用var声明,那么应该创建一个可配置的全局变量。但是全局环境有一个变量a,并且是用var声明过的。

按照我这个初学者的认识:
var b=function(){a=3}() 内的a应该覆盖掉全局环境下a的值,并且a应该是可用delete删除的。情况a的值确实变为3了,但delete a却不可以,请问这是为什么?谢谢。

阅读 9.9k
8 个回答

首先,上个例子:

a = 1    // 1
this.a   // 1
window.a // 1
delete a // true


var b = 2// 2
this.b   // 2
window.b // 2
delete b // false

好,从例子看以看出,未通过var声明的变量a和通过var声明的变量b,都可以通过this和window访问到,区别是一个可以被delete,一个不可以,下面分析下:

为什么delete的结果不同?

因为(在全局下)通过var 声明的变量是全局变量,而未通过var声明的变量是附在this,也就是window上的属性,而属性是可以被删除的。

为什么他们都可以被this和window访问到?

在浏览器中,所以的变量都是存在于window下的,所以可以被window访问到;
而this是指向window的一个引用,未通过var声明的变量通过附在this上,达到可以全局访问的效果。

下面说说你的例子

var a = 1   // 全局都可以 window、this
a = 3       // 全局都可以 window、this  覆盖上一个a的值 现在a是3
delete a    // 虽然值被覆盖了,但是a毕竟是通过var声明的,所以无法删除,所以是false

“如果你没有使用严格模式并给一个未声明的变量赋值的话,JS会自动创建一个全局变量。以这种方式创建的变量,是全局对象的正常可配置属性,并可以删除它们。”--------犀牛书6版,58页。

<script>
var a=1;//全局环境下,用var声明一个全局变量
var b=function(){a=3}();//函数内的a没有用var声明
alert(a);//a的值为3
alert(delete a)//false
</script>

var a = 1 已经在全局执行上下文中声明了变量 a,并且赋值为 1. 所以不符合书中描述的条件!

var a; 这条语句一执行,window 的属性a 对应的属性描述符对象(property descriptor)就确定了:

// in brower console
var a;
Object.getOwnPropertyDescriptor(window, 'a'); // {value: undefined, writable: true, enumerable: true, configurable: false} 

configurable: false 就意味着 不能删除!

js变量的作用域需要涉及到作用域链,如:http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain...

1、函数运行时会有一个执行上下文,函数里用到的变量先去一个叫做活动对象的作用域查找该变量是否在这个类似“堆栈”的作用域链;
2、如果没有,则再去一个叫做全局对象的作用域链查找该变量;
3、每次使用变量都会重复去查找一次这个作用域链,所以为了在循环中提高代码效率,尽量将全局变量赋值给一个局部变量,解决全局变量在反复查找过程中带来的效率(全局作用域链在局部作用域链下面)

function里面的a = 3并没有像你说的那样重新创建一个变量a并且覆盖掉以前的var a;在function里面出现对a的引用,会在函数作用域里面寻找a的定义,没有找到则在函数的作用域链的下一层即global里面寻找。找到a之后对a重新赋值为3。delete的语境等价于var a = 3;delete a;当然是false。如果你把function里面的a换成是c或者其它未定义的变量再delete c,就是true.关于作用域,执行环境的相关部分我建议阅读《Javascript 高级程序设计》的第四章。

因为a不是一个隐式声明的变量!

// 隐式声明变量
a = 1;
delete a; // true

// 显示声明变量
var a = 1;
delete a; // false

返回值:在严格模式中,如果属性是一个不可配置(non-configurable)属性,删除时会抛出异常,非严格模式下返回 false。其他情况都返回 true。

MDN delete

所以说a=3只是把全局变量的a给覆盖了!

a已经被var声明了,所以是不能够被删除的,但是 var b=function(){a=3}();后面加了一个小括号是怎么回事?匿名函数?

@qianjiahao 已经说的很好了
使用var 是声明变量,声明变量自带不可删除属性,而非var 产生的变量为window的属性,所以可以删除

看了某些回答我觉得不是很合适。只解释一下为什么未声明变量可以delete,而声明变量不可。

变量存在声明变量和未声明变量两种,两者大体存在3个区别:

  1. 变量声明只在当前作用域上下文中有效,但未声明变量在赋值后会成为全局作用域的一种属性,所以在全局环境下都有效

  2. 创建方式不同,变量声明不需要赋值(var a;)就可以创建,而未声明变量需要赋值(a=1;)才可以创建

  3. 声明变量的是当前作用域的不可配置属性,而未声明变量属于全局作用域的可配置属性。(通过Object.getOwnPropertyDescriptor()方法可以查看)

我们可以这么理解:隐式全局变量并不算是真正的变量,它本质上是全局对象的属性成员。属性是可以通过delete运算符删除的,而变量不可以被删除。
如果有兴趣的话,建议继续阅读以了解官方的delete机制。

MDN上,delete操作符的机制是:

  1. 如果你删除的属性在对象上不存在,那么delete将不会起作用,但仍会返回true

  2. 如果 delete 操作符删除成功,则被删除的属性将从所属的对象上彻底消失。然后,如果该对象的原型链上有一个同名属性,则该对象会从原型链上继承该同名属性。(也就是说delete操作只会在自身的属性上起作用)

  3. 任何使用 var 声明的属性不能从全局作用域或函数的作用域中删除,同时delete操作不能删除任何在全局作用域中的函数(无论这个函数是来自于函数声明或函数表达式)

  4. 除了在全局作用域中的函数不能被删除,在对象(object)中的函数是能够用delete操作删除的。

  5. 任何用let或const声明的属性不能够从它被声明的作用域中删除。

  6. 不可设置的(Non-configurable)属性不能被移除。这意味着像Math, Array, Object内置对象的属性以及使用Object.defineProperty()方法设置为不可设置的属性不能被删除。

所以,delete机制并不是通过可配置属性与不可配置属性就可以完全划分的。

国内关于delete操作符的文章很少,除了MDN还算说得过去外没什么可以参考的资料。最近看到一个好的文章,有兴趣的话可以读一下:Understanding delete
至于解决本题另一个知识点:执行环境与作用域链,其他回答已经很完善了。

希望有所帮助。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题