亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

深入理解JavaScirpt的函數(shù)調(diào)用和"this"

bladefury / 1956人閱讀

摘要:簡(jiǎn)單的函數(shù)調(diào)用顯而易見,一直用調(diào)用函數(shù)將會(huì)非常煩人。規(guī)范說(shuō)幾乎總是被傳遞,但不在嚴(yán)格模式下時(shí)被調(diào)用函數(shù)應(yīng)該將其更改為全局對(duì)象。實(shí)際上,規(guī)范有一個(gè)和都使用的原語(yǔ)內(nèi)部稱為。

過(guò)去很多年里,我看到過(guò)太多關(guān)于JavaScript函數(shù)調(diào)用的混淆。尤其是,很多人抱怨函數(shù)調(diào)用中this的語(yǔ)義令人困惑。
在我看來(lái),通過(guò)理解核心函數(shù)調(diào)用原語(yǔ),然后將其他所有調(diào)用函數(shù)的方法視為在原語(yǔ)之上的語(yǔ)法糖,如此便可澄清很多這類疑惑。事實(shí)上,這正是ECMAScript規(guī)范對(duì)此的看法。在某些方面,這篇文章是規(guī)范的簡(jiǎn)化,但基本思路是一樣的。

核心原語(yǔ)

首先,我們先看一下函數(shù)調(diào)用的核心原語(yǔ),F(xiàn)unction對(duì)象的call方法[1]。調(diào)用方法方法相對(duì)簡(jiǎn)單。

從參數(shù)1到末尾創(chuàng)建一個(gè)參數(shù)列表(argList)

第一個(gè)參數(shù)(參數(shù)0)是thisValue

通過(guò)將this的值設(shè)為thisValueargList作為其參數(shù)列表調(diào)用函數(shù)

舉例:

function hello(thing) {
  console.log(this + " says hello " + thing);
}

hello.call("Yehuda", "world") //=> Yehuda says hello world

如你所見,我們通過(guò)將this設(shè)置為“Yehuda”和單個(gè)參數(shù)“world”來(lái)調(diào)用hello方法。這正是JavaScript中函數(shù)調(diào)用的核心原語(yǔ)。你可以認(rèn)為所有其他方式的函數(shù)調(diào)用都可”去糖“得到這個(gè)原語(yǔ)。(“去糖”是指采用一種方便的語(yǔ)法并用更基本的核心原語(yǔ)來(lái)描述它)。

[1]在ES5規(guī)范中,call方法是用另一個(gè)更底層的原語(yǔ)來(lái)描述的,但它是在那個(gè)原語(yǔ)之上的簡(jiǎn)單封裝,所以我在這里簡(jiǎn)化了一下。有關(guān)更多信息,請(qǐng)參閱本文末尾。

簡(jiǎn)單的函數(shù)調(diào)用

顯而易見,一直用call調(diào)用函數(shù)將會(huì)非常煩人。JavaScript允許我們直接使用括號(hào)語(yǔ)法hello("world")來(lái)調(diào)用函數(shù)。當(dāng)我們這樣做時(shí),調(diào)用“去糖”如下:

function hello(thing) {
  console.log("Hello " + thing);
}

// this:
hello("world")

// desugars to:
hello.call(window, "world");

僅在使用嚴(yán)格模式[2]的ECMAScript 5中,此行為將改變:

// this:
hello("world")

// desugars to:
hello.call(undefined, "world");

簡(jiǎn)短版本的說(shuō)法是:fn(...args)這樣的函數(shù)調(diào)用和fn.call(window [ES5-strict: undefined], ...args)是一模一樣的。
注意,對(duì)于行內(nèi)聲明的函數(shù)(function() {})()也是成立的:(function() {})()(function() {}).call(window [ES5-strict: undefined)是一模一樣的。

[2]事實(shí)上,我撒了一點(diǎn)小謊。ECMAScript 5規(guī)范說(shuō)undefined(幾乎)總是被傳遞,但不在嚴(yán)格模式下時(shí)被調(diào)用函數(shù)應(yīng)該將其thisValue更改為全局對(duì)象。這允許嚴(yán)格模式下調(diào)用者避免破壞現(xiàn)有的非嚴(yán)格模式庫(kù)。

成員函數(shù)

調(diào)用方法的下一個(gè)非常普遍的方式是作為一個(gè)對(duì)象的一個(gè)成員 (person.hello())。在這種情況下,調(diào)用“去糖”如下:

var person = {
  name: "Brendan Eich",
  hello: function(thing) {
    console.log(this + " says hello " + thing);
  }
}

// this:
person.hello("world")

// desugars to this:
person.hello.call(person, "world");

注意,hello方法在這種形式下是如何附加到對(duì)象上是無(wú)關(guān)緊要的。請(qǐng)記住,我們之前將hello定義為一個(gè)獨(dú)立函數(shù)。接下來(lái)我們看看如果動(dòng)態(tài)地將其附加到對(duì)象上會(huì)發(fā)生什么:

function hello(thing) {
  console.log(this + " says hello " + thing);
}

person = { name: "Brendan Eich" }
person.hello = hello;

person.hello("world") // still desugars to person.hello.call(person, "world")

hello("world") // "[object DOMWindow]world"

注意,函數(shù)對(duì)其this值沒(méi)有一貫的定義,它總是在調(diào)用時(shí)根據(jù)調(diào)用者調(diào)用的方式進(jìn)行設(shè)置。

使用Function.prototype.bind

因?yàn)橐?b>this值一貫不變的函數(shù)有時(shí)是很方便的,人們歷來(lái)使用一個(gè)簡(jiǎn)單的閉包技巧將函數(shù)轉(zhuǎn)換為this值一貫不變的對(duì)應(yīng)函數(shù):

