js访问一个变量时会优先在该做用域内(访问时的那个做用域)寻找是否声明过这个变量,若是该变量已经存在,则直接使用它的值,不然会寻找该做用域的‘父做用域/上级做用域',依次类推,直到找到全局做用域为止。通俗地讲,当声明一个函数时,局部做用域一级一级向上包起来,就是做用域链。bash
首先看一个多级做用域的栗子:闭包
//多级做用域
//======>此处是1级做用域
var gender = "男";
function fn(){ // ======>从这里开始是2级做用域
//gender 能够访问 gender是全局做用域的变量,任何地方均可以访问
//age 能够访问
//height 不能访问
return function(){ // ======>从这里开始是3级做用域
//gender 能够访问
//age 能够访问
//height 能够访问
var height = 180;
}
var age = 5;
}
复制代码
因为做用域是相对于变量而言的,而若是存在多级做用域,这个变量又来自于哪里?这个问题就须要好好地探究一下了,咱们把这个变量的查找过程称之为变量的做用域链函数
做用域链的意义:查找变量(肯定变量来自于哪里,变量是否能够访问)post
简单来讲,查找一个变量来自哪里,可否被访问,须要如下四步:ui
- 查看当前做用域,若是当前做用域声明了这个变量,能够直接访问
- 查找当前做用域的上级做用域,也就是当前函数的上级函数,看看上级函数中有没有声明,有就返回变量,没有继续下一步
- 再查找上级函数的上级函数,直到全局做用域为止,有则返回,无则继续
- 若是全局做用域中也没有,咱们就认为这个变量未声明(xxx is not defined)
这四步操做就描述了整个做用域链及做用域链如何查找变量的过程。spa
下面咱们来举几个特殊的栗子: 例子1:code
function fn(callback){
var age = 20;
callback();
}
fn(function(){
console.log(age);//报错
//1.在当前做用域没有查找到age
//2.查找上一级做用域:全局做用域
//为什么是全局做用域?
//由于看上一级做用域,不是看函数在哪调用,而是看函数在哪编写的。
//这种特别的做用域,叫作“词法做用域”
})
复制代码
这个栗子比较特殊,可能不少人会认为输出20,由于函数调用的地方是fn函数内部,恰巧age又是声明在这个函数内部的,理所应当输出20。这是错误的! 如今分析下做用域链如何查找变量的:cdn
例子2:对象
var name="张三";
function f1(){
var name = "abc";
console.log(name);
}
f1(); // abc
复制代码
若是查找一个变量时,在当前做用域找到变量,无论上级、上上级有没有同名变量都不会再去寻找。blog
例子3:
var name="张三";
function f1(){
console.log(name);
var name = "abc";
}
f1(); // undefined
复制代码
若是这个栗子能看懂说明已经了解变量提高,若是不懂,参考后面的文章。
例子4:
var name = "张三";
function f1(){
var name = "abc";
return function(){
console.log(name);
console.log(age);
}
var age = 18;
}
var fn = f1();
fn();
//abc
//undefined
复制代码
这个栗子咋一看可能有点懵,可是记住以前一句很重要的话,查找函数上级做用域,不是看函数在哪调用,而是看函数在哪编写。
例子5:
var name="张三";
function f1(){
return {
say:function(){
console.log(name);
var name="abc";
}
}
}
var fn=f1();
fn.say();//undefined
复制代码
当前做用域查到了变量,则不会再继续寻找,直接返回该变量的值,这里打印的时候变量声明可是未赋值,因此输出undefined。
若是以上几个栗子都能看懂,说明你已经掌握了做用域&做用域链,学好做用域和做用域链能够为理解闭包打下很好的基础。
提到做用域就不得不提到闭包,简单来说,闭包外部函数可以读取内部函数的变量。
优势:闭包能够造成独立的空间,永久的保存局部变量。
缺点:保存中间值的状态缺点是容易形成内存泄漏,由于闭包中的局部变量永远不会被回收
产生闭包的根本缘由是做用域链
- 产生闭包的缘由是由做用域链引发的
- 函数嵌套函数,被嵌套的函数就能够称为闭包
- 子函数可使用父函数的变量(访问其余函数内部的局部变量)
- 让变量始终保存在内存中,避免自动垃圾回收(其实上面的例子中就已经用到了的)
- 对外提供公有属性和方法
详细讲解能够参考我写的闭包系列: