this 初探

經濟基礎決定上層建築。javascript


我來了

  • 學習,記錄,備忘。
  • 感謝參考過的全部資料的做者。
  • 嗯,能看源碼的就不要看文檔,能看英文文檔的就不要看中文文檔,能本身上手驗證的就不要僅僅參考。請保持質疑。深有所感。

this

定義

A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.
In most cases, the value of this is determined by how a function is called. It can't be set by assignment during execution, and it may be different each time the function is called. ES5 introduced the bind method to set the value of a function's this regardless of how it's called, and ES2015 introduced arrow functions which don't provide their own this binding (it retains the this value of the enclosing lexical context).
  1. js 的嚴格模式和非嚴格模式有所不一樣。至於有什麼不一樣,可能寫個 demo 比聽別人解釋更直接。
  2. 大多數狀況,this 的值取決於 function 是怎麼被調用的。在執行期間,是不能經過賦值來設置 this 的值的,而且每次 function 的調用,this 的值也是可能不一樣的。

可能較爲難理解的就是 this 所對應的 context 了。
如下暫不考慮嚴格模式。html

Global Context

在全局執行 context 下,不管是否在嚴格模式下,this 指向全局對象。java

// 瀏覽器中,全局對象指 window
console.log(this === window) // true
  
// 全局調用 alert
// 至關於:window.alert(this)
alert(this) // [Object Window]

Function Context

Inside a function, the value of this depends on how the function is called.

