改变this指向方法总结

1. this 别名

这种方式一般就是在函数作用域顶端创建一个指向 this 的变量。

例如,在一个对象方法中在执行一个方法或者事件的时候:

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
a: 1,
init: function(){
let ele = document.getElementsByClassName("ele");
let that = this;
let aaa = (() => {
that.a = 2; //通过this的别名that改变a的值
})(); //匿名自执行函数
}
}
obj.init();
console.log(obj.a) //结果为2

或者在vue实例的created()方法中利用axios获取数据的时候:

1
2
3
4
5
6
7
8
9
10
11
created() {
var that = this;
//发送请求的时候带上参数
axios.get('../static/data.json?id=' + this.seller.id)
.then(function (response) {
that.seller = Object.assign({},that.seller,response.data.seller)
})
.catch(function (error) {
console.log(error);
})
}

2. call和apply

call和apply的区别在于,使用apply时,所有参数都应放在一个单独的数组参数中,而在使用call的时候,参数应该一次列出,并用逗号隔开。call和apply的详细作用可以阅读我原来的博文:重温——apply和call方法,里面详细介绍了apply和call的各种用法。这里只说它们的其中一种用法:改变this指向。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//如果单纯只是在一个函数中调用另一个函数是没什么多大区别,apply / call 的最大作用就是改变上下文
var name = "张三";
//在全局里定义函数func1
function getName(){
return this.name;
}

//在对象里定义函数func2
var obj = {
name:"李四",
showName:function(){

console.log(getName()) //这里直接调用,"张三"

return getName.apply(this); //在showName里调用getName,并将this从window改变为obj;也可以说是在obj对象中(obj的上下文环境中)调用window环境中的getName函数,利用apply方法保持了this原来的值。
}
};,

console.log(obj.showName()); //obj调用,"李四"

3. bind(this)

ES5中的bind()方法,它会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。例如:

1
2
3
4
5
6
7
8
window.color = 'green';
var o = {color:'blue'};

function sayColor(){
console.log(this.color);
}
var objectSaycolor = sayColor.bind(o);
objectSaycolor();//blue

在这里sayColor()调用bind()并传入对象o,创建了objectSayColor()函数。objectSayColor()函数的this值等于o,因此即使是在全局作用域中调用这个函数,也会输出blue,而不是全局window对象中国的green。

bind()方法还有一种使用方式是在函数运行时将 this 注入到回调中,使回调中的 this 能指向正确的上下文(改方法跟别名方式的第二个例子是相同的效果),例如:

1
2
3
4
5
this.setState({ loading: true });

fetch('/').then(function loaded() {
this.setState({ loading: false });
}.bind(this));

在 JavaScript 中,所有函数都有 bind 方法,其允许你为 this 指定特定值。一旦函数被绑定,上下文就不能被覆盖,也就意味着 this 会指向正确的上下文。

4. ES6箭头函数

ES2015 规范引入了箭头函数,使函数的定义更加简洁。箭头函数会隐式返回一个值,但更重要的是,它是在一个封闭的作用域中使用this:

1
2
3
4
5
this.setState({ loading: true });

fetch('/').then(() => {
this.setState({ loading: false });
});

不管嵌套多少层,箭头函数中的 this 总能指向正确的上下文,因为函数体内的 this 指向的对象,就是定义时所在的对象,而不是使用时所在的对象。但缺点就是,由于箭头函数不能命名,因而在调试时,堆栈信息给的标签是anonymous function。

如果你用 Babel 将 ES6 的代码转换成 ES5 的代码,就会发现两个有趣的现象:

  • 在某些情况下,编译器能判断函数名是否被赋值给了某个变量
  • 编译器使用 别名 来维护上下文
1
2
3
4
5
6
7
8
9
10
const loaded = () => {
this.setState({ loading: false });
};

// will be compiled to

var _this = this;
var loaded = function loaded() {
_this.setState({ loading: false });
};

5. ES7 的绑定语法

在 ES7 中,有一个关于 bind 语法 的提议,提议将 :: 作为一个新的绑定操作符,该操作符会将左值和右值(一个函数)进行绑定。

以 map 的实现为例:

1
2
3
4
5
6
7
8
9
function map(f) {
var mapped = new Array(this.length);

for(var i = 0; i < this.length; i++) {
mapped[i] = f(this[i], i);
}

return mapped;
}

与 lodash 不同,我们不需要传递数据给 map 作为参数:

1
2
[1, 2, 3]::map(x => x * 2)
// [2, 4, 6]

对下面的代码熟悉吗?

1
2
3
4
[].map.call(someNodeList, myFn);
// or
Array.from(someNodeList).map(myFn);
//Array.from() 方法从一个类似数组(对象必须有length属性,不然转化为空数组)或可迭代对象中创建一个新的数组实例。

ES7 的绑定语法允许你像使用箭头函数一样使用 map:

1
someNodeList::map(myFn);

6. 方法传参指定

一些函数允许为 this 传递一个明确的值,保证其指向正确的上下文,例如 map 函数则将 this 作为最后一个参数:

1
2
3
items.map(function(x) {
return <a onClick={this.clicked}>x</a>;
}, this);

虽然代码能运行,但这不是函数的一致实现。大部分函数并不接受 this 参数,所以最好还是采用上文中的其它方式来绑定 this 。

参考文章: 6 Ways to Bind JavaScript‘s this Keyword in React, ES6 & ES7



完~