this四大绑定规则

本文介绍了JavaScript中this的四大绑定规则:默认绑定、隐式绑定、隐式丢失、显式绑定(包括call/apply方法)以及new绑定。通过示例解析了如何在不同场景下确定this的指向,帮助理解this在函数调用中的行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

谈到this绑定规则要先找到函数执行过程中的调用位置。
调用位置就是函数在代码中被调用的位置。这就涉及分析调用栈(就是为了到达当前执行位置所调用的所有函数)。调用位置就在当前正在执行的函数的前一个调用中
那什么是调用栈和调用位置呢?

    //1.调用栈和调用位置
    function baz(){
        //当前调用栈是baz
        //当前调用位置是全局作用域
        console.log("baz");
        bar(); //<--bar的调用位置
    }

    function  bar(){
        //当前调用栈是baz->bar
        //当前调用位置是baz
        console.log("bar");
        foo();//<--foo的调用位置
    }

    function foo(){
        //当前调用栈是baz->bar->foo
        //当前调用位置是bar
        console.log("foo");
    }
    baz();//<-- baz的调用位置

调用栈是baz->bar->foo,当调用foo()时调用位置就是bar(),可以简单理解为在bar()中调用foo().
注意是如何(从调用栈中)分析出真正的调用位置的,因为它决定了this的绑定。

this有四大绑定规则,下面来仔细看看这四大规则。

  • 默认规则
function foo(){
    console.log(this.a); //this.a被解析成全局变量a a=2
}
var a=2;
foo(); //2

foo()是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则

//严格模式下全局对象将无法使用默认绑定,this会绑定到undefined. 严格模式下与foo()的调用位置无关
function foo(){
    "use strict";
    console.log(this.a);
}
var a=2;
foo(); //undefined
  • 隐式绑定
    先看一段代码
function foo(){
    console.log(this.a);
}
var obj={
    a:2,
    foo:foo
};
obj.foo();//2

调用位置会使用obj上下文来引用函数。当foo()被调用时,它的落脚点指向obj对象。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象,因此this.a和obj.a是一样的。
对象属性引用链中只有最顶层或者说最后一层会影响调用位置。举例来说:

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

隐式丢失
一个最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把this绑定到全局对象或者undefined上,取决于是否是严格模式。

//隐式丢失
function foo(){
   console.log(this.a);
}
var obj={
   a:2,
   foo:foo
};
var bar=obj.foo;  //函数别名!bar引用的是foo函数本身
var a="Hello Febby"; //a是全局对象属性
bar(); //Hello Febby

虽然bar只是obj.foo的一个引用,实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定

同时注意区分下面这种调用方式

function foo(){
    return this.a;
}
var obj={
    a:2,
    foo:foo
};
var bar=obj.foo();  //绑定到obj上
var a="Hello Febby"; //a是全局对象属性
// bar; //2
console.log(bar); //2

再来思考一下下面这段代码:

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

参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值,所以结果跟上面一样。

如果把函数传入语言内置的函数而不是传入自己声明的函数,结果是一样的。

function foo(){
    console.log(this.a);
}
var obj={
    a:2,
    foo:foo
};
var a="Hello Febby"; //a是全局对象属性
setTimeout(obj.foo,100); //Hello Febby

//js环境中内置的setTimeout()函数实现与下面伪代码类似
function setTimeout(fn,delay){
    //等待delay毫秒
    fn();//<--调用位置
}
  • 显式绑定
    就像我们刚才看到那样,在分析隐式绑定时,我们必须在一个对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把this间接(隐式)绑定在这个对象上。
    那么如果我们不想在对象内部包含函数引用,而想在某个对象上强制调用函数,该怎么做呢?
    可以利用JS提供的call()和apply()方法
    这两个方法是如何工作的呢?它们的第一个参数是一个对象,它们会把这个对象绑定到this,接着在调用函数时指定这个this。因为你可以直接指定this的绑定对象,称之为显式绑定
//显式绑定
function foo(){
    console.log(this.a);
}
var obj={
    a:2,
    foo:foo
};
foo.call(obj);//2
foo.apply(obj);//2

硬绑定可以解决丢失绑定问题。

function foo(){
     console.log(this.a);
}
var obj={
    a:2
};
var bar=function(){
   foo.call(obj);
};
bar();//2
setTimeout(bar,100);//2
//硬绑定的bar不可能再修改它的this
bar.call(window)//2

我们创建了bar(),并在它的内部手动调用了foo.call(obj),因此强制把foo的this绑定到了obj上。无论之后怎么调用函数bar,它总会手动在obj上调用 foo。这种绑定是一种显式的强制绑定,因此我们称之为硬绑定

new绑定
在JS中,构造函数只是一些使用new操作符是被调用的函数。它们并不属于某个类,也不会实例化一个类。实际上,它们只是被new操作符调用的普通函数而已。
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

  1. 创建(或者说构造)一个全新的对象。
  2. 这个新对象会被执行[[原型]]连接。
  3. 这个新对象会绑定到函数调用的this。
  4. 如果函数没有返回其他对象,那么new表达式中的函数会自动返回这个新对象。
//new绑定
function foo(a){
    this.a=a;
}
var bar=new foo(2);
console.log(bar.a);//2

使用new来调用foo(…)时,会构造一个新对象并把它绑定到foo(…)调用中的this。

以上便是this四大绑定规则的总结,希望可以帮到大家。
具体用法传送门:https://blog.youkuaiyun.com/Febby_/article/details/89430391
知识整合来源:《你不知道的JavaScript》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值