重拾 JavaScript 中 的 this
this 指向的是什么?相信这个问题大家都探究过,但是我们当中很多人选择了一种舒适的方式去回避了它。回头看 N 久之前,自己的项目里面是不是也存在这样的语句:let _this = this 。
让我们重新出发,认真的理解一下 JS 中 this 的机制吧。
在 JavaScript 中,this 是一个很特别的关键字,被自动定义在所有函数的作用域中。当一个函数被调用时,会创建一个执行上下文。这个上下文会包含函数在哪里被调用(调用栈)、函数的调用方法 、传入的参数等信息。this 就是上下文的其中一个属性,会在函数执行的过程中用到。
this 是在运行时进行绑定的,this 的绑定只取决于函数的调用方式。 即 this 是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
1、绑定规则
1.1 默认绑定
默认绑定就是 独立函数调用 :如果在严格模式下,此时 this 指向为 undifined ;如果在非严格模式下,此时 this 指向为 window。
function fn() {
console.log( this.a );
}
var a = '123';
fn(); // 123
严格模式下:
"use strict";
function fn() {
console.log( this.a );
}
var a = '123';
fn(); // Uncaught TypeError: Cannot read property 'a' of undefined
1.2 隐式绑定
隐式绑定是 函数调用位置是否被某个对象拥有或者包含
function fn() {
console.log(this.a)
}
var obj = {
a: '123',
Fn: fn
}
obj.Fn() // 123
// obj.Fn() 调用时, this 被绑定到了 obj 上
对象属性引用链中只有最后一层会影响调用位置。
function fn() {
console.log(this.a)
}
var obj1 = {
a: '000',
fn: fn
}
var obj2 = {
a: '111',
obj1
}
obj2.obj1.fn(); // 000
1.3 强制绑定
使用函数的 call()、apply() 或者 bind()。
call()、apply() 它们的第一个参数是一个对象,它们会把这个对象绑定到 this ,接着在调用函数时指定这个 this 。bind() 类似,只是创建一个新的函数,需要手动调用一下。
function fn() {
console.log(this.a)
}
var obj = {
a: '123'
}
fn.call(obj) // 123
call()、apply() 、bind()的区别
function callFn(p1, p2, p3){
console.log(`${this.name} is learning ${p1}、${p2}、${p3}`)
}
function applyFn(arr){
console.log(`${this.name} is learning ${arr.join('、')}`)
}
var person = {
name: 'jens'
}
// call
callFn.call(person, 'koa', 'nodejs', 'redis');
// jens is learning koa、nodejs、redis
// apply
applyFn.call(person, ['koa', 'nodejs', 'redis']);
// jens is learning koa、nodejs、redis
// bind
callFn.bind(person, 'cooking', 'eating', 'fatting')();
// jens is learning cooking、eating、fatting
1.4 new绑定
使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
- 创建(或者说构造)一个全新的对象。
- 这个新对象会被执行 [[原型]]连接。
- 这个新对象会绑定到函数调用的 this 。
- 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
function foo(a) {
this.a = a;
}
var bar = new foo('111');
console.log(bar.a); // 111
2、如何使用规则
按照如下顺序应用规则:
-
函数是否在 new 中调用( new 绑定)?如果是的话 this 绑定的是新创建的对象。
var bar = new foo() -
函数是否通过 call、apply 或者 bind 调用?如果是的话,this 绑定的是传入的对象。
var bar = foo.call(obj) -
函数是否在某个上下文对象中调用(隐式绑定)?如果是的话, this 绑定的是那个上下文对象。
var bar = obj.foo() -
如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined ,否则绑定到 全局对象。
var bar = foo() -
如果把 null 或者 undefined 作为 this 的绑定对象传入 call 、 apply 或者 bind , 实际应用的是默认绑定规则(同第4点)。
-
箭头函数会继承外层函数调用的 this 绑定,且绑定无法被修改(在声明时候绑定this)。
function foo() {
return (a) => {
console.log( this .a );
};
}
var obj1 = { a:2 };
var obj2 = { a:3 };
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2
// foo() 内部创建的箭头函数会捕获调用时 foo() 的 this 。
// 由于 foo() 的 this 绑定到 obj1 ,
// bar(引用箭头函数)的 this 也会绑定到 obj1 ,
// 箭头函数的绑定无法被修改。
function foo() {
setTimeout(() => {
// 这里的 this 在此法上继承自 foo()
console.log( this.a );
},100);
}
var obj = { a:2 };
foo.call( obj ); // 2
相关试题
// test1
var Test ={
foo:"test",
func:function () {
var self = this;
console.log(this.foo);
console.log(self.foo);
(function () {
console.log(this.foo);
console.log(self.foo);
})();
}
};
Test.func();
运行结果:
test
test
undefined // 立即执行函数,适用 默认规则,非严格模式下 this 指向 window, window下没有foo属性, 故为 undefined
test
// test2
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn();
arguments[0]();
}
};
obj.method(fn, 1);
运行结果:
10
2
method()内部执行的是fn()函数,
而fn()函数绑定的对象是window,即window.fn()
全局函数fn同时也属于arguments数组中的一员,
即当作为arguments成员之一调用的时候,
其作用域就绑定到了arguments上,
this也就是指向了arguments对象
// test3
var that = this;
setTimeout(function() {
console.log(this === that); // true
});
(function(){
console.log(that === this); // true
})();
// 在浏览器中setTimeout、setInterval和匿名函数执
// 行时的当前对象是全局对象window