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. 默認綁定
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章