javascript
export function函数传参_从底层看前端(七)—— JavaScript到底有多少种函数?
在上篇文章中我們了解到了執行上下文是什么,也知道了任何語句的執行都會依賴特定的上下文。
一旦上下文被切換,整個語句的效果可能都會發生變化。那么,切換上下文的時機就顯得非常重要。
在JavaScript中,切換上下文最主要的場景就是函數調用。在這篇文章中,我們就來講講函數調用切換上下文的事情。
在我們講函數調用之前,我們先來認識一下函數家族。
函數
在ES2018中,函數已經是一個很復雜的體系了,我在這里整理了一下。
第一種,普通函數,用function關鍵字定義的函數。示例:
function foo(){//code } 第二種,箭頭函數:用 => 運算符定義的函數。示例:
const foo = ()=>{//code } 第三種,在class中定義的函數。示例:
class C {foo(){//code} } 第四種,生成器函數:用 function* 定義的函數。示例:
function* foo(){//code } 第五種:類。用class定義的類,實際上也是函數。
示例:
class Foo {constructor(){//code} } 第六,七,八種,異步函數:普通函數,箭頭函數和生成器函數前加上async關鍵字。示例:
async function foo(){// code } const foo = async () => {// code } async function foo*(){// code }ES6以來,大量加入的新語法極大地方便了我們編程的同時,也增加了很多我們理解的心智負擔。要想認識這些函數的執行上下文切換,我們必須要對他們行為上的區別有所了解。
對普通變量而言,這些函數并沒有本質區別,都是遵循了'繼承定義時的環境'的規則,它們的一個行為差異在于this關鍵字。
那么,this 關鍵字是什么呢?
this 關鍵字行為
this 是JavaScript中的一個關鍵字,它的使用方法類似一個變量(但是this跟變量有很多不同)。
this是執行上下文中很重要的一個組成部分。同一個函數調用方式不同,得到的this值也不同,我們看一個例子:
function showThis(){console.info(this) }var o = {showThis: showThis }showThis(); //global o.showThis(); //o在這個例子中,我們定義了函數showThis,我們把它賦值給了一個對象o的屬性,然后嘗試分別使用兩個引用來調用同一個函數,結果得到了不同的this值。
普通函數的 this 值由'調用它所使用的的引用'來決定,其中奧秘就在于:我們獲取函數的表達式,它實際上返回的并非函數本身,而是一個 Reference 類型(其中標準類型之一)。
Reference 類型由兩部分組成,一個對象和一個屬性值。不難理解 o.showThis 產生的Reference類型,即由對象 o 和屬性'showThis'構成。
當做一些算術運算時,Reference類型會被解引用,即獲取真正的值來參與運算,而類似函數調用,delete等操作,都需要用到 Reference 類型中的對象。
在這個例子中,Reference類型中的對象被當做this值,傳入了執行函數的上下文中。
至此,我們對this的解釋已經非常清晰了,調用函數時使用的引用,決定了函數執行時刻的this值。
實際上從運行時的角度來看,this跟面向對象毫無關聯,它是與函數調用的表達式相關。
這個設計來自JavaScript早年,通過這樣的方式,巧妙地模擬了Java的語法,但是仍然保留了純粹的'無類'運行時設施。
如果,我們把這個例子稍作修改,換成箭頭函數,結果就不一樣了。
const showThis = () => {console.log(this); }var o = {showThis: showThis }showThis(); // global o.showThis(); // global我們看到,改為箭頭函數后,無論用什么來調用它,都不影響它的this值。
接下來我們看看'方法',它的行為又不一樣了:
class C {showThis() {console.log(this);} } var o = new C(); var showThis = o.showThis;showThis(); // undefined o.showThis(); // o這里我們創建了一個類C,并且實例化出對象o,再把 o 的方法賦值給了變量 showThis。
這時候,我們使用 showThis 這個引用去調用方法時,得到了undefined。
所以,在方法中,我們看到this的行為也不大一樣,它得到了undefined的結果。
按照我們上面的方法,不難驗證出:生成器函數,異步函數和異步普通函數跟普通函數行為是一致的,異步箭頭函數與箭頭函數的行為是一致的。
this關鍵字機制
說完了this行為,我們再來簡單談談在JavaScript內部,實現this這些行為的機制。
函數能夠引用定義時的變量,如上文分析,函數能記住定義時的this,因此,函數內部必須有一個機制來保存這些信息。
在JavaScript標準中,為函數規定了用來保存定義是上下文的私有屬性[[Environment]]。
當一個函數執行時,會創建一條新的執行環境記錄,記錄的外層詞法環境(outer lexical environment)會被設置成函數的[[Environment]]。
這個動作就是切換上下文了,我們假設有這樣的代碼:
var a = 1; foo();在別處定義了foo:
var b = 2; function foo(){console.log(b); // 2console.log(a); // error }這里的foo能夠訪問 b (定義時的詞法環境),卻不能訪問 a (執行時的詞法環境),這就是執行上下文的切換機制了。
JavaScript用一個棧來管理執行上下文,這個棧的每一項又包含一個鏈表。如下圖所示:
當函數調用時,會入棧一個新的執行上下文,函數調用結束時,執行上下文被出棧。
而this則是一個更為復雜的機制,JavaScript標準定義了[[thisMode]]私有屬性。
[[thisMode]]私有屬性有三個取值
lexical:表示從上下文中找this,這對應了箭頭函數。global:表示this為undefined時,取全局對象,對應了普通函數。
strict:當嚴格模式時使用,this嚴格按照調用時傳入的值,可能為null或者undefined。
非常有意思的是,方法的行為跟普通函數有差異,恰恰是因為class設計成了默認按照strict模式執行。
我們可以用strict達成與上一節中方法的例子中一樣的效果。
"use strict" function showThis(){console.log(this); }var o = {showThis: showThis }showThis(); // undefined o.showThis(); // o函數創建新的執行上下文中詞法環境記錄時,會根據[[thisMode]]來標記新紀錄的[[ThisBindingStatus]]私有屬性。
代碼執行遇到this時,會組成檢查當前詞法環境記錄中的[[ThisBindingStatus]],當我們找到有this的環境記錄時獲取this的值。
這樣的規則的實際效果時,嵌套的箭頭函數中的代碼都指向外層this,例如:
var o = {} o.foo = function foo(){console.log(this);return () => {console.log(this);return () => console.log(this);} }o.foo()()(); // o, o, o在上面的例子中,我們定義了三層嵌套的函數,最外層的是普通函數,兩層都是箭頭函數。
這里調用三個函數,獲得的this值是一樣的,對象都是o。
JavaScript還提供了一系列函數的內置方法來操作this值,下面我們來了解一下。
操作this的內置函數
Function.prototype.call和Function.prototype.apply可以執行函數調用時傳入的this值,示例如下:
function foo(a, b, c){console.log(this);console.log(a, b, c); } foo.call({}, 1, 2, 3); foo.apply({}, [1, 2, 3]);這里call和apply作用是一樣的,只是傳參方式有區別。
此外,還有Function.prototype.bind 它可以生成一個綁定的函數,這個函數的this值固定了參數。
function foo(a, b, c){console.log(this);console.log(a, b, c); } foo.bind({}, 1, 2, 3)();有趣的是,call,bind和apply用于不接受this的函數類型如箭頭,class都不會報錯。
這時候,它們無法實現改變this的能力,但是可以實現傳參。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的export function函数传参_从底层看前端(七)—— JavaScript到底有多少种函数?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 西南科技大学OJ题 平衡二叉树的判
- 下一篇: javascript --- [虚拟D