1. 程式人生 > >JavaScript中的this關鍵字的幾種用法

JavaScript中的this關鍵字的幾種用法

JS 裡的 this

  • 在 function 內部被建立
  • 指向呼叫時所在函式所繫結的物件(拗口)
  • this 不能被賦值,但可以被 call/apply 改變

1. this 和建構函式

    function C(){
    this.a = 37;
    }

    var o = new C();
    console.log(o.a); // logs 37


    function C2(){
    this.a = 37;
    return {a:38};
    }

    var b = new C2();
    console.log(b.a); // logs 38

2. this 和物件

物件內部方法的this指向呼叫這些方法的物件:

  • 函式的定義位置不影響其this指向,this指向只和呼叫函式的物件有關。
  • 多層巢狀的物件,內部方法的this指向離被呼叫函式最近的物件(window也是物件,其內部物件呼叫方法的this指向內部物件, 而非window)。
    //1:this指向呼叫函式的物件
    var o = {
        prop: 37,
        f: function() {
            return this.prop;
        }
    };
    console.log(o.f());  //37   this指向o
    var a = o.f;
    console.log(a()):  //undefined  this指向a ,a中沒有定義prop

    var o = {prop: 37};
    function independent() {
        return this.prop;
    }
    o.f = independent;
    console.log(o.f()); // logs 37      this指向o

    //2:this指向離被呼叫函式最近的物件
     var o = {
            prop: 37,
            f: function() {
                return this.prop;
            }
        };
        function independent() {
            return this.prop;
        }
        o.b = {
            g: independent,
            prop: 42
            };
            console.log(o.b.g());   //42    this指向o.b

3. this 和函式

普通函式內部的this分兩種情況,嚴格模式非嚴格模式

//非嚴格模式下,this 預設指向全域性物件window
function f1(){
  return this;
}
f1() === window; // true

//而嚴格模式下, this為undefined
function f2(){
  "use strict"; // 這裡是嚴格模式
  return this;
}
f2() === undefined; // true

4. 全域性環境的this

前面提到 this 是 “指向呼叫時所在函式所繫結的物件”, 這句話拗口但絕對正確,沒有一個多餘的字。
全域性環境中有不同的宿主物件,瀏覽器環境中是 window, node 環境中是 global。這裡重點說下瀏覽器環境中的 this。
瀏覽器環境中非函式內 this 指向 window

    alert(window=== this) // true

因此你會看很很多開源 JS lib 這麼寫

    (function() {
        // ...
        
    })(this);

或這樣寫

    (function() {
        // ...
    
    }).call(this);

比如 underscore 和 requirejs,大意是把全域性變數 window 傳入匿名函式內快取起來,避免直接訪問。至於為啥要快取,這跟 JS 作用域鏈有關係,讀取越外層的識別符號效能會越差。

瀏覽器中比較坑人,非函式內直接使用 var 宣告的變數預設為全域性變數,且預設掛在 window 上作為屬性。

    var andy = '劉德華'
    alert(andy === window.andy) // true
    alert(andy === this.andy) // true
    alert(window.andy === this.andy) // true

因為這個特性,有些筆試題如

    var x = 10;
    function func() {
        alert(this.x)
    }
    var obj = {
        x: 20,
        fn: function() {
            alert(this.x)
        }
    }
    var fn = obj.fn
    func() // 10
    fn() // 10

沒錯,最終輸出的都是全域性的 10。永遠記住這一點:
判斷 this 指向誰,看執行時而非定義時只要函式(function)沒有繫結在物件上呼叫,它的 this 就是 window

5. this和DOM事件

