This的四种绑定

this的绑定取决于它的调用位置,而不是取决于它的声明位置

function baz(){
    //调用栈baz,当前调用位置全局
    bar();
}
function bar(){
    //调用栈baz->bar,调用位置baz
    foo();
}
function foo(){
    //调用栈baz->bar->foo,调用位置bar
}
baz();

this的默认绑定

首先是最常用的函数调用类型:独立函数调用。这条规则是无法应用其他规则时的默认规则。

function foo(){
    console.log(this.a);
}
let a = 2;
foo();//2

foo()是直接使用不带任何修饰的函数引用进行调用,只能使用默认绑定。当中的this指向全局对象。但在严格模式下,则不能将全局对象用于默认绑定,this会绑定到undefined

this的隐式绑定

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

无论foo无论是在obj中定义还是先定义在添加为应用,这个函数严格来说都不属于obj对象。然而在调用位置使用obj上下文来引用对象时,可以说函数被调用时obj包含对函数的引用。

当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。并且对象属性引用链只有最后一层调用位置起作用

隐式丢失

  • 函数别名

    let bar = obj2.foo;
    bar();//3
    
  • 间接引用

    let p = {a:4}
    obj2.foo();//42
    (p.foo = obj2.foo)();//3返回值是目标函数引用调用位置是foo()引用默认绑定
    
  • 回调函数

    setTimeout(obj2.foo,1000);
    

    参数传递的过程是一种隐式赋值

显式绑定

使用函数的call(...)、apply(...)来直接指定this的绑定对象。

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

使用bind解决隐式丢失(一些函数也有context选项确保指定的this)

function foo(something){
    return this.a + something;
}
let obj = {
    a:2
}
let a = 6;
let bar = foo.bind(obj);
let b = bar(3)//5

new绑定

js中的构造函数不属于某个类,也不会实例化一个类,它们只是被new操作符调用的普通函数

只用new调用函数会执行:

1. 构造一个全新的对象
2. 新对象会执行Prototype链接
3. 新对象会绑定到函数调用的this
4. 如果函数没有返回其他对象,new表达式中函数调用会自动返回这个新对象
function foo(a){
    this.a = a;
}
let bar = new foo(2);
console.log(bar.a);//2

优先级:new绑定>显示绑定>隐式绑定>默认绑定

箭头函数

箭头函数不使用this的四种标准规则,而是根据外层作用域决定。箭头函数的绑定无法修改

function foo(){
    return (a)=>{
        //this继承自foo()
        console.log(this.a)
    }
}
let obj1 = {a:2}
let obj2 = {a:3}
let bar = foo.call(obj1)
bar.call(obj2)//2,不是3
function foo(){
    setTimeout(()=>{
        console.log(this.a);
    },1000);
}
let obj = {a:2}
foo.call(obj);