javascript之this绑定

javascript之this绑定

基本知识

  • call-site

    • 函数或子程序的调用站点并非是它们声明的地方,而是调用函数或者是通过动态调度调用的位置。
  • call-stack

    • 调用堆栈是一个方法列表,按调用顺序保存所有在运行期被调用的方法
function baz(){
    //调用栈是 `baz`
    bar(); //`bar`的调用站点
}

function bar(){
    //调用栈是 `baz`->`bar`
    foo(); //`foo`的调用站点
}

function foo(){
    //调用栈是 `baz`->`bar`->`foo`
    console.log("foo");
}

baz(); //baz的调用站点

绑定规则

  • 默认绑定

    function foo(){
        console.log(this.a);
    }
    var a=2;
    foo(); //2
    
    function bar(){
        var a=6;
        foo();
    }
    bar(); //2
    • foo()被调用时,this.a决定全局变量a。因为在这种情况下,this与函数调用相关,this始终指向全局对象

    • 如果在strict mode的影响下,全局对象将不会是默认绑定,相反this将会被设置成undefined

    function foo(){
        "use strict";
        console.log(this.a);
    }
    var a=2;
    foo(); //TypeError: this is undefined
  • 隐式绑定

    • 如果函数作为对象的方法调用,this指向的是这个上级对象,即调用方法的对象
    function foo(){
        console.log(this.a);
    }
    var obj1={
        a:2,
        foo:foo
    };
    obj1.foo() //2
    var obj2={
        a=42,
        obj1:obj1
    }
    obj2.obj1.foo() //2
    • 不管foo是在obj中声明的还是作为引用加入的,这个函数都已经属于obj对象
  • 隐式失绑

    • 当一个隐式绑定失去绑定转化为默认绑定时,,this绑定将会根据是否为strict mode变为全局对象或者是undefined
    function foo(){
        console.log(this.a);
    }
    function doFoo(fn){
        //`fn`仅仅是一个`foo`的引用
        fn(); //<-- call-site
    }
    var obj={
        a:2,
        foo:foo
    };
    var a="oops,global";//`a`同样也是一个全局对象的一个属性
    doFoo(obj.foo); //"oops,global"
    setTimeout(obj.foo,100);//"oops,global"
  • 显式绑定

    function foo(){
        console.log(this.a);
    }
    var obj={
        a:2
    };
    foo.call(obj); //2
    • 通过显式绑定foo.call(),在调用foo时允许我们强制将this赋值为obj

    • 如果你将一个基本的类型作为this的绑定值,那么这些基本值会转换成相应的形式(new String(..)new Boolean(..)new Number(..)),这通常被叫做装箱

  • 硬绑定

    • 硬绑定是显式绑定的一种形式,这种绑定易读并且健壮
    function foo(){
        console.log(this.a);
    }
    var obj={
        a:2,
        foo:foo
    };
    var bar=function(){
        foo.call(obj);
    };
    bar(); // 2
    setTimeout(bar,100); // 2
    //`bar`使得`foo`的`this`始终与`obj`相绑定,并且不能被覆盖
    bar.call(window); //2
    function foo(something){
        console.log(this.a,something);
        return this.a+something;
    }
    function bind(fn,obj){
        return function(){
            return fn.apply(obj,arguments);
        };
    }
    var obj={
        a:2
    };
    var bar=bind(foo,obj);
    var b=bar(3); // 2 3
    console.log(b) // 5
    • 硬绑定是一种常用的模式,在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
  • API调用环境

    • 许多库函数和许多javascript语言里新增的内嵌函数提供了一个可供选择的参数,通常叫做’上下文‘,它被用来确保你的回调函数在不使用bind(..)的情况下使用一个指定的this
    function foo(el){
        console.log(el,this.id);
    }
    var obj={
        id:"awesome"
    };
    //对于`foo`的调用使用`obj`作为`this`
    [1,2,3].forEach(foo,obj); // 1 awesome 2 awesome 3 awesome
  • new绑定

    在传统的面向对象语言中,构造器是类的一个特殊的方法,当类通过new运算符来实例化时,构造器通常像这样被调用something = new MyClass(..);。javascript也有new运算符,但与此不同。在javascript中,构造器仅仅是和new运算符一起调用的函数,它不和类关联,也不能初始化对象,仅仅是常规的函数。举个例子,当Number(..) 函数被调用作为new运算符的一部分,它作为一个构造器初始化一个新创造的对象。

    当一个函数Foonew运算符一起被触发,或者说作为构造器被调用,下列的事情将会被自动完成

    1. 一个继承自 Foo.prototype 的新对象被创建。
    2. 使用指定的参数调用构造函数 Foo ,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
    3. 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
    function Foo(a){
        this.a=a;
    }
    var bar=new Foo(2);
    console.log(bar.a); // 2

绑定规则优先级

  1. new绑定
  2. 显式绑定
  3. 隐式绑定
  4. 默认绑定
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章