FE 讀書會(二):Function
Function
What is this
在其他以類別為基礎的程式語言中,this指的是目前使用類別進行實體化的物件。
而JavaScript語言中因為在設計上並不是以類別為基礎的物件導向,設計上不一樣,所以this的指向的是目前呼叫函式或方法的擁有者(owner)物件,也就是說它與函式如何被呼叫或調用有關,雖然是同一函式的呼叫,因為不同的物件呼叫,也有可能是不同的this值。
全域環境下
this 值在所有函式以外的全域執行環境下,會被當作全域物件。
console.log(this);
//function declaration
function a(){
console.log(this);
}
a();
//function expression
let b = function(){
console.log(this);
}
b();
函式環境下
當這個函式是物件裡面的method時,這時候的this就會指稱到包含這個method的物件
var c = {
log: function(){
console.log(this);
}
}
c.log(); // this為c
this
會亂認人, 也因為這個特性, 如何讓this
與function
好好相認變得很重要, 請往下看
Function bind/call/apply
先說結論
Use .call() or .apply() when you want to invoke the function immediately, and modify the context.
Use .bind() when you want that function to later be called with a certain context, useful in events.
call & apply
使用給定的
this參數
以及分別給定的參數
來呼叫某個函數
fun.call(thisArg[, arg1[, arg2[, ...]]])
fun.apply(thisArg, [argsArray])
兩者差異, call接受一連串變數, apply接受一個Array或陣列
function add(c, d) {
return this.a + this.b + c + d;
}
var o = {a: 1, b: 3};
// 第一個參數(parameter)是調用了 this 的物件,
// 後續參數(parameters)會作為函式呼叫內的參數(arguments)而通過
add.call(o, 5, 7); // 16
// 第一個參數(parameter)是調用了 this 的物件,
// 第二個參數的陣列作為函式呼叫內的參數(arguments)之構件
add.apply(o, [10, 20]); // 34
特殊用法: apply可以用來debug既有的function
var originalfoo = someobject.foo;
someobject.foo = function() {
// Do stuff before calling function
console.log(arguments);
// Call the function as it would have been called normally:
originalfoo.apply(this, arguments);
// Run stuff after, here.
}
bind
bind() 方法,會建立一個新函式。該函式被呼叫時,會將
this
關鍵字設為給定的參數,並在呼叫時,帶有提供之前,給定順序的參數。
fun.bind(thisArg[, arg1[, arg2[, ...]]])
我的理解是, function會被各種人call, 如果function內部使用this, 會無法保證值一定會跟開發者想的一樣, 所以使用bind將function的context(也就是this回傳的物件)綁定, 未來無論如何使用都會是一樣的context. 也因為這個性質, 當任一function第一次bind完後, 後面的bind皆無效.
function f() {
return this.a;
}
var g = f.bind({a: 'azerty'});
console.log(g()); // azerty
var h = g.bind({a: 'yoo'}); // bind 只能使用一次!
console.log(h()); // azerty
var o = {a: 37, f: f, g: g, h: h};
console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty
bind 可以套用Partial Function唷!
function plus(a, b) {
return a + b;
}
var addSeven = plus.bind(undefined, 7);
console.log(addSeven(3)); // 10
console.log(addSeven(23)); //30
用call回頭複習一下this值的產生規則
- 當使用strict code(嚴格模式程式碼)時,直接設定為call方法裡面的thisArg(this參數值)。
//注意嚴格模式strict mode
function f2(){
"use strict"; // 嚴格模式
return this;
}
f2() === undefined; //true
因為 f2 是直接被呼叫,而不是在其為某個物件的方法或屬性的情況下(例如 window.f2())被直接呼叫。某些瀏覽器首次支援嚴格模式時沒導入這個特徵,它們會因此錯誤的回傳 window 物件。
- 當thisArg(this參數值)是null或undefined時,會綁定為全域(global)物件。
- 當thisArg(this參數值)的類型不是物件類型時,會綁定為轉為物件類型的值。
- 都不是以上的情況時,綁定為thisArg(this參數值)。
最重要的一點, 當無法確定this是誰的時候, 記得把它console.log來看看比較重要.
確保this
的方法
舉例來說
function Person() {
// 這個 Person() 建構式為它自己定義了 this
this.age = 0;
setInterval(function growUp() {
// 在非嚴謹模式下, growUp() 函式把 this 定義為全域物件,
// 而不是 Person() 建構式所定義的 this
this.age++;
}, 1000);
}
方法1 為作用域鏈(Scope Chain)
function Person() {
var self = this; // 有些人喜歡 `that` 而不是 `self`.
// 選好一種取法後始終如一
self.age = 0;
setInterval(function growUp() {
// 這個 callback 參考 `self` 變數,為預期中的物件。
self.age++;
}, 1000);
}
方法2是使用bind, 確保this的context不會錯誤
function Person() {
this.age = 0;
setInterval(function growUp() {
this.age++;
}.bind(this), 1000);
}
Named/Anonymous/Arrow function
function add(a,b){
return a+b;
}
//Anonymous 1
let add = function(a,b){
return a+b;
}
//Arrow 1
let add = (a,b) => {
return a+b;
}
//Arrow 2
let add = (a,b) => (a+b);
Why do we need Arrow?
- 短, 可閱讀性高
- 可以綁定this(箭頭函數當中的 this 是定義時的對象,而不是使用時的對象)
舉例來說:
var adder = {
base : 1,
add : function(a) {
var f = v => v + this.base;
return f(a);
},
addThruCall: function(a) {
var f = v => v + this.base;
var b = {
base : 2
};
return f.call(b, a);
}
};
console.log(adder.add(1)); // 顯示 2
console.log(adder.addThruCall(1)); // 依舊顯示 2
Default Parameter Value
參數默認值
以前的日子
function show(message) {
if(typeof message ==="undefined"){
message="預設值";
}
console.log(message);
}
show('Hello'); //Hello
show(); //預設值
ES6
function show(message="預設值") {
console.log(message);
}
show('Hello'); //Hello
show(); //預設值
Reference
Author Leefu Chen
LastMod 2018-05-07