this 是怎樣


Posted by ericcch24 on 2020-10-16

參考資料:What's THIS in JavaScript ?

  • 面試回答:
let obj = {
  x: 1,
  test: function () {
    console.log(this.x);
  },
};

obj.test(); // 1
// this 會指向呼叫這個函式的物件,也就是 obj 本身
// 等於 obj.test.call(obj)
  • this 是 JavaScript 的一個關鍵字。
  • this 是 function 執行時,自動生成的一個內部物件。
    隨著 function 執行場合的不同,this 所指向的值,也會有所不同。
  • 在大多數的情況下, ==this 代表的就是呼叫 function 的物件== (Owner Object of the function)。
var getGender = function(){
  return this.gender;
};

var people1 = {
  gender: 'female',
  getGender: getGender
};

var people2 = {
  gender: 'male',
  getGender: getGender
};

console.log( people1.getGender() ); // female
console.log( people2.getGender() ); // male

==this 會因執行的環境與上下文 (context) 的不同,而有不同的結果。==,this 代表的是 function 執行時所屬的物件。


  • 在物件導向中,this 用來存取對應的 instance,也就是代表在物件導向裡面,那個 instance 本身。
class Car {
  setName(name) {
    this.name = name
  }

  getName() {
    return this.name
  }
}

const myCar = new Car()
myCar.setName('hello')
console.log(myCar.getName()) // hello

在上面宣告了一個 class Car,寫了 setName 跟 getName 兩個方法,在裡面用this.name來存取這個 instance 的屬性。

==this 的作用就是所指到的對象就是那個 instance 本身。==

以上面的範例來說,myCar.setName('hello'),所以 this 就會是myCar


一但脫離了物件導向,其實 this 就沒有什麼太大的意義

在沒有意義的地方呼叫 this,預設值會是什麼?

function hello(){
  console.log(this)
}

hello()
  1. 嚴格模式use strict底下就都是undefined
  2. 非嚴格模式,瀏覽器底下是window
  3. 非嚴格模式,node.js 底下是global

直接印出 this 會是一個空物件

var a = '10'
console.log(this) // {}
console.log(this.a)// undefined
console.log(10 == this.a) // false
console.log(10 === this.a) // false

更改 this 的值

call & apply

'use strict';
function hello(a, b){
  console.log(this, a, b)
}

hello(1, 2) // undefined 1 2
hello.call(undefined, 1, 2) // undefined 1 2
hello.apply(undefined, [1, 2]) // undefined 1 2

call 跟 apply 的差別就是,call 跟平常呼叫 function 一樣傳入 function 的參數,apply 的第二個參數則是將 function 的參數用 array 包起來。

至於第一個參數傳什麼,裡面 this 的值就會是什麼。

'use strict';
function hello(a, b){
  console.log(this, a, b)
}

hello.call('yo', 1, 2) // yo 1 2
hello.apply('hihihi', [1, 2]) // hihihi 1 2

儘管原本已經有 this,也依然會被這種方法給覆蓋掉:

class Car {
  hello() {
    console.log(this)
  }
}

const myCar = new Car()
myCar.hello() // myCar instance
myCar.hello.call('yaaaa') // yaaaa

bind

'use strict';
function hello() {
  console.log(this)
}

const myHello = hello.bind('my')
myHello() // my

與 call 和 apply 不同,==bind 會回傳一個新的 function==,在這邊我們把 hello 這個 function 用 my 來綁定,所以最後呼叫 myHello() 時會輸出 my。


小總結

  1. 在物件以外的 this 基本上沒有任何意義,硬要輸出的話會給個預設值
  2. 可以用 call、apply 與 bind 改變 this 的值

物件中的 this

重點注意:

this 的值跟作用域跟程式碼的位置在哪裡完全無關,只跟「你如何呼叫」有關

const obj = {
  value: 1,
  hello: function() {
    console.log(this.value)
  }
}

obj.hello() // 1
const hey = obj.hello
hey() // undefined

明明就是同一個函式,怎麼第一次呼叫時 this.value 是 1,第二次呼叫時就變成 undefined 了?

==function call 呼叫方式補充:==
在 ES5 中正常情況下的 function call:
func.call(context, p1, p2),其中第一個參數 context 就是 this

