先来谈谈 ES5 中的 this
在 ES5 中,每个函数在被调用时都会自动取得 this 这个特殊的对象.因此,每个内部函数不能访问到外部函数的 this 对象.(跟变量访问一样,如果局部环境存在某个变量,就不会去搜索全局环境的同名变量).
** this 对象是在运行时基于函数的执行环境决定的.**
执行环境:
a. 全局环境
运行在全局上下文 (在任何函数体外部),this 指向全局对象,并且,无论是否是在严格模式下,this 都指代全局对象.在浏览器中,this 指向 window 对象.
console.log(this === window); // true;
b. 函数环境
在函数内部,this 的值取决于函数是如何调用的.
直接调用,非严格模式下,this 默认指向全局对象.不管调用的函数是在全局函数还是局部环境.
function app() {
console.log(this === window); // true
}
app(); // 全局调用
function foo() {
function bar() {
console.log(this === window); // true
}
bar(); // 局部调用
}
foo();
直接调用,严格模式下,this 默认指向 undefined.不管调用的函数是在全局函数还是局部环境.
'use strict'
function app() {
console.log(this === undefined); // true
}
app();
function foo() {
function bar() {
console.log(this === undefined); // true
}
bar();
}
foo();
作为对象调用
当函数以对象的方法被调用时,它的 this 是调用该函数的对象,并且是距离最近的对象.
var o = {
sayName: function() {
console.log(this === o); // true
}
}
o.sayName();
作为构造函数调用
当函数被当做构造函数调用时,this 指向刚要被创建的新对象.
function Person() {
this.name = 'noshower';
}
var person = new Person();
console.log(person.name); //'noshower'
使用 call 和 apply 调用,改变函数内部 this 的绑定值.
function foo() {
console.log(this.name); //'noshower'
}
var obj = {
name: 'noshower'
}
foo.call(obj);
// 函数 foo 内部的 this 对象被绑定到了 obj 对象上.
使用 bind 方法,会永久把 this 与一个对象绑定.无论这个函数如何被调用,都改变不了 this.
function foo() {
console.log(this.name); // 'noshower'
}
var obj1 = {
name: 'noshower'
}
var obj2 = {
name: 'DaLin'
}
var a = foo.bind(obj1); // 用 bind 方法,将 this 进行永久绑定.
a.call(obj2); // 此时使用 call 方法将改变不了 this 的指向.
下面我们开始讲 ES6 箭头函数中的 this 对象
箭头函数被调用的时候,不会自动绑定一个 this 对象.换句话说,箭头函数根本就没有自己的 this.它的 this 都是捕获自其所在上下文的 this 值.
作为匿名函数被调用.
function foo() {
console.log(this); //{name:'noshower'
setTimeout(() => {
console.log('name:', this.name); //noshower
}, 100);
}
var name = 'DaLin';
foo.call({name:'noshower');
上面代码中,我们用 call()方法,将函数 foo 的 this 对象绑定到了对象 {name:'noshower' 上.由于,箭头函数没有 this 变量,所以,箭头函数能够访问到外部环境的 this 变量.此时,箭头函数内部,访问到的是 foo 函数的 this 对象.因此,this.name 的值是'noshower'.
作为方法被调用
var obj = {
name: 'noshower',
sayName: () = >this.name
}
var name = 'bar';
console.log(obj.sayName());
上面代码中,箭头函数是作为对象的方法.因为,箭头函数本身是不绑定 this 对象的.因此,它只能从外部搬运 this 对象.上面代码中,只有全局环境存在 this 对象,因此返回的是 window.name,即'bar'.
在方法内部被调用
var obj = {
name: 'noshower',
sayName: function() {
var a = () = >this.name;
console.log(a()); // noshower
}
}
var name = 'bar';
obj.sayName();
上面代码中,箭头函数是在 sayName 方法中运行的.此时,箭头函数的 this 对象,搬运的是 sayName 方法的 this 对象.当 sayName 函数作为方法调用时,它的 this 对象指向 obj 对象.因此,箭头函数的 this.name 就是 obj.name, 即'noshower';
使用 new 操作符调用,会抛出错误
var Obj = (name) = >{
this.name = name;
}
new Obj('noshower');
上面的代码,箭头函数作为构造函数被调用,会报错.原因是,箭头函数没有自己的 this 对象,就没法给 this 添加属性.
使用 call 和 apply,bind() 调用
var o = (val) = >{
console.log(this.name, val); // bar,bar
};
var obj = {
name: 'foo'
}
var name = 'bar';
o.call(obj, name);
上面代码中,箭头函数使用了 call 方法来调用.原本想将函数的 this 绑定在 obj 对象上面.结果显示,this 被绑定在了全局对象上了.这是因为,箭头函数没有自己的 this 对象,此时使用 call 方法仅仅起到了传递参数的作用,没有改变它的 this 对象.它的 this 对象依旧来自外部执行环境.
总结一下
箭头函数没有自己的 this 对象,它总是搬运外部环境的 this 对象.因此,只要离它最近的外部环境中的 this 改变,箭头函数中的 this 就改变.如果离它最近的环境中的 this,没有改变.那么箭头函数中的 this 就不会改变.
箭头函数里的 this 是定义时所在的作用域,而不是运行时所在的作用域
来源: http://www.jianshu.com/p/175a71961c68