是的,这也是篇读书笔记,记录《你不知道的JavaScript》上卷的第二部分的this相关内容,有大量增删和个人理解。
this是什么
这里很难说this是什么,只能说是JavaScript的一种机制
this是在运行时就已经绑定了,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式
当一个函数被调用时,会创建一个活动记录(执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性,会在函数执行的过程中用到
换言之,this是在函数被调用时就发生的绑定,它指向什么完全取决于函数在哪里被调用
调用位置
调用位置是函数在代码中被调用的位置,而不是声明的位置。寻找调用位置就是“函数被调用的位置”
分析调用栈就是为了到达当前执行位置所调用的所有函数
绑定规则
函数的执行过程中,调用位置如何决定this的绑定对象
找到调用位置,如何根据四条规则去作判断,这四条规则分别是默认绑定、隐式绑定、显式绑定、new绑定
默认绑定
这条规则可以看作为无法应用其他规则时的默认规则
例如
function foo(){
console.log(this.a);
}
var a=2;
foo();
在代码中,foo()
是直接使用不带任何修饰的函数引用进行调用的,只能使用默认绑定,无法应用其他规则
如果是严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定到 undefinded
隐式绑定
这条规则是考虑调用位置是否有上下文对象,或者说是否被某个对象拥有或包含
例如
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
obj.foo(); //2
当函数被引用用上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
因为调用foo()
时 this 被绑定到obj,因此 this.a 和 obj.a是一样的
对象属性引用链中只有最顶层或者说是最后一层灰影响调用位置,如
function foo(){
console.log(this.a);
}
var obj2 = {
a:32,
foo:foo
}
var obj1 = {
a:2,
obj2:obj2
}
obj1.obj2.foo(); //32
显式绑定
分析隐式绑定时,我们必须在一个对象内包含一个指向函数的属性,并通过这个属性间接引用这个函数,从而把 this 间接(隐式)绑定到这个对象上。如果我们不想在对象内部包含函数引用,而是想再某个对象上强制调用函数,我们可以使用函数的call()
和apply()
方法
call()
和apply()
的第一个参数是一个对象,它们会把这个对象绑定到this,接着在调用函数时指定这个this。因为可以直指定this的绑定对象,称为显式绑定
例如
function foo(){
console.log(this.a);
}
var obj = {
a:2
};
foo.call(obj); //2
ES5也给我们提供了一种硬绑定的方法Function.prototype.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); //2 3
console.log(b); //5
new绑定
使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作
1、创建(构造)一个全新的对象
2、这个新对象会被执行[原型]链接
3、这个新对象会绑定到函数调用的this
4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
例如
function foo(a){
this.a = a;
}
var bar = new foo(2);
console.log(bar.a);
优先级
默认绑定的优先级是四条规则中最低的
显式绑定的优先级大于隐式绑定
new绑定的优先级大于隐式绑定
new绑定的优先级大于显式绑定
判断this
如果要判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置,找到后就可以按下面顺序应用四条规则去判断this的绑定对象
1、由 new 调用?绑定到新创建的对象
2、由 call 或者 apply (或者 bind )调用?绑定到指定的对象
3、由上下文对象调用?绑定到那个上下文对象
4、默认,严格模式下绑定到 undefined ,否则绑定到全局对象
this词法
ES6中有一种无法应用上面所述的四条规则的特殊函数类型:箭头函数
箭头函数并不是使用 function 关键字定义的,而是使用胖箭头=>
定义的。
箭头函数不使用 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,箭头函数的绑定无法被修改(new 也不行)
内容不多,至此~
本文由 Chakhsu Lau 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。