this 的指向
在 ES5 中,其实 this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象,来,跟着我朗读三遍:this 永远指向最后调用它的那个对象,this 永远指向最后调用它的那个对象,this 永远指向最后调用它的那个对象。记住这句话,this 你已经了解一半了。 下面我们来看一个最简单的例子:例 1:
1 2 3 4 5 6 7 8 9 10
|
var name = "windowsName"; function a() { var name = "Cherry"; console.log(this.name); // windowsName console.log("inner:" + this); // inner: Window } a(); console.log("outer:" + this) // outer: Window
|
例 2:
1 2 3 4 5 6 7 8
|
var name = "windowsName"; var a = { name: "Cherry", fn : function () { console.log(this.name); // Cherry } } a.fn();
|
例 3:
1 2 3 4 5 6 7 8
|
var name = "windowsName"; var a = { name: "Cherry", fn : function () { console.log(this.name); // Cherry } } window.a.fn();
|
例 4:
1 2 3 4 5 6 7 8
|
var name = "windowsName"; var a = { // name: "Cherry", fn : function () { console.log(this.name); // undefined } } window.a.fn();
|
例 5:
1 2 3 4 5 6 7 8 9 10 11
|
var name = "windowsName"; var a = { name : null, // name: "Cherry", fn : function () { console.log(this.name); // windowsName } } var f = a.fn; f();
|
例 6:
1 2 3 4 5 6 7 8 9 10 11
|
var name = "windowsName"; function fn() { var name = 'Cherry'; innerFunction(); function innerFunction() { console.log(this.name); // windowsName } } fn()
|
怎么改变 this 的指向
改变 this 的指向我总结有以下几种方法:- 使用 ES6 的箭头函数
- 在函数内部使用 _this = this
- 使用 apply、call、bind
- new 实例化一个对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
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
|
箭头函数
众所周知,ES6 的箭头函数是可以避免 ES5 中使用 this 的坑的。箭头函数的 this 始终指向函数定义时的 this,而非执行时。,箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。 例 8 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
var name = "windowsName"; var a = { name : "Cherry", func1: function () { console.log(this.name) }, func2: function () { setTimeout( () => { this.func1() },100); } }; a.func2() // Cherry
|
在函数内部使用 _this = this
如果不使用 ES6,那么这种方式应该是最简单的不会出错的方式了,我们是先将调用这个函数的对象保存在变量 _this 中,然后在函数中都使用这个 _this,这样 _this 就不会改变了。例 9:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
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
|
使用 apply、call、bind
使用 apply、call、bind 函数也是可以改变 this 的指向的,原理稍后再讲,我们先来看一下是怎么实现的:使用 apply
例 10:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
var a = { name : "Cherry", func1: function () { console.log(this.name) }, func2: function () { setTimeout( function () { this.func1() }.apply(a),100); } }; a.func2() // Cherry
|
使用 call
例 11:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
var a = { name : "Cherry", func1: function () { console.log(this.name) }, func2: function () { setTimeout( function () { this.func1() }.call(a),100); } }; a.func2() // Cherry
|
使用 bind
例 12:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
var a = { name : "Cherry", func1: function () { console.log(this.name) }, func2: function () { setTimeout( function () { this.func1() }.bind(a)(),100); } }; a.func2() // Cherry
|
apply、call、bind 区别
刚刚我们已经介绍了 apply、call、bind 都是可以改变 this 的指向的,但是这三个函数稍有不同。 在 MDN 中定义 apply 如下;apply() 方法调用一个函数, 其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数语法:
fun.apply(thisArg, [argsArray])
- thisArg:在 fun 函数运行时指定的 this 值。需要注意的是,指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。
- argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。浏览器兼容性请参阅本文底部内容。
apply 和 call 的区别
其实 apply 和 call 基本类似,他们的区别只是传入的参数不同。 call 的语法为:
1
|
fun.call(thisArg[, arg1[, arg2[, ...]]])
|
1 2 3 4 5 6 7 8 9
|
var a ={ name : "Cherry", fn : function (a,b) { console.log( a + b) } } var b = a.fn; b.apply(a,[1,2]) // 3
|
1 2 3 4 5 6 7 8 9
|
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 试一下
1 2 3 4 5 6 7 8 9
|
var a ={ name : "Cherry", fn : function (a,b) { console.log( a + b) } } var b = a.fn; b.bind(a,1,2)
|
bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。所以我们可以看出,bind 是创建一个新的函数,我们必须要手动去调用:
1 2 3 4 5 6 7 8 9
|
var a ={ name : "Cherry", fn : function (a,b) { console.log( a + b) } } var b = a.fn; b.bind(a,1,2)() // 3
|
JS 中的函数调用
看到留言说,很多童靴不理解为什么 例 6 的 innerFunction 和 例 7 的 this 是指向 window 的,所以我就来补充一下 JS 中的函数调用。例 6:
1 2 3 4 5 6 7 8 9 10 11
|
var name = "windowsName"; function fn() { var name = 'Cherry'; innerFunction(); function innerFunction() { console.log(this.name); // windowsName } } fn()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
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
|
作为一个函数调用
比如上面的 例 1:例 1:
1 2 3 4 5 6 7 8 9 10
|
var name = "windowsName"; function a() { var name = "Cherry"; console.log(this.name); // windowsName console.log("inner:" + this); // inner: Window } a(); console.log("outer:" + this) // outer: Window
|
函数作为方法调用
所以说更多的情况是将函数作为对象的方法使用。比如例 2:例 2:
1 2 3 4 5 6 7 8
|
var name = "windowsName"; var a = { name: "Cherry", fn : function () { console.log(this.name); // Cherry } } a.fn();
|
使用构造函数调用函数
如果函数调用前使用了 new 关键字, 则是调用了构造函数。
这看起来就像创建了新的函数,但实际上 JavaScript 函数是重新创建的对象:
1 2 3 4 5 6 7 8 9
|
// 构造函数: function myFunction(arg1, arg2) { this.firstName = arg1; this.lastName = arg2; } // This creates a new object var a = new myFunction("Li","Cherry"); a.lastName; // 返回 "Cherry"
|
这里就简单的来看一下 new 的过程吧:
伪代码表示:
1 2 3 4 5 6 7 8
|
var a = new myFunction("Li","Cherry"); new myFunction{ var obj = {}; obj.__proto__ = myFunction.prototype; var result = myFunction.call(obj,"Li","Cherry"); return typeof result === 'obj'? result : obj; }
|
作为函数方法调用函数
在 JavaScript 中, 函数是对象。 JavaScript 函数有它的属性和方法。这个时候我们再来看例 6:
call() 和 apply() 是预定义的函数方法。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身 在 JavaScript 严格模式(strict mode)下, 在调用函数时第一个参数会成为 this 的值, 即使该参数不是一个对象。
在 JavaScript 非严格模式(non-strict mode)下, 如果第一个参数的值是 null 或 undefined, 它将使用全局对象替代。
例 6:
1 2 3 4 5 6 7 8 9 10 11
|
var name = "windowsName"; function fn() { var name = 'Cherry'; innerFunction(); function innerFunction() { console.log(this.name); // windowsName } } fn()
|
例 7:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
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
|