通俗地讲,当声明一个函数时,局部作用域一级一级向上包起来,就是作用域链。
1.当执行函数时,总是先从函数内部找寻局部变量。
2.如果内部找不到(函数的局部作用域没有),则会向创建函数的作用域(声明函数的作用域)寻找,依次向上。
比如在闭包中可以直接使用外部函数内定义的变量,这也是闭包的本质:闭包函数的定义基于外部函数的执行。
function outer(){ let outerSay= 'i am outer'; function inner(){ console.log(outerSay); } return inner; } outer()();
运行结果:
闭包函数访问到了外层函数定义的变量。也就是声明闭包函数时的外层作用域中的变量。
以另一个例子作对比:
var name = "windowName"; function f1() { var name = "f1Name"; console.log("f1 say : ",name); f0(); } function f0() { console.log("f0 say :",name); } f1();
运行结果:
对于 f1 而言,在其内部声明了 name 变量,所以首先会找到函数内部定义的变量。而对 f0 而言,函数内部没有定义内部变量,需要从函数定义时的外层环境寻找,也就是全局变量 name 。而不是 f1 中定义的局部变量内部, f1 的环境是调用 f0 的外部环境,而不是声明 f0 的外部环境。
相对于变量的取值取决于函数定义时的环境,this 的取值正相反,取决于函数运行时的上下文。this是使用call方法调用函数时传递的第一个参数,它可以在函数调用时修改,在函数没有调用的时候,this的值是无法确定。对于函数的调用:
const obj = { name: 'Jerry', greet: function() { console.log(this.name) } } obj.greet() //第一种调用方法 obj.greet.call(obj) //第二种调用方法
对于函数的调用,第一种方式只是第二种方式的语法糖。函数在调用时需要传入调用对象。当使用第一种方法调用普通函数时,有以下规则:
1. 一般方法中,this代指全局对象 window
2. 作为对象方法调用,this代指当前对象
3. 作为构造函数调用,this 指代new 出的对象()
4. 调用方法的apply和call方法,可以改变函数的调用对象/作用域
而对于箭头函数来说,箭头函数本身是没有 this 的,call 传入的第一个参数会被忽略。在严格模式下,this 为 undfined,否则为 window。箭头函数的 this 取决于执行上下文中的 this 。当作为闭包函数时,执行上下文中有外层函数的 this ,在箭头函数中使用 this 会直接使用外层函数的 this。闭包函数的定义取决于外部函数的执行。
其他情况下箭头函数的 this 均指向 undfined(严格模式)或 window。
对于普通函数来说,做为闭包函数时,其 this 指向的是 window 对象。因为其并没有被作为对象的方法调用,call 方法传入的第一个方法为 window 。
可以说闭包函数的 this 是一个特例,其忽略了 call 中传入的第一个参数,不可人为指定 this,this 只会从执行上下文中去寻找。
举个例子:
var name = 'windowName'; var obj = { name: 'outerName', inner0: function() { console.log('inner0 say : ', this.name); }, inner1: () => { console.log('inner1 say : ', this.name) } } obj.inner0(); obj.inner1();
var name = 'windowName'; var obj = { name: 'outerName', inner0: function() { setTimeout(function() { console.log('inner0 say : ', this.name); }, 10) }, inner1: function() { setTimeout(() => { console.log('inner1 say : ', this.name); }, 10) } } obj.inner0(); obj.inner1();