"use strict"
function test() {
  console.log(this)
}

test() === test.call() === test.call(undefined)
// strict 模式下是 undefined
// 在 node.js 是 global, 在瀏覽器是 window

test.call("ass") // 第一個參數傳的值就是 this
// ass

---

var obj = {
  test: function() {
    console.log(this)
  }
}

obj.test() === obj.test.call(obj)
// {test: [Function: test]} 就是 obj 自己

參考資料:this 的值到底是什么?一次说清楚

這時把所有的 function call,都轉成利用call的形式來看,以上面那個例子來說,會是這樣:

const obj = {
  value: 1,
  hello: function() {
    console.log(this.value)
  }
}

obj.hello() // 1
obj.hello.call(obj) // 轉成 call,印出 1
// obj.hello() 可以看成 obj.hello.call(obj)
// 括號內的 obj 就是 this


const hey = obj.hello
hey() // undefined
hey.call() // 轉成 call 來看,沒傳參數,所以是 undefined

在非物件導向、非嚴格模式下,呼叫 hello 函式,依舊將其轉為 call 的形式來看。

在不同的執行環境 (runtime) 下,傳入 call 第一個參數的值不同,在瀏覽器下是 window,在 node.js 下是 global,所以轉為 call 的形式變成:hello.call(window)/hellow.call(global)

因已將 hello 賦值為 obj.inner.hello,hello() 會執行 console.log(this.value),欲印出 this.value 的值。而無論 runtime 是瀏覽器或是 node.js,在全域環境中都找不到 value 的值,故都會印出 undefined。

範例 1:

const obj = {
  value: 1,
  hello: function() {
    console.log(this.value)
  },
  inner: {
    value: 2,
    hello: function() {
      console.log(this.value)
    }
  }
}

const obj2 = obj.inner
const hello = obj.inner.hello
obj.inner.hello() 
// obj.inner.hello.call(obj.inner) -> 2


obj2.hello()
// obj2.hello.call(obj2) -> 2

hello()
// hello.call() -> undefined

範例 2:

function hello() {
  console.log(this)
}

var a = { value: 1, hello }
var b = { value: 2, hello }
hello() // hello.call() => window(瀏覽器非嚴格模式)
a.hello() // a.hello.call(a) => a
b.hello.apply(a) => 直接用 apply,所以就是 a

不合群的箭頭函式

因為箭頭函式沒有 this,所以在箭頭函式內看到 this,直接把它當作外面宣告的 this 即可,箭頭函式內外的 this 是一樣的東西。
箭頭函式內的 this 跟如何呼叫無關,跟如何定義有關。

"use strict"
const obj = {
  x: 1,
  hello: function(){
    // 這邊印出來的 this 是什麼,test 的 this 就是什麼
    // 在宣告它的地方的 this 是什麼,test 的 this 就是什麼
    console.log(this)     
    const test = () => {
      console.log(this) 
      // 所以這邊的 this 就是上面宣告的 this
    }
    test()
  }
}

obj.hello() 
// {x:1, hello: [Function: hello]} 即 obj 本身
// 同上 obj 本身

const hello = obj.hello
hello() 
// undefined
// undefined

總結

  1. 脫離物件的 this 基本上沒有任何意義
  2. 沒有意義的 this 會根據嚴格模式以及環境給一個預設值
  3. 嚴格模式底下預設就是 undefined,非嚴格模式在瀏覽器底下預設值是 window
  4. 可以用 call、apply 與 bind 改變 this 的值
  5. 要看 this,就看這個函式「怎麽」被呼叫
  6. 可以把hello()看成hello.call()a.b.c.hello() 看成 a.b.c.hello.call(a.b.c),以此類推,就能輕鬆找出 this 的值
  7. this 就是 call 一個函式時,傳入的第一個參數

參考資料:
淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂
this 的值到底是什么?一次说清楚

tags: Week16

#week16







Related Posts

使用 Python 和 PyGame 遊戲製作入門教學

使用 Python 和 PyGame 遊戲製作入門教學

CSS Relative Unit: rem & em

CSS Relative Unit: rem & em

資結、 Sorting Algorithms I

資結、 Sorting Algorithms I


Comments