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

資訊專欄INFORMATION COLUMN

理解引用

curlyCheng / 1768人閱讀

摘要:我會(huì)解釋里面神秘的引用,一旦你理解了引用,你就會(huì)明白通過(guò)引用來(lái)了解的綁定是多么輕松,你也會(huì)發(fā)現(xiàn)讀的規(guī)范容易得多了。二理論把引用定義成??纯催\(yùn)算符的說(shuō)法這也就是為什么我們對(duì)一個(gè)無(wú)法解析的引用使用操作符的時(shí)候并不會(huì)報(bào)錯(cuò)。

Know thy reference

(原文:know thy reference - kangax)

一、前言

翻譯好不是件容易的事兒,我盡量講得通順,一些術(shù)語(yǔ)會(huì)保留原詞,翻出一篇拗口的文章實(shí)在是得不償失。

二、用大量的抽象來(lái)解釋"this"

之前的一個(gè)星期天的早上,我躺床上看HackerNews,有一篇叫「This in JavaScript」的文章,我稍微掃了兩眼。不出意外,就是函數(shù)調(diào)用、方法調(diào)用、顯式綁定、構(gòu)造函數(shù)實(shí)例化這檔子事。這篇文章特別長(zhǎng),我越看就越覺(jué)得,這一大堆的解釋和例子會(huì)給一個(gè)不了解this機(jī)制的人帶來(lái)多大的心理陰影啊。

我想起來(lái)幾年前我第一次看DC的「JavaScript The Good Parts」,當(dāng)時(shí)看完書(shū)里的相關(guān)總結(jié)之后覺(jué)得無(wú)比清晰,書(shū)里簡(jiǎn)要地列出了這幾條:

The this parameter is very important in object oriented programming, and its value is determined by the invocation pattern.There are four patterns of invocation in JavaScript:the method invocation pattern,the function invocation pattern,the constructor invocation pattern and the apply invocation pattern.The patterns differ in how the bonus parameter this is initialized.

只由調(diào)用方式?jīng)Q定,而且只有四種情況??纯?,這說(shuō)得多簡(jiǎn)單。

于是我去評(píng)論里看有沒(méi)有人說(shuō) HackerNews 的這篇文章講得太復(fù)雜了。果然,很多人都搬出了「JavaScript The Good Parts」里的總結(jié),其中一個(gè)人提煉了一下:

The keyword this refers to whatever is left of the dot at call-time.

If there"s nothing to the left of the dot,then this refers to the root scope(e.g. Window)

A few functions change the behavior of this - bind,call and apply

The keyword new binds this to the object just created

簡(jiǎn)直精辟。但是我注意到里面的一句話-"whatever is left of the dot at call-time"。乍一看很有道理嘛,比方說(shuō)foo.bar(),this指向foo;又比方說(shuō)foo.bar.baz(),this指向foo.bar。但是(f = foo.bar)()呢?在這里所謂的「Whatever is left of the dot at call-time」就是foo,那this就指向foo咯?

為了拯救前端程序員于水火之中,我留言說(shuō),所謂的「句號(hào)左邊的東西」可能沒(méi)這么簡(jiǎn)單,要真的理解this,你可能需要理解引用和它的base values。

也是這一次經(jīng)歷我才真的意識(shí)到引用的概念其實(shí)很少被提到。我去搜了一下"JavaScript reference",結(jié)果出來(lái)一些關(guān)于"pass-by-reference vs. pass-by-value"的討論。不行,我得出來(lái)救場(chǎng)了。

這就是為什么我要來(lái)寫(xiě)這篇博客。

我會(huì)解釋ECMAScript里面神秘的引用,一旦你理解了引用,你就會(huì)明白通過(guò)引用來(lái)了解this的綁定是多么輕松,你也會(huì)發(fā)現(xiàn)讀ECMAScript的規(guī)范容易得多了。

一、關(guān)于引用

老實(shí)說(shuō),看到關(guān)于引用的討論那么少我也多多少少可以理解,畢竟這也并不是語(yǔ)言本身的一部分。引用只是一種機(jī)制,用來(lái)描述ECMAScript里的特定行為。它對(duì)于解釋引擎的實(shí)現(xiàn)至關(guān)重要,但是它們?cè)诖a里是看不見(jiàn)摸不著的。

當(dāng)然,理解它對(duì)于寫(xiě)代碼完完全全是必要的。

回到我們之前的問(wèn)題:

foo.bar()
(f = foo.bar)()

到底為什么第一個(gè)的this指向foo,而第二個(gè)指向全局對(duì)象呢?

