《你不知道的JS》读书笔记——this

Yukino 393 2021-12-29

一、默认绑定

function foo() {
    console.log(this.a);
}

var a = 2;
foo(); // 2

这里调用foo函数打印的a就是全局作用域中定义的a,这就是第一种情况,this的默认绑定,绑定的对象是全局对象。
在严格模式下,默认绑定的是undefined

二、隐式绑定

function foo() { 
    console.log( this.a );
}
var obj={ 
    a: 2,
    foo: foo 
};
obj.foo(); // 2

如果你之前有学过面向对象的语言,比如:Java,那你应该会觉得这个很眼熟。当函数调用具有上下文对象时,隐式绑定规则会将this绑定到这个上下文对象上,在这个例子中,this就绑定到了obj这个对象上。


function foo() { 
    console.log( this.a );
}
var obj2={ 
    a: 42,
    foo: foo 
};
var obj1={ 
    a: 2,   
    obj2: obj2 
};
obj1.obj2.foo(); // 42

对象属性引用链中只有最后一层会影响调用绑定结果,上面的例子中,可以理解为(obj1.obj2).foo(),这也比较好理解。


function foo() { 
    console.log( this.a );
}
function doFoo(fn) {
    // fn其实引用的是foo
    fn(); // <--调用􏰇位置􏰈!
}
var obj={ 
  a: 2,
	foo: foo 
};
var a = "oops, global"; // a 是全局对􏰥象的属性
doFoo( obj.foo ); // "oops, global"

隐式丢失在上面的例子中我们将obj.foo作为参数传入到doFoo中,然后在doFoo中调用了传入的fn,结果打印的时全局对象中的a,这是因为在参数的传递过程中是赋值的操作,我们是把obj.foo赋值给了fn,这里的fn其实就是foo函数的一个引用,还有一个更直白的例子可以帮助理解:

function foo() { 
  console.log( this.a ); 
} 
var obj={ 
  a: 2, 
  foo: foo 
}; 
var bar = obj.foo; // 函数别名! 
var a = "oops, global"; // a 是全局对象的属性 
bar(); // "oops, global" 

这也是在我们平时写项目中设置回调函数时经常会发生的问题,而大多数情况下,我们时将this的值存储下来,比如:

let self = this; 
this.$axios({ ... }) 
		.then( function() { self.$Message() } )

又或者使用箭头函数

 let self = this; 
this.$axios({ ... }) 
    .then((a) => { this.$Message() }) 

第一种是将函数和self作为一个闭包传入了回调中,第二种是利用箭头函数没有单独的this,在函数中调用会继承函数的this的特性,下面会介绍一种更加常用的方法来绑定this。

三、显示绑定

显示绑定是通过call或者apply方法将this绑定到对应的obj上。

function foo() { 
	console.log(this.a); 
} 
var obj={ a:2 }; 
foo.call(obj); // 2 

我们可以看到通过call方法,我们成功的将这次调用的foo的this绑定到了obj上,而apply的作用与call完全一致,唯一的区别在于,call传递参数时是传递的参数列表,apply传递的时参数数组。

var func1 = function(arg1, arg2) {}; 
func1.call(this, arg1, arg2); 
func1.apply(this, [arg1, arg2]); 

function foo() { 
  console.log( this.a ); 
} 
var obj={ 
  a:2 
}; 
var bar = function() { 
  foo.call( obj ); 
}; 
bar(); // 2 
setTimeout( bar, 100 ); // 2 

硬绑定是一种常用的模式,如上面的例子,创建一个包裹函数bar,在其中调用foo.call(obj),之后我们无论在那里调用这个函数,this绑定的都是obj。由于这个方法过于常用,在es5中,提供了内置的bind方法来进行硬绑定,如:

function foo(something) { 
  console.log( this.a, something ); 
  return this.a + something; 
} 
var obj={ 
  a:2 
}; 
var bar = foo.bind( obj ); 
var b=bar(3);//23 
console.log( b ); // 5 

四、new绑定

学过面向对象语言的对这个词应该不陌生,但与面向对象语言中new不同的是,js中的是以new的方式去执行一个函数,而不是调用构造函数,在JS中目前并没有实际的类,具体的过程如下:

​ 1. 创建或者构造一个全新的对象

​ 2. 这个新对象会执行[[原型]]连接

​ 3. 这个新对象会绑定到函数调用的this

​ 4. 如果函数没有返回其他对象,那么new表达式中的函数会自动返回这个对象

function foo(a) { 
  this.a = a; 
} 
var bar = new foo(2); 
console.log( bar.a ); // 2 

五、优先级

new绑定>硬绑定>隐式绑定>默认绑定


# 前端 # JS