JS之this
this是什么 ?
this 关键字是 JavaScript 中最复杂的机制之一。它是一个很特别的关键字,被自动定义在
所有函数的作用域中。但是即使是非常有经验的 JavaScript 开发者也很难说清它到底指向
什么。
this的四大绑定规则
一、默认绑定
这条规则应用于没有其他规则的默认规则。看下面的例子:
var a = 2;
function foo(){
console.log(this.a);
}
foo();// 2
这里实际上foo函数中的this指向了全局对象,有一个细节在于,如果是严格模式下,则会指向undefined。
二、隐式绑定
当函数通过对象的属性这种方式调用时,就触发了this的隐式绑定规则,此时函数中的this会指向该对象。看下面的例子:
var a = 2;
function foo() {
console.log(this.a);
}
let obj = {
a: 1,
foo: foo
}
obj.foo(); // 1
这里有一个很容易让人陷入的误区,函数是独立存在的,对象中的属性只是函数的引用,看下面的例子:
let bar = obj.foo;
bar(); //2
打印结果是2,仔细想想又显而易见,不管是bar还是obj的foo属性都是对foo这个函数的引用,bar是直接调用的,没有其他规则,所以应用了默认的绑定规则。一个更让人意想不到的结果发生在回调之中,看下面的例子:
var a = 2;
function foo() {
console.log(this.a);
}
let obj = {
a: 1,
foo: foo
}
setTimeout(obj.foo, 1000); // 2;
很多人会很惊讶为啥是2,而不是1,其实原理很简单,setTimeout的第一个参数接收一个函数,这里我们传的obj.foo是实参,在setTimeout内部接收的是形参,相当于fn = obj.foo,所以在setTimeout内部不是用obj.foo来执行,而是用fn执行的,命中默认绑定规则,所以this指向全局对象。
三、显式绑定
所谓显式绑定,也叫硬绑定,是强制将this绑定到指定的对象上。JS提供了三个函数来显式绑定this:apply、call、bind。看下面的例子:
var a = 2;
function foo() {
console.log(this.a)
}
let obj = {
a: 1
}
foo.call(obj); // 1
foo.apply(obj); // 1
foo.bind(obj)(); // 1
四、new绑定
函数中的this会自动绑定到通过new操作符创建的对象上,看下面的例子:
function Foo(a) {
this.a = a;
}
let obj = new Foo(2);
obj.a; // 2;
五、优先级
默认绑定是在无法应用其他绑定时应用的,所以默认绑定的优先级最低;现在我们来看一看隐式绑定和显式绑定谁的优先级高:
function foo() {
console.log(this.a);
}
let obj1 = {
a: 1,
foo: foo
}
let obj2 = {
a: 3
}
obj1.foo.call(obj2); // 3
从打印结果来看,显式绑定的优先级高于隐式绑定,我们再来看看隐式绑定和new 操作符谁的优先级高:
function foo(a) {
this.a = a;
}
let obj1 = {
a: 1,
foo: foo
}
let obj2 = new obj1.foo(3);
console.log(obj1.a); //1
console.log(obj2.a); //3
从打印结果来看,new 操作符优先级明显高于隐式绑定,最后我们再来看看显式绑定和new绑定谁的优先级更高:
function foo(a) {
this.a = a;
}
let obj1 = {
a: 1,
foo: foo
}
let bar = obj1.foo.bind(obj1);
bar(3);
console.log(obj1.a); // 3
let obj2 = new bar(5);
console.log(obj1.a); // 3
console.log(obj2.a); // 5
不难看出,new操作符修改了bar中的this,bar是由bind生成的函数,是硬绑定的一种,所以new 的优先级是高于显式绑定的。
得出结论: new > 显式绑定 > 隐式绑定 > 默认绑定
总结
从前面的例子中可以看出,this有四种应用规则,且this究竟应用哪一种规则不是由函数声明的时候决定,而是由调用的时候决定,在分析this的时候我们一定要小心、谨慎。