你可能會(huì)說(shuō),“括號(hào)左邊的表達(dá)式里面完成了一次對(duì) f 的賦值,賦值完了之后就相當(dāng)于調(diào)用 f() ,這樣的話就是一次函數(shù)調(diào)用,而不是方法調(diào)用了?!?/em>

好的,那這個(gè)呢:

(1, foo.bar)()

“噢,這是個(gè)圓括號(hào)運(yùn)算符嘛!它完成從左邊到右邊的求值,所以它肯定和 foo.bar() 是一樣的,所以它的this指向foo?!?/em>

var foo = {
  bar: function() {
    "use strict"
    return this
  }
}
(1, foo.bar)() //undefined

“呃......真是奇怪啊”

那這個(gè)呢:

(foo.bar)()

“呃,考慮到上一個(gè)例子,肯定也是undefined吧,應(yīng)該是圓括號(hào)搞了什么鬼?!?/em>

(foo.bar)()  //{bar: function(){ ... }}

“好吧......我服了?!?/em>

二、理論

ECMAScript把引用定義成「resolved name binding」。這是由三個(gè)部分組成的抽象實(shí)體 - base, namestrict flag,第三個(gè)好懂,現(xiàn)在咱們聊前兩個(gè)就夠了。

創(chuàng)建引用有兩種情況:

Identifier resolution

property access

比方說(shuō)吧,foo創(chuàng)建了一個(gè)引用,foo.bar也創(chuàng)建了一個(gè)引用。而像1, "foo", /x/, { }, [ 1,2,3 ]這些字面量值和函數(shù)表達(dá)式(function(){})就沒(méi)有。

Example Reference? Notes
"foo" NO
123 NO
/x/ NO
({}) NO
(function(){}) NO
foo YES Could be unresolved reference if foo is not defined
foo.bar YES Property reference
(123).toString YES Property reference
(function(){}).toString YES Property reference
(1, foo.bar)() NO Already evaluated, BUT see grouping operator exception
(f = foo.bar)() NO Already evaluated, BUT see grouping operator exception
(foo) YES Grouping operator does not evaluate reference
(foo.bar) YES Grouping operator does not evaluate reference

先別管后面四個(gè),我們待會(huì)再看。

每次一個(gè)引用創(chuàng)建的時(shí)候,它的組成部分base,name,strict都被賦上值。name就是解析的標(biāo)識(shí)符或者屬性名,base就是屬性對(duì)象或者環(huán)境對(duì)象。

可能把引用理解成一個(gè)沒(méi)有原型的JavaScript對(duì)象會(huì)比較好,它就只有base, namestrict三個(gè)屬性。下面舉兩個(gè)例子:

//when foo is defined earlier

foo

var Reference = {
  base: Environment,
  name: "foo",
  strict: false
}
----------------
foo.bar

//這就是所謂的「Property Reference」
var Reference = {
  base: foo,
  name: "bar",
  strict: false
} 

還有第三種情況,即不可解析引用。如果在作用域里找不到標(biāo)識(shí)符,引用的base就會(huì)設(shè)為undefined:

//when foo is not defined

foo

var Reference = {
  base: undefined,
  name: "foo",
  strict: false
}

你肯定見(jiàn)過(guò),解析不了的引用可能會(huì)導(dǎo)致引用錯(cuò)誤-("foo is not defined").

本質(zhì)上來(lái)說(shuō),引用就是一種代表名稱綁定的簡(jiǎn)單機(jī)制,它把對(duì)象的屬性解析和變量解析抽象出一個(gè)類似對(duì)象的數(shù)據(jù)結(jié)構(gòu):

var reference = {
  base: Object or Environment,
  name: name
}

現(xiàn)在我們知道ECMAScript底層做了什么了,但是這對(duì)解釋this的指向有什么用呢?

三、函數(shù)調(diào)用

看看函數(shù)調(diào)用的時(shí)候發(fā)生了什么:

Let ref be the result of evaluating MemberExpression.

Let func be GetValue(ref).

Let argList be the result of evaluating Arguments, producing an internal list of argument values (see 11.2.4).

If Type(func) is not Object, throw a TypeError exception.

If IsCallable(func) is false, throw a TypeError exception.

If Type(ref) is Reference, then

If IsPropertyReference(ref) is true, then

Let thisValue be GetBase(ref).

Else, the base of ref is an Environment Record

Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).

Else, Type(ref) is not Reference.

Let thisValue be undefined.

Return the result of calling the [[Call]] internal method on func, providing thisValue as the this value and providing the list argList as the argument values.

