日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

export function函数传参_从底层看前端(七)—— JavaScript到底有多少种函数?

發布時間:2023/12/10 javascript 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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到底有多少种函数?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。