在函數內,this 取決於這個函數是如何被調用的。取決於調用這個函數的對象。瀏覽器

  1. Simple callapp

    function sayName() {
      console.log(this.name)
    }
    
    var name = '~ window ~'
    
    var apple = {
      name: 'apple'
    }
    
    var banana = {
      name: 'banana'
    }
    
    // 全局調用,至關於:window.getThis(),this 指向 window
    sayName() // ~ window ~
    
    // call 改變 this 指向,this 指向 apple
    sayName.call(apple) // apple
    
    // apply 改變 this 指向,this 指向 banana
    sayName.apply(banana) // banana
    
    // 注意:
    // 在使用 call/apply 來改變 this 的指向的時候,
    // 若是第一個參數數據類型不是 object 時,如:7 或者 'test',內部會嘗試將該值用其相關的構造函數轉爲對象,
    // 即:7 => new Number(7), 'test' => new String('test')
    
    function getThis() {
      console.log(this)
    }
    
    var str_1 = 'test'
    getThis.apply(str_1) // 原諒愚笨,這個結果不知如何描述,可測。
  2. The bind methodless

    ECMAScript 5 introduced Function.prototype.bind. Calling f.bind(someObject) creates a new function with the same body and scope as f, but where this occurs in the original function, in the new function it is permanently bound to the first argument of bind, regardless of how the function is being used.

    調用 f.bind(someObject) 會建立一個新 function ,這個新 function 擁有函數 f 相同的主體和做用域。可是 this 的存在只出如今原始函數(f)中,在新 function 中 ,this 就被永久的綁定在 bind 的第一個參數(someObject)上,不管這個新 function 是怎麼被調用。ide

    function sayName() {
      console.log(this.name);
    }
      
    var apple = {
      name: 'apple'
    }
    
    var banana = {
      name: 'banana'
    }
    
    // bind 返回一個新函數,且新函數 this 已經綁定 apple
    var appleName = sayName.bind(apple)
    appleName() // apple
    
    // 因爲 appleName 已經永久綁定 apple,即便再次綁定 banana,this 依然指向 apple
    var bananaName_1 = appleName.bind(banana)
    bananaName_1() // apple
    
    // 可是從新對原始函數進行 bind 綁定,會再次返回一個新函數,新函數 this 永久綁定 banana
    var bananaName_2 = sayName.bind(banana)
    bananaName_2() // banana
    // interesting...
  3. Arrow functions函數

    In arrow functions, this retains the value of the enclosing lexical context's this. In global code, it will be set to the global object.
    In arrow functions, this is set to what it was when it was created (in the following example, the global object). The same applies to arrow functions created inside other functions: their this remains that of the enclosing lexical context.

    箭頭函數中的 this 保留 the enclosing lexical context 中 this 的值。全局環境中箭頭函數 this 指向全局變量。
    箭頭函數中的 this 指向定義這個箭頭函數時所在對象(下面的例子中,this 指向全局對象)。在其餘函數內部的箭頭函數,這些箭頭函數的 this 保留 the enclosing lexical context 。學習

    var getThis = (() => this)
    console.log(getThis() === window)
    // Create obj with a method bar that returns a function that
    // returns its this. The returned function is created as 
    // an arrow function, so its this is permanently bound to the
    // this of its enclosing function. The value of bar can be set
    // in the call, which in turn sets the value of the 
    // returned function.
    var obj = {bar: function() {
      var x = (() => this);
      return x;
    }
    };
    
    // Call bar as a method of obj, setting its this to obj
    // Assign a reference to the returned function to fn
    var fn = obj.bar();
    
    // Call fn without setting this, would normally default
    // to the global object or undefined in strict mode
    console.log(fn() === obj); // true
    
    // But caution if you reference the method of obj without calling it
    var fn2 = obj.bar;
    // Then calling the arrow function this is equals to window because it follows the this from bar.
    console.log(fn2()() == window); // true
    // 此例子以爲 MDN 上講解的很到位了
    Note: if this arg is passed to call, bind, or apply on invocation of an arrow function it will be ignored. You can still prepend arguments to the call, but the first argument (thisArg) should be set to null.

    注意:箭頭函數的 call/apply/bind 的第一個綁定參數會被忽略。依然能夠用這些方法傳遞後面的參數,可是第一個參數應設置爲 null 。ui

    var sayName = (() => this.name)
    
    var apple = {
      name: 'apple'
    } 
    
    var banana = {
      name: 'banana'
    }
    
    // this 依然指向全局對象 window
    console.log(sayName.call(apple))  // ''
    console.log(sayName.bind(banana)()) // ''
  4. As an object method

    When a function is called as a method of an object, its this is set to the object the method is called on.

    當一個函數做爲一個對象的方法被調用時,這個函數的 this 指向調用該方法的對象。

    var apple = {
      name: 'apple',
      category: 'fruit',
      sayName: function (){
          console.log(this.name)
      }
    }
    
    function getCategory() {
      console.log(this.category) 
    }
    
    apple.getCategory = getCategory
    
    apple.getCategory() // fruit
    apple.sayName() // apple
    Similarly, the this binding is only affected by the most immediate member reference.

    this 的指向只與最直接調用函數的對象有關.

    var apple = {
      name: 'apple',
      category: 'fruit',
      sayName: function (){
          console.log(this.name)
      }
    }
    
    function madeIn() {
      console.log(this.name)
    }
    
    apple.produce = {
       name: 'China',
       madeIn: madeIn
    }
    
    // this 指向最直接調用本身的對象 apple.produce
    apple.produce.madeIn()
    The same notion holds true for methods defined somewhere on the object's prototype chain. If the method is on an object's prototype chain, this refers to the object the method was called on, as if the method were on the object.

    對象原型鏈上的方法也是如此(即: this 指向調用該方法的對象)。若是這個方法在對象的原型鏈上,當這個對象調用方法時,this 指向這個對象。正如對象的方法同樣。

    var base_methods = {
      sayName: function() {
        console.log(this.name)
      },
      getCategory: function() {
        console.log(this.category)
      }
    }
    
    var apple = Object.create(base_methods)
    apple.name = 'apple'
    apple.category = 'fruit'
    
    apple.sayName() // apple
    apple.getCategory() // fruit
    // interesting...
    Again, the same notion holds true when a function is invoked from a getter or a setter. A function used as getter or setter has its this bound to the object from which the property is being set or gotten.

    getter/setter 中的函數調用也是如此(即: this 指向調用該方法的對象)。用做 getter/setter 的函數,this 指向用這個函數定義 get/set 屬性的那個對象 。

    var apple = {
      name: 'apple'
    }
    
    function sayName() {
      console.log(this.name)
    }
    
    Object.defineProperty(apple, 'sayName', {
      get: sayName, enumerable: true, configurable: true
    })
    
    apple.sayName // apple
    // 目前對 getter/setter 的執行暫未深刻了解
  5. As a constructor

    When a function is used as a constructor (with the new keyword), its this is bound to the new object being constructed.
    While the default for a constructor is to return the object referenced by this, it can instead return some other object (if the return value isn't an object, then the this object is returned).

    當一個 function 做爲一個 constructor 的時候,在用 new 關鍵字實例化一個新對象以後,funtion 的 this 指向這個新對象。
    須要額外注意的是,constructor 在沒有 return 的時候,默認返回一個 this 引用的對象,也能夠指定 return 一個其餘對象(若是 return 的不是一個對象,那麼返回的是 this 引用的對象)
    constructor 如何執行不作贅述。

    function Person() {
      this.nature = 'Person'
    }
    
    function Teacher() {
      this.nature = 'Teacher'
      return {
        name: 'Jane'
      }
    }
    
    function Student() {
      this.nature = 'Student'
      return 'Summer'
    }
    
    var a = new Person()
    console.log(a.nature) // Person
    
    var b = new Teacher()
    console.log(b.nature) // undefined
    console.log(b.name) // Jane
    
    var c = new Student()
    console.log(c.nature) // Student
  6. As a DOM event handler
    當一個 function 用於在 DOM 的事件監聽中, this 指向這個 DOM 元素。

    function getThis() {
      console.log(this)
    }
    
    var demo = document.getElementById('demo') // html 須要本身寫
    
    demo.addEventListener('click', getThis, false) // this 指向 id='demo' 的 DOM 元素
    demo.addEventListener('mouseover', function() {
      console.log(this) // this 指向 id='demo' 的 DOM 元素
    }, false)
  7. In an inline event handler

    // 在 HTML 的 DOM 的內聯中監聽事件
    // 如下的 this 指向正在監聽的 DOM 自己,即:這個 button 元素
    <button onclick="console.log(this)">
      Show this
    </button>
    
    // 然而下面的寫法,在內聯事件內部函數體中的 this 指全局對象 window
    <button onclick="console.log((function() { return this })())">
      Show inner this
    </button>

參考


好記性不如爛筆頭。

相關文章
相關標籤/搜索