加粗的第六步基本上就解釋了DC四條里面的1、2兩條:

//foo.bar()
- `foo.bar`是個(gè)屬性引用嗎?
- 是的
- 那取它的base,也就是`foo`作為`this`吧

//foo()
- `foo`是個(gè)屬性引用嗎?
- 不是
- 那你的base就是undefined

//(function(){})()
- 什么?你連引用都不是啊,那不用看了,undefined
四、賦值,逗號(hào),圓括號(hào)運(yùn)算符

有了前面的了解,我們看看能不能解釋得了下面這幾個(gè)函數(shù)調(diào)用的this指向。

(f = foo.bar)()

(1, foo.bar)()

(foo.bar)()

從第一個(gè)賦值運(yùn)算說(shuō)起,括號(hào)里是一個(gè)簡(jiǎn)單賦值操作,如果我們看看簡(jiǎn)單賦值做了些什么的話,我們可能可以看出點(diǎn)端倪:

Let lref be the result of evaluating LeftHandSideExpression.

Let rref be the result of evaluating AssignmentExpression.

Let rval be GetValue(rref).

Throw a SyntaxError exception if the following conditions are all true:

Type(lref) is Reference is true

IsStrictReference(lref) is true

Type(GetBase(lref)) is Environment Record

GetReferencedName(lref) is either "eval" or "arguments"

Call PutValue(lref, rval).

Return rval.

注意到右邊的表達(dá)式在賦值之前通過(guò)內(nèi)部的GetValue()求值。在我們的例子里面,foo.bar引用被轉(zhuǎn)換成了一個(gè)函數(shù)對(duì)象,那么以非引用方式調(diào)用函數(shù)的話,this就指向了undefined。所以深入剖析的話,比起來(lái)foo.bar(),(f = foo.bar)()其實(shí)更像是(function(){})()。就是說(shuō),它是個(gè)求過(guò)值的表達(dá)式,而不是一個(gè)擁有base的引用。

第二個(gè)逗號(hào)運(yùn)算就類似了:

Let lref be the result of evaluating Expression.

Call GetValue(lref).

Let rref be the result of evaluating AssignmentExpression.

Return GetValue(rref).

通過(guò)了GetValue,引用轉(zhuǎn)換成了函數(shù)對(duì)象,this指向了undefined.

最后是圓括號(hào)運(yùn)算符:

Return the result of evaluating Expression. This may be of type Reference.

NOTE This algorithm does not apply GetValue to the result of evaluating Expression. The principal motivation for this is so that operators such as delete and typeof may be applied to parenthesised expressions.

那很明白了,圓括號(hào)運(yùn)算符沒(méi)有對(duì)引用進(jìn)行轉(zhuǎn)換,這也就是為什么它的this指向了foo.

五、typeof運(yùn)算符

既然都聊到這兒了,干脆聊一聊別的??纯磘ypeof運(yùn)算符的說(shuō)法:

Let val be the result of evaluating UnaryExpression.

If Type(val) is Reference, then

If IsUnresolvableReference(val) is true, return "undefined".

Let val be GetValue(val).

Return a String determined by Type(val) according to Table 20.

這也就是為什么我們對(duì)一個(gè)無(wú)法解析的引用使用typeof操作符的時(shí)候并不會(huì)報(bào)錯(cuò)。但是如果不用typeof運(yùn)算符,直接做一個(gè)聲明呢:

Expression Statement:

Let exprRef be the result of evaluating Expression.

Return (normal, GetValue(exprRef), empty).

GetValue():

If Type(V) is not Reference, return V.

Let base be the result of calling GetBase(V).

If IsUnresolvableReference(V), throw a ReferenceError exception.

看到了吧,過(guò)不了GetValue這一關(guān),所以說(shuō)出現(xiàn)了沒(méi)法解析的聲明直接就報(bào)錯(cuò)了。

六、delete運(yùn)算符

長(zhǎng)話短說(shuō):

如果不是個(gè)引用,返回true(delete 1,delete /x/)

如果是沒(méi)法解析的引用(delete iDontExist)

嚴(yán)格模式,報(bào)錯(cuò)

否則返回true

如果確實(shí)是個(gè)屬性引用,那就刪了它,返回true

如果是全局對(duì)象作為base的屬性

嚴(yán)格模式,報(bào)錯(cuò)

否則,刪除,返回true

三、后記

這篇文章都是基于ES5的,ES2015可能會(huì)有些變化。

另外,結(jié)果我還是翻出來(lái)一篇拗口的文章,Oops!

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

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