當函式被當做監聽事件處理函式時, 其 this 指向觸發該事件的元素 (針對於addEventListener事件)

    // 被呼叫時,將關聯的元素變成藍色
    function bluify(e){
      //在控制檯打印出所點選元素
      console.log(this);
      //阻止時間冒泡
      e.stopPropagation();
      //阻止元素的預設事件
      e.preventDefault();      
      this.style.backgroundColor = '#A5D9F3';
    }

    // 獲取文件中的所有元素的列表
    var elements = document.getElementsByTagName('*');

    // 將bluify作為元素的點選監聽函式,當元素被點選時,就會變成藍色
    for(var i=0 ; i<elements.length ; i++){
      elements[i].addEventListener('click', bluify, false);

6. this和內聯事件

內聯事件中的this指向分兩種情況:

  • 當代碼被內聯處理函式呼叫時,它的this指向監聽器所在的DOM元素
  • 當代碼被包括在函式內部執行時,其this指向等同於 函式直接呼叫的情況,即在非嚴格模式指向全域性物件window, 在嚴格模式指向undefined



    依次點選上邊的三個按鈕後在控制檯的輸出結果

    7. setTimeout & setInterval

    對於延時函式內部的回撥函式的this指向全域性物件window(當然我們可以通過bind方法改變其內部函式的this指向)
    看下邊程式碼及截圖
    //預設情況下程式碼
    function Person() {  
        this.age = 0;  
        setTimeout(function() {
            console.log(this);
        }, 3000);
    }

    var p = new Person();//3秒後返回 window 物件
    ==============================================
    //通過bind繫結
    function Person() {  
        this.age = 0;  
        setTimeout((function() {
            console.log(this);
        }).bind(this), 3000);
    }

    var p = new Person();//3秒後返回建構函式新生成的物件 Person{...}


8. this可以被 call/apply 改變

當函式通過Function物件的原型中繼承的方法 call() 和 apply() 方法呼叫時, 其函式內部的this值可繫結到 call() & apply() 方法指定的第一個物件上, 如果第一個引數不是物件,JavaScript內部會嘗試將其轉換成物件然後指向它。

例子:

    function add(c, d){
    return this.a + this.b + c + d;
    }

    var o = {a:1, b:3};

    add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16

    add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

    function tt() {
    console.log(this);
    }
    // 返回物件見下圖(圖1)
    tt.call(5);  // Number {[[PrimitiveValue]]: 5} 
    tt.call('asd'); // String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}

9. me/self/that/_this 暫存 this

如果採用 OOP 方式寫 JS 程式碼,無可避免的會用到 this,方法內會訪問類的內部屬性(欄位),也可能會呼叫類的另一個方法。當類的方法內又有一個 function 時,比如瀏覽器端開發經常遇見的給 DOM 元素新增事件,這時如果事件處理器(handler)中的想呼叫類的一個方法,此時 handler 內的 this 是 dom 元素而非類的當前物件。這個時候,需要把 this 暫存,開發者發揮著自己的聰明才智留下了幾種經典的命名** me, self, that, _this**。如


如:

一般會在每個方法的第一句就把 this 暫存下來

10. ES5 中新增的 bind 和 this

bind方法在ES5引入, 在Function的原型鏈上, Function.prototype.bind。通過bind方法繫結後, 函式將被永遠繫結在其第一個引數物件上, 而無論其在什麼情況下被呼叫。

function f(){
  return this.a;
}

var g = f.bind({a:"azerty"});
console.log(g()); // azerty

var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty

11. ES6 箭頭函式=> 和 this

箭頭函式的一個重要特徵就是顛覆了上面的一句話,再貼一次

判斷 this 指向誰,看執行時而非定義時,只要函式(function)沒有繫結在物件上呼叫,它的 this 就是 window

是的,前面一直用這句話來判斷 this 的指向,在箭頭函式裡前面半句就失效了。箭頭函式的特徵就是,定義在哪,this 就指向那。即箭頭函式定義在一個物件裡,那箭頭函式裡的 this 就指向該物件。如下

var book = {
    author: 'John Resig',
    init:  function() {
        document.onclick = ev => {
            alert(this.author) ; // 這裡的 this 不是 document 了
        }
    }
};
book.init()

物件 book 裡有一個屬性 author, 有一個 init 方法, 給 document 添加了一個點選事件,如果是傳統的函式,我們知道 this 指向應該是 document,但箭頭函式會指向當前物件 book。

箭頭函式讓 JS 迴歸自然和簡單,函式定義在哪它 this 就指向哪,定義在物件裡它指向該物件,定義在類的原型上,就指向該類的例項,望文知意這樣更容易理解。
作為方法的箭頭函式this指向全域性window物件,而普通函式則指向呼叫它的物件

原文參考:
https://www.cnblogs.com/snandy/p/4773184.html
http://www.cnblogs.com/dongcanliang/p/7054176.html