var person = {
  name: "Brendan Eich",
  hello: function(thing) {
    console.log(this.name + " says hello " + thing);
  }
}

var boundHello = function(thing) { return person.hello.call(person, thing); }

boundHello("world");

盡管我們的boundHello調(diào)用仍然“去糖”為boundHello.call(window, "world"),但我們改變方向并使用我們的原語(yǔ)call方法將this值更改回我們想要的值。
我們做些調(diào)整可以把這個(gè)技巧變?yōu)橥ㄓ媒夥ǎ?/p>

var bind = function(func, thisValue) {
  return function() {
    return func.apply(thisValue, arguments);
  }
}

var boundHello = bind(person.hello, person);
boundHello("world") // "Brendan Eich says hello world"

為了理解這一點(diǎn),您只需要兩個(gè)額外的知識(shí)。首先,arguments是一個(gè)類Array對(duì)象,它表示傳遞給函數(shù)的所有參數(shù)。其次,apply方法的工作原理和call原語(yǔ)除了它采用類Array對(duì)象而不是一次列出一個(gè)參數(shù)之外完全一樣。
我們的bind方法簡(jiǎn)單地返回一個(gè)新函數(shù)。當(dāng)它被調(diào)用時(shí),我們的新函數(shù)只是調(diào)用傳入的原始函數(shù),并將原始值設(shè)置為其this值,當(dāng)然它也傳遞參數(shù)。
因?yàn)檫@是一個(gè)有點(diǎn)常見的習(xí)慣用法,ES5在所有Function對(duì)象上引入了一個(gè)新方法bind,實(shí)現(xiàn)了此行為:

var boundHello = person.hello.bind(person);
boundHello("world") // "Brendan Eich says hello world"

當(dāng)您需要將原始函數(shù)作為回調(diào)傳遞時(shí),此方法將非常有用:

var person = {
  name: "Alex Russell",
  hello: function() { console.log(this.name + " says hello world"); }
}

$("#some-div").click(person.hello.bind(person));

// when the div is clicked, "Alex Russell says hello world" is printed

確實(shí),這有點(diǎn)笨,TC39(負(fù)責(zé)ECMAScript下一版本的委員會(huì))將繼續(xù)致力于一個(gè)更優(yōu)雅、向后兼容的解決方案。

面向jQuery

由于jQuery中大量使用匿名回調(diào)函數(shù),因此它在內(nèi)部使用call方法將這些回調(diào)的this值設(shè)置為更有用的值。舉個(gè)例子,在所有事件處理程序中(如不進(jìn)行特殊干預(yù)),jQuery不接收window作為其this值,而是通過(guò)把設(shè)置事件處理程序的元素作為它第一個(gè)參數(shù)在回調(diào)函數(shù)上調(diào)用call。
這非常有用,因?yàn)槟涿卣{(diào)函數(shù)中的默認(rèn)this的值并不是特別有用,除了它給初學(xué)者對(duì)javascript的一種印象,this通常是一個(gè)奇怪的,經(jīng)常變動(dòng)至于難以解釋的概念。
如果你理解了將“含糖”函數(shù)調(diào)用轉(zhuǎn)換為“已去糖”的func.call(thisValue, ...args)的基本規(guī)則,那么你應(yīng)該能夠在并不是那么危險(xiǎn)的JavaScriptthis水域中航行。

PS:我撒謊的部分

在個(gè)別地方,我從規(guī)范的確切措辭中略微簡(jiǎn)化了事實(shí)??赡茏顕?yán)重的欺騙是我稱呼func.call為原語(yǔ)的說(shuō)法。實(shí)際上,規(guī)范有一個(gè)func.call[obj.]func()都使用的原語(yǔ)(內(nèi)部稱為[[Call]])。
然而,還是看一下func.call的定義吧:

如果IsCallable(func)值為false,則拋出TypeError異常

argList為一個(gè)空的List

如果使用多個(gè)參數(shù)調(diào)用此方法,則從arg1開始,從左往右將每個(gè)參數(shù)追加為argList的最后一個(gè)元素

提供thisArg作為this的值,并將argList作為參數(shù)列表,返回調(diào)用func的內(nèi)部方法[[Call]]的結(jié)果

