welcome to Cheeto's blog

0%

JavaScript核心篇:this (合併整理)

this

  • 每個執行環境都有屬於自己的 this 關鍵字
  • this 與函式如何宣告沒有關聯性,僅與呼叫方式有關
  • 嚴格模式下,簡易呼叫會有很大個改變

this的實際指向跟我們如何去呼叫函式是有很大的關聯性的!

this指的並不是函式本身,而是指向目前呼叫函式或方法的擁有者(owner)物件。所以函式需要了解函式的幾種調用方式,就能一併了解this在這些情況下的不同。

影響函式this的調用方式

this: 物件的方式調用(最常見)

  • this 與函式如何宣告沒有關聯性,僅與呼叫方式有關
  • 物件方法調用時,僅需要關注是在哪一個物件下呼叫

this的用途

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function callName(e) {
console.log(this, this.myName);
};

var family = {
myName: '小明家',
callName: callName,
Ming: {
myName: '小明',
callName: callName
}
}
family.callName();
family.Ming.callName();

this 的常用方式

物件方法調用時,僅需要關注是在哪一個物件下呼叫。
所以family.callName();this就是指向family;而family.Ming.callName();this就是指向family.Ming

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var myName = '真心鎮大冒險';

function callName(e) {
console.log(this, this.myName);
};

var family = {
myName: '小明家',
callName: function(){
console.log(this.myName);
}
}

var callName = family.callName;
callName(); // 真心鎮大冒險

this 與函式如何宣告沒有關聯性,僅與呼叫方式有關。

所以我們只要管它是如何被呼叫出來的就好,而 callName() 是在全域裡面被執行出來的,所以它的this 就會指向 window

this: 簡易呼叫(simple call)

  • 盡可能不要使用 simple call 的 this

簡易呼叫的 this 都會指向 window
簡易呼叫的 this 都會指向 window
簡易呼叫的 this 都會指向 window

1
2
3
4
5
6
7
var myName = '真心鎮大冒險';

function callName(){
console.log(this.myName);
}

callName(); // 真心鎮大冒險

原因是因為我們在全域呼叫函式,所以函式的 this 會指向 window,所以會指到全域變數的 myName。

而這種直接在全域下呼叫的方式就算是「簡易呼叫」的一種。

1
2
3
4
5
6
7
8
9
10
var myName = '真心鎮大冒險';

// IIFE 立即函式
(function (){
console.log(this.myName); //真心鎮大冒險
function callSomeone(){
console.log(this.myName);
}
callSomeone(); //真心鎮大冒險
})()

this 不會看你的呼叫位置,而是依據呼叫方式而定的。

簡易呼叫(閉包)

1
2
3
4
5
6
7
8
9
10
11
12
var myName = '真心鎮大冒險';

function easyCard(base) {
var money = base;
var name = '悠遊卡';
return function (update) {
money = money + update;
console.log(this.myName, money);
}
}
var MingEasyCard = easyCard(100);
MingEasyCard(10); // 真心鎮大冒險 110

這邊也可以看到它跟前面的例子一樣都是在全域下直接呼叫,所以它的this就是指向 window。

簡易呼叫(callback)

所謂的「Callback function」其實就是「把函式當作另一個函式的參數,透過另一個函式來呼叫它」。

callback function 的方式被呼叫,所以算是「簡易呼叫」。所以 this 會指向 window。

1
2
3
4
5
6
7
8
9
var myName = '真心鎮大冒險';

function myEasyCard(callback) {
var money = 100
return callback(money)
}
myEasyCard(function (money) {
console.log(this.myName, money + 100)
});

callback範例


再來下一個範例

1
2
3
4
5
6
7
8
9
10
11
var myName = '真心鎮大冒險';

var family = {
myName: '小明家',
callName: function () {
setTimeout(function () {
console.log(this.myName);
}, 1000);
}
}
family.callName(); //真心鎮大冒險

this 不會看你是在那裡執行,而是看你是如何去執行它。因為setTimeout的()內是一個callback function,所以它是以callback function 的方式被呼叫,所以算是「簡易呼叫」。


