this的设计及绑定规则

发布时间 2023-04-01 21:54:37作者: heyujun-

每个函数的this是在调用时被绑定的,完全取决于函数的调用位置;

什么是调用位置呢?

顾名思义就是“函数被调用的位置”;要去寻找被调用的位置,那么就要分析调用栈,this的调用位置就在当前正在执行的函数的前一个调用中;

举个例子

function baz() {
    // 当前调用栈是全局 -> baz,调用位置是上一个执行栈就是全局作用域
    bar() // bar的调用位置
}
function bar() {
    // 当前调用栈是全局 -> baz -> bar,调用位置是上一个执行栈就是baz
    foo() // foo的调用位置
}
function foo() {
    // 当前调用栈是全局 -> baz -> bar -> foo,调用位置是上一个执行栈就是bar
}
baz() // baz的调用位置
 
接下来我们看看再函数的执行过程中调用位置是如何决定this的绑定对象的。
this绑定有四条规则,分别是:
  a.默认绑定
  b.隐式绑定
  c.显式绑定
  d.new绑定
四种规则分别从弱到强,new绑定优先级最高,依次类推;
 
默认绑定
可以把这条规则是无法应用其他规则时的默认规则;
function foo() {
    // this指向全局
    console.log(this.a)
}
a = 1;
foo();  // 输出1
这里由于foo函数时直接引用进行调用的,只能使用默认绑定,无法应用其他规则,所以this指向全局对象,而全局变量a为1,所以输出1;
需要注意的是:再严格模式下,this会绑定到undefined,而不是全局对象;
 
隐式绑定
调用位置是否有上下文对象是隐式绑定需要考虑的规则,或者说是否被某个对象引用;
function foo() {
    console.log(this.a)
}
var obj = {
    a: 1,
    foo: foo
}
obj.foo(); // 输出1
可以看到,obj对象的foo属性添加了foo函数的引用,当foo被调用时,指向了obj对象,当函数引用有上下文对象时,隐式绑定规则会把this绑定到这个上下文对象,即obj对象;
注意:对象属性引用链中只有最后一层会影响调用位置,举个例子:

function foo() {
    console.log(this.a)
}
var obj1 = {
    a: 1,
    obj2: obj2
}
var obj2 = {
    a: 2,
    foo: foo
}
obj1.obj2.foo(); // 输出2
 
显式绑定
其实这种方法很常见,就是通过call/apply/bind等强绑定,可以直接指定this的绑定对象。这里就不展开描述了
 
new绑定
相信大家对new构造函数调用都耳熟能详,使用new调用函数,对自动执行下面的操作:
  a.创建一个全新的对象;
  b.这个新对象会被执行原型连接;
  c.这个新对象会被绑定到函数调用的this;
  d.如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象;
看以下代码
function foo() {
    this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2
使用new来调用foo()时,会构造一个新对象并把它绑定到foo()调用中的this上。
 
接下来总结一下如何判断this
  1、函数是否再new中调用?是的话this绑定的是新创建的对象;
  2、函数是否通过call/apply调用?是的话this绑定的是指定的对象;
  3、函数是否在某个上下文对象中调用?是的话this绑定的是那个上下文对象;
  4、如果都不是以上,使用默认绑定,如果在严格模式下,被绑定到undefined,否则就绑定到全局对象;
以上就是this的绑定原理啦~