iT邦幫忙

2024 iThome 鐵人賽

DAY 7
0
佛心分享-刷題不只是刷題

30 天克服前端面試系列 第 7

Day 7 - 請說明 this 如何在 JavaScript 中運作?

  • 分享至 

  • xImage
  •  

在 JavaScript 中this的指向會根據不同呼叫的方式而有不同的結果,以下分別列出幾種this的運作方式:

全域環境

在全域的環境下:

  • 瀏覽器中:this指向的是window物件
  • Node.js: this指向一個空的模組作用域內的物件
console.log(this === window); //true

直接呼叫函式

當直接呼叫函式,在函式中的this均指向的是window物件。

function hiThis() {
  console.log(this === window); // true (在瀏覽器中,非嚴格模式)
}

hiThis();

("use strict");
function hiStrict() {
  console.log(this === undefined); // true (嚴格模式)
}

hiStrict();

new 運算子

使用new運算子會建立一個新的物件實例,此時的this的指向為該物件。

function Person(name) {
  this.name = name;
}

const person = new Person("Ashley");
console.log(person.name); // "Ashley"

物件的方法

當呼叫物件中的方法時,此時的this指向的是呼叫該方法的物件。

const person = {
  name: "Ashley",
  sayHi: function () {
    console.log(`Hi!${this.name}.`); //Hi!Ashley.
  },
};

person.sayHi();

在這個例子中,this指向的是 person

class constructor

在 class 中 this 是自動綁定到 class 的實例,在 class 函式建構子中使用this呼叫函式,這裡的this指向的是 class 的實例。

class Person {
  constructor(name) {
    this.name = name;
  }

  getPersonName() {
    console.log(this);
  }
}

const person = new Person("Ashley");
console.log(person.getPersonName()); //Person{name:'Ashley'}

this 指向 class 的物件實例,也就是 Person 的實例。

箭頭函式

箭頭函式沒有自己的this,是繼承外層作用域的 this,因此即使在物件內使用箭頭函式,this 也不會指向該物件,而是指向箭頭函式定義時的外層環境。

const person = {
  name: "Ashley",
  sayHi: () => {
    console.log(`Hi!${this.name}`); //Hi!undefined
  },
};

person.sayHi();

因為箭頭函式中的 this 是從外層作用域繼承,而不是指向 person,因為箭頭函式中的 this 指向外層的window,所以此例中 this.nameundefined

const person = {
  name: "Ashley",
  sayHi: function () {
    const arrowFn = () => {
      console.log(`Hi!${this.name}`); //Hi!Ashley
    };
    arrowFn();
  },
};

person.sayHi();

在這個例子中,arrowFn 繼承了 sayHi 函式中的 this,因此指向 person

使用 .call()、.apply().bind()

使用.call.apply.bind.可以傳入物件改變this的指向。

function greet() {
  console.log(this.name);
}

const person = { name: "Ashley" };

greet.call(person); // Ashley
greet.apply(person); // Ashley

const boundGreet = greet.bind(person);
boundGreet(); // Ashley

call()apply() 會立即執行函式,並將 this 設定為傳入的物件。
bind() 則會返回一個新的函式,並永久綁定 this 到傳入的物件。


實例練習 1

41. this III

// This is a JavaScript Quiz from BFE.dev

const obj = {
  a: 1,
  b: this.a + 1,
  c: () => this.a + 1,
  d() {
    return this.a + 1;
  },
  e() {
    return (() => this.a + 1)();
  },
};
console.log(obj.b);
console.log(obj.c());
console.log(obj.d());
console.log(obj.e());

解題 1

// This is a JavaScript Quiz from BFE.dev

const obj = {
  a: 1,
  b: this.a + 1,
  c: () => this.a + 1,
  d() {
    return this.a + 1;
  },
  e() {
    return (() => this.a + 1)();
  },
};
console.log(obj.b); //NaN; 因為這裡this指的是window物件,undefined + 1
console.log(obj.c()); //NaN; 因為這裡this指的是window物件,undefined + 1
console.log(obj.d()); //2;因為這裡this指的是obj,1+1
console.log(obj.e()); //2;因為匿名函式的this指向是上一層的作用域,上一層的指向是obj,1+1

實例練習 2

19. this

// This is a JavaScript Quiz from BFE.dev

const obj = {
  a: 1,
  b: function () {
    console.log(this.a);
  },
  c() {
    console.log(this.a);
  },
  d: () => {
    console.log(this.a);
  },
  e: (function () {
    return () => {
      console.log(this.a);
    };
  })(),
  f: function () {
    return () => {
      console.log(this.a);
    };
  },
};

console.log(obj.a);
obj.b();
obj.b();
const b = obj.b;
b();
obj.b.apply({ a: 2 });
obj.c();
obj.d();
obj.d();
obj.d.apply({ a: 2 });
obj.e();
obj.e();
obj.e.call({ a: 2 });
obj.f()();
obj.f()();
obj.f().call({ a: 2 });

解題 2

// This is a JavaScript Quiz from BFE.dev

const obj = {
  a: 1,
  b: function () {
    console.log(this.a);
  },
  c() {
    console.log(this.a);
  },
  d: () => {
    console.log(this.a);
  },
  e: (function () {
    return () => {
      console.log(this.a);
    };
  })(),
  f: function () {
    return () => {
      console.log(this.a);
    };
  },
};

console.log(obj.a); //1
obj.b(); //1,this指向為obj
obj.b(); //1,this指向為obj
const b = obj.b;
b(); //undefined,b 被賦值給變數 b,這時 this 是 undefined
obj.b.apply({ a: 2 }); //2,因為使用apply指定this為{ a: 2 }
obj.c(); //1,this指向為obj
obj.d(); //undefined,因為是箭頭函式,綁到上一層的作用域也就是全域
obj.d(); //undefined
obj.d.apply({ a: 2 }); //undefined,因為是箭頭函式,參考上一層
obj.e(); //undefined,因為是箭頭函式,參考上一層
obj.e(); //undefined,因為是箭頭函式,參考上一層
obj.e.call({ a: 2 }); //undefined,箭頭函數的 this 不會被 call 改變
obj.f()(); //1,f 返回一個箭頭函數,this 綁定到 f 的 this,即 obj
obj.f()(); //1
obj.f().call({ a: 2 }); //1,箭頭函數的 this 不會被 call 改變

本文同步於此


上一篇
DAY 6 - 請說明 JavaScript 中的事件委派 event delegation 是什麼?
下一篇
Day 8 - 請描述 cookie, sessionStorage 和 localStorage 在瀏覽器中的差異為何?
系列文
30 天克服前端面試16
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言