扫码关注官方订阅号
(function(){ a = 5; alert(window.a); var a = 10; alert(a); })();
这段代码中的alert,为什么第一个是返回的undefined.按说,不要VAR的a变量,不就是全局变量吗?
闭关修行中......
这个问题很有趣,但也很难真的完整的回答,至少我会认为它在不同的浏览器品牌中或版本(尤其IE)中,行为有有极大差异。以下只是我的的测试结果与个人理解,如果你在不同的浏览器上测试,或许有不同的答案。为了简化起见,太细节的资料就略过
首先要理解的是,在浏览器全域中有个window物件(在Node.js是global物件),在全域中共有四种可能的宣告变量的方式:
window
global
a = 5; var a = 5; window.a = 5; this.a = 5;
再加上ES6(ES2015)的let与const,又会多了两种。这两种先不在这里讨论,我只看这个问题中的var方式。
let
const
var
那么它们会有什么差异?而且有哪些是相等的?用一个简单的测试程式码来看:
"use strict"; //这里切换严格模式 var a = 5 //这里更动试试 console.log(window.a); console.log(this.a); console.log(a);
你会得到类似的答案,这三个都是5。在严格模式(strict code/mode)情况下,只有a=5这个测试直接报错无法执行。
a=5
第二个测试是加上IIFE(立即执行函式定义),来看这几种情况在函式里的是否能存取:
"use strict"; //这里切换严格模式 a = 5; //这里更动试试 (function(){ console.log(window.a); console.log(this.a); console.log(a); })()
在这组测试中,明显的在严格模式中,产生很多的错误,尤其是a = 5是全部报错,其它的则是在函式中的this.a会报错。
a = 5
this.a
最后一个测试是这个程式码,它只是用来对照这个问题中的在IIFE函式中如果有使用a = 5,a变量会转为全域的情况,当然你也可以代换用其他的window.a试试:
window.a
//"use strict"; //这里切换严格模式 (function(){ a = 5; console.log(window.a); console.log(this.a); console.log(a); })()
根据测试结果与相关文件来看,window.a与this.a是一致的,因为在全域中的this相当于window物件。当然因为this到了函式中是另一回事,一般不会这样直接使用,不过这里的IIFE的呼叫者是全域物件,this也相当于window物件,刚好是个对应到的情况。不过因为严格模式对于函式有非常严密的规定,在函式中的this无法再对应到window物件,这一点是要注意的。
this
var a = 5;这是一个正常情况下的变量宣告,也是一般情况下应该这样作的方式,而且它需要定义在所有执行或函式呼叫的最上面。它与window.a的定义不同的地方在于,JavaScript会把它视为原始资料类型的定义,而非window物件中的一个属性值,虽然用起来都一样,但内部的实作是有差异的。
var a = 5;
window物件
要怎么知道差异在哪?最明显的例子是用delete运算符,它是用来删除物件中的属性用的。你可以把上面四种宣告出来的变数用下面的这语句来删除看看,因为delete会回传删除成功与否的布林,所以你可以看到是不是能被删除。不过在严格模式中,delete是无法使用的。
delete
console.log(delete a);
根据测试后,只有var a = 5;这种宣告方式,是无法被删除,它并不真正算是window物件的属性而已。另外藉由对window物件进行(for...in),也可以得到一样的结果。
那么a = 5这种宣告方式又是什么?它其实是隐喻式的window.a宣告方式,也就是说如果window.a = 5是明确地来指定window物件的属性a为5,a = 5就是暗地里把window物件的属性指定为5。
window.a = 5
不过在IE8之前中的测试资料显示,a = 5与var a = 5的意义相等,这又是浏览器品牌与版本实作的可怕差异之处。所以这个宣告方式,是一种极为不建议的陷阱语法。
var a = 5
回到题目本身的程式码来看,另外有一个变数特性重点,称之为提升(Hoisting),提升的层次是局限在函式中的,大致的情况会是,所有的以var(let与const相同行为)宣告的变量,在执行前会先提升宣告部份到函式区块中的最前面,但要特别注意的是它的赋值(指定值)部份是不会被一并提升的,所以会变为先被赋值为undefined。
undefined
所以原本的程式码在执行前的变量被提升如下:
(function(){ var a; a = 5; alert(window.a); a = 10; alert(a); })();
所以到执行到a = 5这行时,a是一个只在函式区块中的区域变量,而非像上面所言的成为全域window物件中的属性,也因此在window.a要取值时,必定是取到undefined。你可以对比上面的问题,另外宣告一个b=100,看看会不会取得到window.b范例如下:
a
b=100
window.b
(function(){ a = 5; b = 100; alert(window.a); alert(window.b); var a = 10; alert(a); })();
这个问题是个典型而常见的陷阱题目,在面试时也常被拿出来考试。要很清楚地回答它需要对JavaScript语言中的内部设计有一定理解。当然,你也发觉有很多地方在最新的浏览器中或严格模式中被修改或拿掉,代表这些都是很容易造成错误的设计。
其他资讯可以参考: http://stackoverflow.com/ques...
变量提升啊,下面有var a 啊,你知道变量提升吗?
程序运行分为两个阶段,第一是编译阶段(在当前作用域注册变量),第二是运行阶段。
程序首先编译这段代码,其他都不管,只关注变量和函数的声明。首先看到var a,就在你的当前作用域IIFE函数中注册a这个变量。
var a
然后进入运行阶段。遇到a = 5时,首先是寻找a这个变量是否已经在当前作用域注册了?如果已注册,就使用当前作用域的a,并给他赋值5.如果在当前作用域没找到已注册的变量a,这时就会向外一层作用域寻找,也就是全局作用域寻找a。
随后执行alert(window.a),这时是寻找window对象的a属性,此时window对象并没有a属性,所以此时结果是undefined。如果你在IIFE函数外赋值a = 20,这时结果就是20了。
alert(window.a)
a = 20
随后a = 10寻找变量a并赋值10(运行过程同第三段)。
a = 10
最后alert(a),查找当前作用域是否有变量a,如果有,则返回该变量的值。否则向外一层寻找,如果在全局作用域都没有找到a,这时程序就会报ReferenceError: a is not defined的错误。
alert(a)
ReferenceError: a is not defined
关于作用域,推荐You Don't Know JS
自己待填坑的文章
嗯,变量提升,变量声明会提前到最前面,与输写的顺序无关。
之前的回答感觉有不正确的地方,自己下去又研究了下,`
(function(){ a = 5; alert(window.a); var a = 10; alert(a); })();`
这个代码中a=5在程序执行时这个a是局部变量a,而不是全局变量a,因为下面的var a=10;导致变量声明提升到程序最顶部,也就是a=5之前了,所以执行a=5时其实是对局部变量的赋值。window.a中的a是window对象的一个属性,因为从未赋值,所以一直是未定义,这句话放在这个匿名函数内的任何位置输出都是未定义。
微信扫码关注PHP中文网服务号
QQ扫码加入技术交流群
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
PHP学习
技术支持
返回顶部
这个问题很有趣,但也很难真的完整的回答,至少我会认为它在不同的浏览器品牌中或版本(尤其IE)中,行为有有极大差异。以下只是我的的测试结果与个人理解,如果你在不同的浏览器上测试,或许有不同的答案。为了简化起见,太细节的资料就略过
首先要理解的是,在浏览器全域中有个
window物件(在Node.js是global物件),在全域中共有四种可能的宣告变量的方式:再加上ES6(ES2015)的
let与const,又会多了两种。这两种先不在这里讨论,我只看这个问题中的var方式。那么它们会有什么差异?而且有哪些是相等的?用一个简单的测试程式码来看:
你会得到类似的答案,这三个都是5。在严格模式(strict code/mode)情况下,只有
a=5这个测试直接报错无法执行。第二个测试是加上IIFE(立即执行函式定义),来看这几种情况在函式里的是否能存取:
在这组测试中,明显的在严格模式中,产生很多的错误,尤其是
a = 5是全部报错,其它的则是在函式中的this.a会报错。最后一个测试是这个程式码,它只是用来对照这个问题中的在IIFE函式中如果有使用
a = 5,a变量会转为全域的情况,当然你也可以代换用其他的window.a试试:根据测试结果与相关文件来看,
window.a与this.a是一致的,因为在全域中的this相当于window物件。当然因为this到了函式中是另一回事,一般不会这样直接使用,不过这里的IIFE的呼叫者是全域物件,this也相当于window物件,刚好是个对应到的情况。不过因为严格模式对于函式有非常严密的规定,在函式中的this无法再对应到window物件,这一点是要注意的。var a = 5;这是一个正常情况下的变量宣告,也是一般情况下应该这样作的方式,而且它需要定义在所有执行或函式呼叫的最上面。它与window.a的定义不同的地方在于,JavaScript会把它视为原始资料类型的定义,而非window物件中的一个属性值,虽然用起来都一样,但内部的实作是有差异的。要怎么知道差异在哪?最明显的例子是用
delete运算符,它是用来删除物件中的属性用的。你可以把上面四种宣告出来的变数用下面的这语句来删除看看,因为delete会回传删除成功与否的布林,所以你可以看到是不是能被删除。不过在严格模式中,delete是无法使用的。根据测试后,只有
var a = 5;这种宣告方式,是无法被删除,它并不真正算是window物件的属性而已。另外藉由对window物件进行(for...in),也可以得到一样的结果。那么
a = 5这种宣告方式又是什么?它其实是隐喻式的window.a宣告方式,也就是说如果window.a = 5是明确地来指定window物件的属性a为5,a = 5就是暗地里把window物件的属性指定为5。不过在IE8之前中的测试资料显示,
a = 5与var a = 5的意义相等,这又是浏览器品牌与版本实作的可怕差异之处。所以这个宣告方式,是一种极为不建议的陷阱语法。回到题目本身的程式码来看,另外有一个变数特性重点,称之为提升(Hoisting),提升的层次是局限在函式中的,大致的情况会是,所有的以
var(let与const相同行为)宣告的变量,在执行前会先提升宣告部份到函式区块中的最前面,但要特别注意的是它的赋值(指定值)部份是不会被一并提升的,所以会变为先被赋值为undefined。所以原本的程式码在执行前的变量被提升如下:
所以到执行到
a = 5这行时,a是一个只在函式区块中的区域变量,而非像上面所言的成为全域window物件中的属性,也因此在window.a要取值时,必定是取到undefined。你可以对比上面的问题,另外宣告一个b=100,看看会不会取得到window.b范例如下:这个问题是个典型而常见的陷阱题目,在面试时也常被拿出来考试。要很清楚地回答它需要对JavaScript语言中的内部设计有一定理解。当然,你也发觉有很多地方在最新的浏览器中或严格模式中被修改或拿掉,代表这些都是很容易造成错误的设计。
其他资讯可以参考: http://stackoverflow.com/ques...
变量提升啊,下面有var a 啊,你知道变量提升吗?
程序运行分为两个阶段,第一是编译阶段(在当前作用域注册变量),第二是运行阶段。
程序首先编译这段代码,其他都不管,只关注变量和函数的声明。首先看到
var a,就在你的当前作用域IIFE函数中注册a这个变量。然后进入运行阶段。遇到
a = 5时,首先是寻找a这个变量是否已经在当前作用域注册了?如果已注册,就使用当前作用域的a,并给他赋值5.如果在当前作用域没找到已注册的变量a,这时就会向外一层作用域寻找,也就是全局作用域寻找a。随后执行
alert(window.a),这时是寻找window对象的a属性,此时window对象并没有a属性,所以此时结果是undefined。如果你在IIFE函数外赋值a = 20,这时结果就是20了。随后
a = 10寻找变量a并赋值10(运行过程同第三段)。最后
alert(a),查找当前作用域是否有变量a,如果有,则返回该变量的值。否则向外一层寻找,如果在全局作用域都没有找到a,这时程序就会报ReferenceError: a is not defined的错误。关于作用域,推荐You Don't Know JS
自己待填坑的文章
嗯,变量提升,变量声明会提前到最前面,与输写的顺序无关。
之前的回答感觉有不正确的地方,自己下去又研究了下,`
这个代码中a=5在程序执行时这个a是局部变量a,而不是全局变量a,因为下面的var a=10;导致变量声明提升到程序最顶部,也就是a=5之前了,所以执行a=5时其实是对局部变量的赋值。window.a中的a是window对象的一个属性,因为从未赋值,所以一直是未定义,这句话放在这个匿名函数内的任何位置输出都是未定义。