如你所見,此定義本質(zhì)上是一種很簡(jiǎn)單的JavaScript語(yǔ)義綁定到原語(yǔ)[[Call]]操作。
如果你看一下調(diào)用函數(shù)的定義,前七個(gè)步驟設(shè)置thisValueargList,最后一步是:“提供thisArg作為this的值,并將列表argList作為參數(shù)值,返回調(diào)用func的內(nèi)部方法[[Call]]的結(jié)果?!?
一旦確定了argListthisValue,它基本上是相同的措辭。
我在稱call是一個(gè)原語(yǔ)時(shí)作了一些欺騙,但其含義基本上與我在文章開頭提出的規(guī)范和引用的章節(jié)是一樣的。
還有一些我沒(méi)有在這里介紹的其他案例(最值得注意的是with)。

原文地址

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/96465.html

相關(guān)文章

  • 學(xué)習(xí)js中'this'關(guān)鍵字

      在JavaScript中‘this’關(guān)鍵字是一個(gè)非常重要的概念,我們雖然知道它重要,但它也十分的晦澀難懂,也給我們學(xué)習(xí)造成不小的困擾?! ∈裁词?#39;this'關(guān)鍵字  'this'關(guān)鍵字是為每個(gè)執(zhí)行上下文(每個(gè)函數(shù))創(chuàng)建的一個(gè)特殊變量;所以一般來(lái)說(shuō),在使用'this'關(guān)鍵字的函數(shù)中,'this'永遠(yuǎn)是取其所有者的值。總結(jié)一句話是該函...

    3403771864 評(píng)論0 收藏0
  • 一道JS面試題所引發(fā)"血案",透過(guò)現(xiàn)象尋本質(zhì),再?gòu)谋举|(zhì)看現(xiàn)象

    摘要:一看這二逼就是周杰倫的死忠粉看看控制臺(tái)輸出,確實(shí)沒(méi)錯(cuò)就是對(duì)象。從根本上來(lái)說(shuō),作用域是基于函數(shù)的,而執(zhí)行環(huán)境是基于對(duì)象的例如全局執(zhí)行環(huán)境即全局對(duì)象。全局對(duì)象全局屬性和函數(shù)可用于所有內(nèi)建的對(duì)象。全局對(duì)象只是一個(gè)對(duì)象,而不是類。 覺得本人寫的不算很爛的話,可以登錄關(guān)注一下我的GitHub博客,博客會(huì)堅(jiān)持寫下去。 今天同學(xué)去面試,做了兩道面試題,全部做錯(cuò)了,發(fā)過(guò)來(lái)給我看,我一眼就看出來(lái)了,因?yàn)?..

    QiShare 評(píng)論0 收藏0
  • ahooks useRequest源碼深入解讀

      大家會(huì)發(fā)現(xiàn),自從 React v16.8 推出了 Hooks API,前端框架圈并開啟了新的邏輯復(fù)用的時(shí)代,從此無(wú)需在意 HOC 的無(wú)限套娃導(dǎo)致性能差的問(wèn)題,同時(shí)也解決了 mixin 的可閱讀性差的問(wèn)題。這里也有對(duì)于 React 最大的變化是函數(shù)式組件可以有自己的狀態(tài),扁平化的邏輯組織方式,更加友好地支持 TS 類型聲明。  在運(yùn)用Hooks的時(shí)候,除了 React 官方提供的,同時(shí)也支持我們...

    3403771864 評(píng)論0 收藏0
  • 深入理解Redis 數(shù)據(jù)結(jié)構(gòu)—簡(jiǎn)單動(dòng)態(tài)字符串sds

    摘要:本文主要介紹的數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)單動(dòng)態(tài)字符串簡(jiǎn)稱。遵守字符串以空字符串結(jié)尾的慣例,保存的空字符串一個(gè)字節(jié)空間不計(jì)算在的屬性里面。添加空字符串到字符串末尾等操作,都是由函數(shù)自動(dòng)完成的,所以這個(gè)空字符對(duì)于使用者來(lái)說(shuō)完全是透明的。Redis是用ANSI C語(yǔ)言編寫的,它是一個(gè)高性能的key-value數(shù)據(jù)庫(kù),它可以作用在數(shù)據(jù)庫(kù)、緩存和消息中間件。其中 Redis 鍵值對(duì)中的鍵都是 string 類型,而鍵...

    番茄西紅柿 評(píng)論0 收藏2637
  • 【譯】使用"BinaryAST"加快JavaScript腳本解析速度?

    摘要:是提出并積極開發(fā)的一種新的在線格式,旨在加快解析速度,同時(shí)保持原始的語(yǔ)義不變。它的實(shí)現(xiàn)方式是使用有效的二進(jìn)制來(lái)表示代碼和數(shù)據(jù)結(jié)構(gòu),并且存儲(chǔ)和提供額外的信息來(lái)提前指導(dǎo)解析器工作。提升依賴于提升所有聲明變量函數(shù)類。 原文:Faster script loading with BinaryAST?本文首發(fā)于公眾號(hào):符合預(yù)期的CoyPan JavaScirpt的冷啟動(dòng) web應(yīng)用的表現(xiàn),越來(lái)...

    Hujiawei 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<