原文出处:https://juejin.im/post/59bfe84351882531b730bac2
[TOC]
1、this指向
在ES5中,其实this的指向,始终坚持一个原理:this永远指向最后调用它的那个对象,来,读三遍:this永远指向最后调用它的那个对象,this永远指向最后调用它的那个对象,this永远指向最后调用它的那个对象。记住这句话,下面我们看一个例子:
//例1
var name = "windowsName";
function a() {
var name = "Cherry";
console.log(this.name); // windowsName
console.log("inner:" + this); // inner:[object Window]
}
a();
console.log("outer:" + this) // outer:[object Window]
这个相信大家都知道为什么log的是windowsName,因为根据刚刚的那句话"this 永远指向最后调用它的那个对象",上例中最后调用a的地方a();,前面没有调用的对象那么就是全局对象window,这就相当于window.a();注意,这里我们没有使用严格模式,如果使用严格模式的话,全局对象就是undefined,那么就会报错Uncaught TypeError: Cannot read property 'name' of undefined。
//例2
var name = "windowsName";
var a = {
name: "Cherry",
fn: function () {
console.log(this.name); // Cherry
}
}
a.fn();
在这个例子中,函数fn是对象a调用的,所以打印的值就是a中的name的值。
做一个小小改动:
//例3
var name = "windowsName";
var a = {
name: "Cherry",
fn: function () {
console.log(this.name); // Cherry
}
}
window.a.fn();
由于这里最后调用fn的对象还是a,所以打印出来仍是Cherry。
//例4
var name = "windowsName";
var a = {
// name: "Cherry",
fn: function () {
console.log(this.name); // undefined
}
}
window.a.fn();
这里打印undefined是由于最后调用fn的对象是a,但是a又没有name属性,所以打印出来是undefined。
这个例子还说明了:this永远指向最后调用它的那个对象,因为最后调用fn的对象是a,所以就算a中没有name属性,也不会继续向上一个对象寻找this.name,而是直接输出undefined。
//例5
var name = "windowsName";
var a = {
name: "Cherry",
fn: function () {
console.log(this.name); // windowsName
}
}
var f = a.fn;
f();
这里你可能会有疑问,为什么不是 Cherry
,这是因为虽然将 a 对象的 fn 方法赋值给变量 f 了,但是没有调用,再接着跟我念这一句话:“this 永远指向最后调用它的那个对象”,由于刚刚的 f 并没有调用,所以 fn()
最后仍然是被 window 调用的。所以 this 指向的也就是 window。
由以上五个例子我们可以看出,this 的指向并不是在创建的时候就可以确定的,在 es5 中,永远是this 永远指向最后调用它的那个对象。
//例6
var name = "windowsName";
function fn() {
var name = 'Cherry';
innerFunction();
function innerFunction() {
console.log(this.name); // windowsName
}
}
fn()
这里是由于:fn()
前面没有对象,那就是windows对象,所以打印出来的是windowsName。
2、怎么改变this指向
改变 this 的指向总结有以下几种方法:
- 使用 ES6 的箭头函数
- 在函数内部使用
_this = this
- 使用
apply
、call
、bind
- new 实例化一个对象
//问题提出
var name = "windowsName";
var a = {
name: "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(function () {
this.func1()
}, 100);
}
};
a.func2() // this.func1 is not a function
上例中,由于this.func1()
外面的是setTimeout
,也就相当于是window
调用的,但是window对象并没有func1()
,所以报错。要解决这个问题可以使用以下几种方法。
(1)使用ES6的箭头函数
箭头函数:众所周知,ES6的箭头函数可以避免ES5中使用this的坑的。箭头函数的this始终指向函数定义时的this,而非执行时。箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。
var name = "windowsName";
var a = {
name: "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(() => {
this.func1()
}, 100);
}
};
a.func2() // Cherry
(2)在函数内部使用_this = this
除了使用ES6的箭头函数,在函数内部使用 _this = this
; 应该是最简单不会出错的方式了。先将调用这个函数的对象保存在 _this
中,然后在函数中都使用这个 _this
,这样 _this
就不会改变了。
var name = "windowsName";
var a = {
name: "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
var _this = this;
setTimeout(function () {
_this.func1()
}, 100);
}
};
a.func2() // Cherry
在这个例子中,在func2中,首先设置 var _this = this;
,这里面的this的对象是调用a.func2()
中的对象 a ,所以 _this
现在指向的对象也是 a了,所以 _this.func1()
调用的是 a的方法func1。
(3)使用apply、call、bind
(a)使用apply
var a = {
name: "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(function () {
this.func1()
}.apply(a), 100);
}
};
a.func2() // Cherry
(b)使用call
var a = {
name: "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(function () {
this.func1()
}.call(a), 100);
}
};
a.func2() // Cherry
(c)使用bind
var a = {
name: "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(function () {
this.func1()
}.bind(a)(), 100);
}
};
a.func2() // Cherry
(d)apply、call、bind 区别
apply 和 call 的区别:
apply 和 call 的区别是 call
方法接受的是若干个参数列表,而 apply
接收的是一个包含多个参数的数组。注意:apply是数组,apply是数组,apply是数组
// apply
var a = {
name: "Cherry",
fn: function (a, b) {
console.log(a + b)
}
}
var b = a.fn;
b.apply(a, [1, 2]) // 3
// call
var a = {
name: "Cherry",
fn: function (a, b) {
console.log(a + b)
}
}
var b = a.fn;
b.call(a, 1, 2) // 3
bind 和 apply、call 区别:
我们将刚才的例子使用 bind 试一下:
// bind 没加()的情况
var a = {
name: "Cherry",
fn: function (a, b) {
console.log(a + b)
}
}
var b = a.fn;
b.bind(a, 1, 2); //发现并没有输出
为啥呢:MDN上面的文档说明:
bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
也就是说,bind是创建一个新的函数,我们需要手动调用,换句话就是需要在后面加上()
。
var a = {
name: "Cherry",
fn: function (a, b) {
console.log(a + b)
}
}
var b = a.fn;
b.bind(a, 1, 2)() // 3
(4)new 实例化一个对象
// 构造函数:
function myFunction(arg1, arg2) {
this.firstName = arg1;
this.lastName = arg2;
}
// This creates a new object
var a = new myFunction("Li","Cherry");
a.lastName; // 返回 "Cherry"