js 中物件方法的定義方式是在物件上定義乙個指向函式的屬性,當方法被呼叫的時候,方法內的 this 就會指向方法所屬的物件。
1.1 定義字面量方法
因為箭頭函式的語法很簡潔,可能不少同學會忍不住用它來定義字面量方法,比如下面的例子:
const calculator =};console.log(
this === window); //
=> true
//throws "typeerror: cannot read property 'reduce' of undefined"
calculator.sum();
calculator.sum 使用箭頭函式來定義,但是呼叫的時候會丟擲 typeerror,因為執行時 this.array 是未定義的,呼叫 calculator.sum 的時候,執行上下文裡面的 this 仍然指向的是 window,原因是箭頭函式把函式上下文繫結到了 window 上,this.array 等價於 window.array,顯然後者是未定義的。
解決的辦法是,使用函式表示式或者方法簡寫(es6 中已經支援)來定義方法,這樣能確保 this 是在執行時是由包含它的上下文決定的,修正後的**如下
const calculator =};calculator.sum();
//=> 6
這樣 calculator.sum 就變成了普通函式,執行時 this 就指向 calculator 物件,自然能得到正確的計算結果。
1.2 定義原型方法
同樣的規則適用於原型方法(prototype method)的定義,使用箭頭函式會導致執行時的執行上下文錯誤,比如下面的例子
functioncat(name)
cat.prototype.saycatname = () =>;
const cat = new cat('mew');
cat.saycatname();
//=> undefined
使用傳統的函式表示式就能解決問題
functioncat(name)
cat.prototype.saycatname = function
() ;
const cat = new cat('mew');
cat.saycatname();
//=> 'mew'
saycatname 變成普通函式之後,被呼叫時的執行上下文就會指向新建立的 cat 例項。
this 是 js 中很強大的特性,可以通過多種方式改變函式執行上下文,js 內部也有幾種不同的預設上下文指向,但普適的規則是在誰上面呼叫函式 this 就指向誰,這樣**理解起來也很自然,讀起來就像在說,某個物件上正在發生某件事情。
但是,箭頭函式在宣告的時候就繫結了執行上下文,要動態改變上下文是不可能的,在需要動態上下文的時候它的弊端就凸顯出來。比如在客戶端程式設計中常見的 dom 事件**函式(event listenner)繫結,觸發**函式時 this 指向當前發生事件的 dom 節點,而動態上下文這個時候就非常有用,比如下面這段**試圖使用箭頭函式來作事件**函式:
const button = document.getelementbyid('mybutton');button.addeventlistener('click', () =>);
在全域性上下文下定義的箭頭函式執行時 this 會指向 window,當單擊事件發生時,瀏覽器會嘗試用 button 作為上下文來執行事件**函式,但是箭頭函式預定義的上下文是不能被修改的,這樣 this.innerhtml 就等價於 window.innerhtml,而後者是沒有任何意義的。
使用函式表示式就可以在執行時動態的改變 this,修正後的** :
const button = document.getelementbyid('mybutton');button.addeventlistener('click', function
() );
當使用者單擊按鈕時,事件**函式中的 this 實際指向 button,這樣的 this.innerhtml = 'clicked button' 就能按照預期修改按鈕中的文字。
建構函式中的 this 指向新建立的物件,當執行 new car() 的時候,建構函式 car 的上下文就是新建立的物件,也就是說 this instanceof car === true。顯然,箭頭函式是不能用來做建構函式, 實際上 js 會禁止你這麼做,如果你這麼做了,它就會丟擲異常。
換句話說,箭頭建構函式的執行並沒有任何意義,並且是有歧義的。比如,當我們執行下面的**
const message = (text) =>;//throws "typeerror: message is not a constructor"
const hellomessage = new message('hello world!');
構造新的 message 例項時,js 引擎拋了錯誤,因為 message 不是建構函式。在筆者看來,相比舊的 js 引擎在出錯時悄悄失敗的設計,es6 在出錯時給出具體錯誤訊息是非常不錯的實踐。可以通過使用函式表示式或者函式宣告 來宣告建構函式修復上面的例子:
const message = function(text) ;
const hellomessage = new message('hello world!');
console.log(hellomessage.text);
//=> 'hello world!'
const multiply = (a, b) => b === undefined ? b => a * b : a *b;const
double = multiply(2);
double(3); //
=> 6
multiply(2, 3); //
=> 6
multiply 函式會返回兩個數字的乘積或者返回乙個可以繼續呼叫的固定了乙個引數的函式。**看起來很簡短,但大多數人第一眼看上去可能無法立即搞清楚它幹了什麼,怎麼讓這段**可讀性更高呢?有很多辦法,可以在箭頭函式中加上括號、條件判斷、返回語句,或者使用普通的函式:
functionmultiply(a, b)
}return a *b;
}const
double = multiply(2);
double(3); //
=> 6
multiply(2, 3); //
=> 6
為了讓**可讀性更高,在簡短和囉嗦之間把握好平衡是非常有必要的。
箭頭函式無疑是 es6 帶來的重大改進,在正確的場合使用箭頭函式能讓**變的簡潔、短小,但某些方面的優勢在另外一些方面可能就變成了劣勢,在需要動態上下文的場景中使用箭頭函式你要格外的小心,這些場景包括:定義物件方法、定義原型方法、定義建構函式、定義事件**函式。
this指向問題及什麼時候不能使用箭頭函式?
箭頭函式固然好用,但是也不能隨地濫用!回答這個問題前,先來回想下this指向方面的問題 this只有在方法被呼叫的時候,才知道他的指向,定義的時候是不知道的。而this的指向我把它劃分為以下幾種 1.物件裡方法中的this指向呼叫他的物件。比如延時器方法settimeout 在呼叫時 非嚴格模式 其...
什麼時候使用引用 什麼時候使用指標
使用引用引數的主要原因有兩個 程式設計師能修改呼叫函式中的資料物件 通過傳遞引用而不是整個資料 物件,可以提高程式的執行速度一般的原則 對於使用引用的值而不做修改的函式 如果資料物件很小,如內建資料型別或者小型結構,則按照值傳遞 如果資料物件是陣列,則使用指標 唯一的選擇 並且指標宣告為指向cons...
什麼時候使用 Lambda 函式?
python 中定義函式有兩種方法,一種是用常規方式 def 定義,函式要指定名字,第二種是用 lambda 定義,不需要指定名字,稱為 lambda 函式。lambda 函式又稱匿名函式,匿名函式就是沒有名字的函式,函式沒有名字也行?當然可以啦。有些函式如果只是臨時一用,而且它的業務邏輯也很簡單時...