作用域和作用域链方面的知识是JS的重点,去面试十个有八个都会问你这块的知识,所以说这块是特别特别的重要,下面我们好好理解一下作用域和作用域链到底是个什么:
先上一段代码:
var a = 'jack'; function fn() { var a = 'frank'; } console.log(a);
我们在函数里定义了一个a变量,在函数外也定义了一个a变量,那最后输出的应该是哪一个a的值呢?
这个时候就有了作用域这个概念了,简单地说作用域就是限制某个变量只能在某个区域内有效。
作用域有全局作用域和局部作用域之分,变量同样如此,在上例中,第一个a很显然是一个全局变量,函数内的a显然是局部变量。全局变量拥有全局作用域而局部变量拥有局部作用域。这道题里console.log是在全局里调用a,那么毋庸置疑最后输出的一定是'jack'。
这个时候我把函数代码块改为if代码块,看看最后应该输出什么呢
var a = 'jack'; if(true) { var a = 'frank'; } console.log(a);
最后的结果a输出的是'frank'。
实际上这里有一个大坑,千万不要以为大括号封起来就一定是封闭环境,if里面的语句执行完后就会自动销毁了,但是在javascript里if内部定义的变量就会变为当前执行环境的变量。当前执行环境在最外围,所以if里面的a就变为全局变量了
我们再来看下面这段代码分别应该输出什么呢?
for(var i = 0;i<3;i++) { break; } console.log(i); k = 5; while(k>1) { k--; var d = 10; } console.log(k); console.log(d);
除了if代码块还有我们常见的for循环,while循环也是相似的结果,我们不要被括号给迷惑了,在括号内定义的变量不一定就是局部作用域,因此这里的i,k,d变量都是全局变量,这是输出结果:
下面结合es6新增的块级作用域做一个总的概括:
- 在ES6中只要{ }没有和函数结合在一起,那么应该就是“块级作用域”。
- 在块级作用域中,var定义的变量是全局变量,let定义的变量是局部变量。
- 而在局部作用域也就是函数作用域中,无论是用var定义的变量还是用let定义的变量都是局部变量。
- 无论是在块级作用域还是局部作用域,省略变量前面的var或者let都会变成一个全局变量。
现在我们再回到前面的例子,这一次增加了全局变量b,在函数内增加了两个console.log输出语句,最后再调用这个函数,但是在函数里并没有定义变量b,那最后会是什么结果呢
var a = 'jack'; var b = 'andy'; function fn() { var a = 'frank'; console.log(a); console.log(b); } fn(); console.log(a);
输出结果:
第二个console.log为什么会输出全局变量andy呢?
这个时候就有了作用域链的概念了,简单的说作用域表示区域,作用域链表示次序
现在我们把眼光放在函数fn里,第一行定义了a是局部变量,第二行输出这个a,但是整个代码里定义了两个a,那么就需要刚刚说到的作用域链来决定到底先用哪个变量。
javascript会先看函数内有没有这个变量a,如果没有再去函数的外围看有没有这个变量,这里作用域链就帮我们安排好了这个次序。
所以,函数内定义了变量a为'frank',那么第二行就会输出'frank',第三行要输出变量b,我们先看函数内有没有这个变量,发现没有,再去外围发现有全局变量b,那么输出的就是这个值,我们再来看最后一个console.log(a),因为他在全局范围内,所以只能访问全局变量a。
也就是说:作用域链只能向上查找,最终找到全局。不能同级(局部)或者向下查找
我们再看这一段代码:
var a = 'jack'; function fn() { console.log(a); var a = 'andy'; console.log(a); } fn();
我们思考一下会输出什么呢?
输出结果:
稍微有点js经验的同学应该都会答对,因为有变量提升,变量a在第一行就被声明了,只不过没有被赋值。下面修改一下代码,大家再看看会输出什么:
var a = 'jack'; function fn() { console.log(a); var a = 'andy'; console.log(ss()); function ss() { return a; } } fn();
我们在fn函数内又添加了一个函数ss,并且在这个函数的顶部就调用了这个函数。不仅函数内声明的变量会被提升,函数内的函数也会被提升,而且函数的提升会比变量更优先
那么,在javascript中这段代码实际上是这样被执行的:
var a = 'jack'; function fn() { function ss() { return a; } var a; console.log(a); a = 'andy'; console.log(ss()); } fn();
先把函数声明提升到首行,再声明变量a,然后输出a,a没有被赋值,所以是undefined,然后a被赋值为'andy',最后调用函数ss,返回的就是andy。
到此这篇关于JavaScript作用域与作用域链使用重点讲解的文章就介绍到这了,更多相关JS作用域与作用域链内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!