相關(guān)文章

  • 理解Javascritp中的"引用"

    摘要:二邏輯判斷中的引用通常我們會(huì)把理解為值相同,把理解為值相同且類型相同。但是這種理解不是完全準(zhǔn)確的。只能確保定義的變量的引用地址不會(huì)被改變。 Author: bugall Wechat: bugallF Email: 769088641@qq.com Github: https://github.com/bugall 一: 函數(shù)中的引用傳遞 我們看下下面的代碼的...

    用戶83 評(píng)論0 收藏0
  • 幾分鐘理解 Jdk - Reference

    摘要:一個(gè)對(duì)象是否有虛引用的存在,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例。為一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個(gè)對(duì)象被垃圾收集器回收時(shí)收到一個(gè)系統(tǒng)通知。在之后提供了類來(lái)實(shí)現(xiàn)虛引用參考深入理解虛擬機(jī) GC $TODO$ 一個(gè)對(duì)象的生命周期 一個(gè)對(duì)象的生命周期從它被創(chuàng)建開(kāi)始,此時(shí)虛擬機(jī)會(huì)給它置一個(gè)內(nèi)部標(biāo)識(shí)finalizable,當(dāng) GC 到達(dá)某一個(gè)安全點(diǎn)并檢驗(yàn)該...

    JohnLui 評(píng)論0 收藏0
  • PHP中引用傳遞+unset+global理解,希望大神指正

    摘要:即產(chǎn)生了相當(dāng)于這樣的效果,所以改變的值也同時(shí)改變了的值。不要用返回引用來(lái)增加性能,引擎足夠聰明來(lái)自己進(jìn)行優(yōu)化。只能從函數(shù)返回引用變量沒(méi)別的方法。 關(guān)鍵是對(duì)global的誤解,之前以為在函數(shù)中g(shù)lobal變量,就是把函數(shù)外部的變量拿進(jìn)函數(shù)內(nèi)部使用,但似乎我錯(cuò)了引用傳遞+unset+global理解 php的引用(就是在變量、函數(shù)、對(duì)象等前面加上&符號(hào))在PHP中引用的意思是:不同的名字訪...

    ConardLi 評(píng)論0 收藏0
  • 理解-PHP引用

    摘要:引用本身概念好理解性能也很好但是用好它還是存在著一定的門(mén)檻不太好寫(xiě)。寫(xiě)本文的起因是這幾天碰到非常好的一個(gè)解決方案,讓我重新理解了引用。如果下面的代碼,你看完就能理解了,說(shuō)明你引用真是學(xué)到家了你也可以直接跳過(guò)本文哈。 起因: 日常開(kāi)發(fā)中,我們會(huì)碰到構(gòu)造樹(shù)的需求,通過(guò)id,pid的關(guān)系去構(gòu)建一個(gè)樹(shù)結(jié)構(gòu),然后對(duì)樹(shù)進(jìn)行遍歷等操作。其實(shí)現(xiàn)方式分為兩種: 1. 遞歸, 2. 引用而這兩個(gè)方法的優(yōu)缺...

    ermaoL 評(píng)論0 收藏0
  • 前端基礎(chǔ)進(jìn)階(一):內(nèi)存空間詳細(xì)圖解

    摘要:一棧數(shù)據(jù)結(jié)構(gòu)與不同,中并沒(méi)有嚴(yán)格意義上區(qū)分棧內(nèi)存與堆內(nèi)存。引用數(shù)據(jù)類型的值是保存在堆內(nèi)存中的對(duì)象。不允許直接訪問(wèn)堆內(nèi)存中的位置,因此我們不能直接操作對(duì)象的堆內(nèi)存空間。為了更好的搞懂變量對(duì)象與堆內(nèi)存,我們可以結(jié)合以下例子與圖解進(jìn)行理解。 showImg(https://segmentfault.com/img/remote/1460000009784102?w=1240&h=683); ...

    _Suqin 評(píng)論0 收藏0
  • 深入理解js對(duì)象的引用

    JavaScript 有七種內(nèi)置類型,其中: 基本類型 ? 空值(null) ? 未定義(undefined) ? 布爾值( boolean) ? 數(shù)字(number) ? 字符串(string) ? 符號(hào)(symbol,ES6 中新增) 引用類型 ? 對(duì)象(object) 對(duì)于基本類型,賦值(=)是值的拷貝,比較(===)的是實(shí)際的值,而對(duì)于引用類型(Array也是一種Object),賦值(=)...

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

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

0條評(píng)論

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