手机版

为什么JavaScript没有块级作用域?

时间:2021-10-29 来源:互联网 编辑:宝哥软件园 浏览:

最近看ES2015的实战,里面有一句话。

JavaScript中没有块级作用域。

你可能不理解这个问题。我们先来看一个例子。

var a=[]for(var I=0;i 10I){ a[I]=function(){ console . log(I);} } a[6](;我想很多人认为这个问题的结果是6,但很遗憾,答案是10。试试别的。a[7]()、a[8]()和a[8]()的结果都是10!因为JS在处理基元变量的时候经常会把基元变量打包成对应的对象,例如,对于var str=' hello worldstr.slice的真实过程(1)。JS大概就是var str=' hello world新字符串。切片(1)。这个过程可能会给我们理解问题带来麻烦。为了解释这个问题,并且我属于原语类型中的number类型,我显式地将其声明为Number类型。因为基本类型的赋值过程是重新应用内存和修改变量指向的过程,所以我们也使用重新新建Number对象的过程来模拟这个过程。修改后的代码如下:

var a=[]var i=新数字(0);for(;i 10I=new Number(I ^ 1)){ a[I]=function(){ console . log(I . tostring());} } a[6](;//10a[7]();//10a[8]();//10a[9](;//10下面我们用一个程序来看看这些变量的相对内存地址。

(function(){ var id=0;函数generateId() {返回id;};object . prototype . id=function(){ var new id=generateId();this . id=function(){ return NewID;};返回新标识;};})();var a=[]var i=新数字(0);console . log(I . id());//0 for(;i 10I=new Number(I ^ 1),I . id()){ a[I]=function(){ console . log(I . id());console . log(I . ToString());} } a[6]();//10 10a[7]();//10 10a[8]();//10 10a[9]();//10 10console.log(i.id())//10在这里,我们真的模拟了我们的I的整个“赋值”效果,I的相对地址从0变成了10(最后需要重新添加,才能跳出for循环)。在查看I的相对地址时,我们发现了一个问题:a [x] (x33600)。这里,将涉及块级范围的问题。这里引用一下ES6入门中阮一峰的:

ES5只有全局作用域和函数作用域,没有块级作用域。

ES5是JS最广泛使用的版本。在javascript中,没有块范围,只有全局范围和块级范围。如何理解?例如

for(var I=0;i 10I){ console . log(I);} console . log(I);//10 console . log(window . I);//10直观上,我们认为for循环是一个代码块,应该属于块级范围。但是这里可以正常输出0 ~ 9,在for循环外可以输出10。同时我们发现,虽然我们在for循环上定义了I,但是看起来I是挂在全局窗口对象上的(如果是nodejs的执行环境,就会挂在全局对象上)。

因此,在JavaScript中,像for循环这样的块不扮演块级作用域的角色。在代码块(如for循环)中定义变量与在当前范围内直接定义变量没有什么不同。

但是我们可以通过函数将作用域与:隔离。

(function(){ for(var I=0;i 10I){ console . log(I);} console . log(I);})()console . log(I);////i未定义,如果执行console . log(window . I);你会得到未定义的结果。这里,我们使用一个立即函数来形成一个作用域,它就像一个代码块。这个函数作用域之后,就不能再访问变量I了,但是可以在函数作用域内随意访问I。回到前面的问题,结合JavaScript中的全局作用域和块级作用域,再来理解一遍。在for循环中,我必须被定义在当前范围内,即窗口范围内。在循环体中,我们给a[i]分配一个函数。当我们执行这个函数时,情况如下。

函数中没有I,所以我是沿着作用域链在窗口作用域中被发现的。我们此时输出的I是这个I,因为跳出了循环中最后一个1,变成了10,所以输出结果总是10。但我们真正需要的不是最后的我,而是中间过程的我。如果我们想解决这个问题,我们需要抛开变量I(因为最后一个I不可避免地变成10)。我们需要让a[0]对应的函数引用值0,让a[1]对应的函数引用值1。如下图所示,

回到我们面前的代码。

图中箭头表示我们可以正确访问i(0~9)。这里,因为for循环本身并不形成块级作用域,所以当我们按照作用域链访问I时,我们访问由for循环定义的I。这里,我们用一个立即执行函数包装我们的代码以形成一个范围,同时,我们为它传递值I如下:

var a=[]var i=新数字(0);console . log(I . id());//0 for(;i 10I=new Number(I ^ 1),I . id()){(function(I){ a[I]=function(){ console . log(I . id());console . log(I . ToString());} })(I);a[6]();//6 6a[7]();//7 7a[8]();//8 8a[9]();//9 9 console . log(I . id());//10}

var a=[];for(var I=0;i 10I){(function(I){ a[I]=function(){ console . log(I);} })(I);}最后,我们来看看ES6的语法,它推荐使用let来代替var,以及bable生成ES5的代码是如何生成:的。

//ES6代码var a=[]for(设I=0;i 10I){ a[I]=function(){ console . log(I);} } a[6](;//ES5代码‘使用严格’由//babel编译生成;var a=[];var _ loop=function _ loop(I){ a[I]=function(){ console . log(I);};};for(var I=0;i 10I){ _ loop(I);} a[6]();

看看我们的解决方案是否与ES6相似。这里,我们的立即执行函数相当于在生成的ES5代码中执行_loop函数和_loop(i)。

版权声明:为什么JavaScript没有块级作用域?是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。