如果想要把 this 指向 family 的話,解決方法如下 ↓

1
2
3
4
5
6
7
8
9
10
var family = {
myName: '小明家',
callName: function () {
var self = this; // vm, that
setTimeout(function () {
console.log(self.myName);
}, 1000);
}
}
family.callName(); //小明家

this: call, apply, bind

call

1
2
3
4
5
6
7
8
9
var family = {
name: '小明家'
};

var fn = function (para1, para2) {
console.log(this, para1, para2);
};
fn('小明', '杰倫');
fn.call(family, '小明', '杰倫');

call

call的語法是能夠把 family 傳入 this 當中,這樣就能指定自己要的this

apply

1
2
3
4
5
6
7
8
9
var family = {
name: '小明家'
};

var fn = function (para1, para2) {
console.log(this, para1, para2);
};
fn.call(family, '小明', '杰倫');
fn.apply(family, ['小明', '杰倫']);

apply

apply的語法其實跟call滿相近的,但是它後面的參數必須用陣列的方式設定。

bind

bind的語法跟call是一樣的方式,但是兩者的不同在於說:「bind不會被立刻地執行」

1
2
3
4
5
6
7
8
9
var family = {
name: '小明家'
};

var fn = function (para1, para2) {
console.log(this, para1, para2);
};
fn.call(family, '小明', '杰倫');
fn.bind(family, '小明', '杰倫');

這裡如果執行的話只會出現 fn.call(family, '小明', '杰倫'); 的結果,而bind的執行結果不會出現。

因為「bind不會被立刻地執行」

1
2
3
4
5
6
7
8
9
10
var family = {
name: '小明家'
};

var fn = function (para1, para2) {
console.log(this, para1, para2);
};
fn.call(family, '小明', '杰倫');
var fn2 = fn.bind(family, '小明', '杰倫');
fn2();

apply

call, apply, bind 進階觀念

call, apply, bind呼叫函式時,可以綁定this是什麼。this會是傳進該函式的第一個參數。

call, apply, bind在非嚴格的模式下,null, undefined會被置換成全域變數,而原生型態的值將會被物件包裹呈現

如果把this傳入純值的話,會用物件包裹的方式呈現。

1
2
3
4
5
6
var fn = function (para1, para2) {
console.log(this, para1, para2);
};
fn.call(1, '小明', '杰倫');
fn.call('文字', '小明', '杰倫');
fn.call(true, '小明', '杰倫');

this進階觀念

嚴格模式(Strict Mode)

嚴格模式

  • 在嚴格模式下未定義的變數不能直接的賦予值
1
2
3
4
5
(function () {
'use strict';
auntie = '漂亮阿姨';
// Uncaught ReferenceError: auntie is not defined
})();
  • 嚴格模式對this的影響
1
2
3
4
5
6
7
function callStrict(para1, para2) {
'use strict';
console.log(this, typeof this, para1, para2);
}
callStrict.call(1, '小明', '杰倫')
callStrict.call(undefined, '小明', '杰倫')
callStrict('小明', '杰倫')

嚴格模式this

simple call的this沒有設定的話預設會是undefined,但是在非嚴格模式的時候會被轉成object,然而在嚴格模式下它並不會轉化,依舊會維持本來的undefined,所以盡可能不要使用簡易呼叫的this!!!

this: DOM

DOM跟this綁定會讓this指向觸發事件的元素(HTML element)。

1
<button onclick="console.dir(this)">按鈕</button>

thisDOM範例一

這裡onclick事件裡的this,就是指向button的元素。

1
2
3
4
5
6
7
8
9
10
var fn = function () {
console.dir(this);
this.style.backgroundColor = 'orange';
};

var els = document.querySelectorAll('li');

for (let i = 0; i < els.length; i++) {
els[i].addEventListener('click', fn, false);
};

thisDOM範例二

DOM的事件監聽會讓this指向觸發事件的元素(HTML element)。