国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

談談Javascript中的delete操作符

antz / 1907人閱讀

摘要:你覺得下列代碼中,哪些操作能成功人肉判斷一下,不要放進瀏覽器里執行。故對于解析而言,得到的為上述所有的屬性在下均為。那么又有什么玄機呢的操作可理解為對于,調用其內部的方法。幾乎所有的都是不可刪除的。

你覺得下列代碼中,哪些delete操作能成功?人肉判斷一下,不要放進瀏覽器里執行。

// #1
a = "hello world";
delete a;

// #2
var b = "hello world";
delete b;

// #3
x = {};
Object.defineProperties(x, {
  "p1": {
    value: "hello",
    configurable: true
  },
  "p2": {
    value: "world",
    configurable: false
  }
});

console.log(delete x.p1);
console.log(delete x.p2);

// #4
function f() {
    console.log("hello f");
}
delete f;

// #5
with({t:"try"}) {
    console.log(t);
    delete t;
    console.log(t); // print what?
}

// #6
try {
  throw "hello";
} catch (e) {
  console.log(e);
  delete e;
  console.log(e);// print what?
}

// #7
function test(a, b, c) {
  delete a;
  console.log(a);

  delete arguments;
  console.log(arguments);
}
test(1,2,3);

// #8
eval("var v = "ttt"");
delete v;
console.log(v);

// #9
y = {a:"bye"};
function f() {
    return y.a;
}
delete f();

如果上述代碼都在strict模式下執行呢,又有哪些操作是成功的呢?如果不清楚的話,往下看。

  

PS:本文所表述的內容均由規范演譯而來,而非通過實驗進行推理,代碼示例僅用來證明文中闡述內容的正確性,如有疑惑歡迎留言討論。

delete究竟在做啥?

參見EMCA 262-5 第11.4.1小節:

  

The production UnaryExpression : delete UnaryExpression is evaluated as follows:

Let ref be the result of evaluating UnaryExpression.

If Type(ref) is not Reference, return true.

If IsUnresolvableReference(ref) then,

If IsStrictReference(ref) is true, throw a SyntaxError exception.

Else, return true.

If IsPropertyReference(ref) is true, then

Return the result of calling the [[Delete]] internal method on
ToObject(GetBase(ref)) providing GetReferencedName(ref) and
IsStrictReference(ref) as the arguments.

Else, ref is a Reference to an Environment Record binding, so

If IsStrictReference(ref) is true, throw a SyntaxError exception.

Let bindings be GetBase(ref).

Return the result of calling the DeleteBinding concrete method of bindings, providing GetReferencedName(ref) as the argument.

要讀懂上面這一堆術語諸如Type(ref), Reference, GetBase(ref),DeleteBinding似乎還是需要花費點力氣的,沒關系,慢慢來。

什么是Reference?

在ECMA規范中,Reference是一個抽象的概念,由三個部分組成,可以理解為:

{
  base: undefined | Object | Boolean | String | Number | environment record, //這個表示Reference的基
  refName: string,       //在ECMA中,常以Reference Name表示
  isStrict: boolean       //是否是一個strict的reference,如果在strict mode下執行的話,對所有的Reference這個值都是true。而在普通mode下,則需要分情況討論了
}

什么時候會創建一個Reference呢?有兩種情況:
- 解析變量(GetIdentifierReference )
- 訪問對象屬性(Property Accessors)

對于如下代碼(在全局作用域下):

var jake= "string";
delete jake;

在delete表達式中,對jake變量的解析便可得到這樣的一個Reference:

{
  base: GLOBAL, //base是全局對象,在瀏覽器環境下就是window對象
  refName: "jake", //Reference Name就是字符串jake
  isStrict: false
}

而對于如下代碼:

var man = {
  name: "delta",
  age: 24
};

console.log(man.age);

console.log(man.age)語句中,對man.age的解析便可得到如下的Reference

{
 base: man,
 refName: "age",
 isStrict: false
}

So Easy,那什么情況下會有IsUnresolvableReference(ref)true的情況呢?當且僅當一個Reference的Base值為undefined時,才會有IsUnresolvableReference(ref)為true。

delete abcd; 

在解析abcd變量時,會查找當前環境記錄(Environment Record)是否有一個叫abcd這樣的綁定(Binding),如果有,則當前環境記錄則為Base值,否則再從當前詞法環境(Lexical Environment)的父環境(parent Lexical Environment)的環境記錄中查找,直到undefined。故對于解析abcd而言,得到的*Reference`為:

{
    base: undefined,
    refName: "abcd",
    isStrict: false
}

上述所有Reference的isStrict屬性在strict mode下均為true

回到delete的定義,可以看到:

  

If Type(ref) is not Reference, return true.
If IsUnresolvableReference(ref) then,

If IsStrictReference(ref) is true, throw a SyntaxError exception.

Else, return true.

這就很好理解了,可以得出如下結論(在普通mode下):

delete abcdefg; //不會報錯,而且還返回true
delete "abcde"; //"abcde"是一個值,不是Reference,返回true
Property Reference

什么時候會有IsPropertyReference(ref)為true呢?這很好理解,僅當一個Reference的Base值為一個Object或一個JS原生類型如string, boolean, number時,它才會為true.

回到delete的定義:

  

If IsPropertyReference(ref) is true, then

+ Return the result of calling the [[Delete]] internal method on ToObject(GetBase(ref)) providing GetReferencedName(ref) and IsStrictReference(ref) as the arguments.

因此有:

a = {};
delete a.p; //結果是true
delete "hello".p //結果也是true

y = {a:"bye"};
function f() {
    return y.a;
}
delete f(); //結果是true,因為f()的結果是一個值,不是Reference

重點在于[[Delete]]這個內部方法,如果一個屬性的Configurable為false,那么:

在普通mode下,屬性不會被刪除,返回true

在strict mode下,拋出Type Error異常

如果一個屬性的Configurable為true的話,那么delete操作就能成功去除相應的屬性。

繼續

回到delete的定義,最后一段:

  

Else, ref is a Reference to an Environment Record binding, so

If IsStrictReference(ref) is true, throw a SyntaxError exception.

Let bindings be GetBase(ref).

Return the result of calling the DeleteBinding concrete method of
bindings, providing GetReferencedName(ref) as the argument.

如果一個reference是一個Environment Record binding的話,但Environment Record是什么?而Environment Record binding又是什么?

這要從執行上下文(Execution Context)說起。

Execution Context

對于一個特定的執行上下文,它有如下構成:

{
  LexicalEnvironment: {},
  VariableEnvironment: {},
  ThisBinding: {} 
}

ThisBinding很好理解,就是一個特定執行上下文的this值。而LexicalEnvironment和VariableEnvironment又是什么?這兩個都是Lexical Environment,(摔,術語越來越多了)。

一個Lexical Environment由兩部分組成:

{
  EnvironmentRecord: {}, //一個Environment Record
  OuterLexicalEnvironment: outer //指向它外層的詞法環境
}

那環境記錄(Environment Record)是什么呢?

Environment Record

Environment Record分為兩種,一種是Object Environment Record,另一種是Declarative Environment Record。 從概念上來講,這兩者區別不大,它們都實現了相同的接口。唯一區別就是Object Environment Record是一個用戶可訪問到的Javascript Object。而Declarative Environment Record無法在JS代碼中訪問到。一個Environment Record上會有一系列的綁定(binding),如果把Environment Record當做一個對象的話,那么它上面的綁定(binding)就可以認為是它的屬性了。

//對于一個函數
function hello(b, c) {
    var a = 10;
}
hello();//執行它會進入一個新的Execution Context
//它有一個Environment Record
er =  {
  a: undefined,
  b: undefined,
  c: undefined,
  arguments: `List of args`
}

//它有一個Lexical Environment
le = {
  EnvironmentRecord: er,
  OuterLexicalEnvironment: GLOBAL
}

//而它的Execution Context為:
EC = {
  LexicalEnvironment: le,
  VariableEnvironment: le, //VariableEnvironment和LexicalEnvironment指向同一個Lexical Environment
  ThisBinding: GLOBAL
}

其實對于任意Execution Context(簡稱EC),99%的情況你都可以認為它的LexicalEnvironment和VariableEnvironment都指向同一個Lexical Environment。但為什么還區分出這兩個呢?

對于一個EC的VariableEnvironment,一量創建它的指向不會改變,永遠是指向同一個Lexical Environment

對于一個LexicalEnvironment,可能會根據代碼的控制流改變,如進入了with代碼塊里或是catch代碼塊里,進入withcatch后,會創建新的LexicalEnvironment(簡稱LE),然后將當前的LE當做新的LE的parent,最后將EC.LexicalEnvironment指向新的LE

一旦了解了Execution Context, Lexical Environment, Environment Record這些概念,回到delete定義:

  

Let bindings be GetBase(ref).

Return the result of calling the DeleteBinding concrete method of
bindings, providing GetReferencedName(ref) as the argument.

通過GetBase(ref)取得它的Environment Record,然后調用相應的DeleteBinding的內部方法來刪除binding。那么DeleteBinding又有什么玄機呢?

DeleteBinding

DeleteBinding的操作可理解為:

對于Object Environment Record,調用其內部的[[Delete]]方法。

對于Declarative Environment Record,當且僅且在創建這個Binding時指定了它是可刪除的,才可以從當前Record中刪掉這個binding

首先看簡單的Object Environment Record情況:

a = "ttt";
delete a;
console.log(a); //報錯,因為GLOBAL是一個Object Environment Record(簡稱OER),而a屬性是可刪除的

var t = {a:"ccc"}
with(t) {
  delete a;
  console.log(a); //報錯,因為當前的Environment Record是一個指向t的OER,而其a屬性是可刪除的
}

對于其它情況,我們就需要充分理解Create Binding細節了,我總結了一下。

幾乎所有的binding都是不可刪除的。函數的參數,變量聲明,函數聲明,catch變量,arguments均不可刪除

例外是eval環境下的變量聲明和函數聲明是可刪除的

詳細的可參見:

10.5 Declaration Binding Instantiation 變量聲明綁定

12.14 The try Statement try..catch時的變量綁定

故有:

var a = "cccc";
delete a; //沒用的

function s(){
}
delete s; //沒用

function f(a,b){
  //均沒用
  delete a;
  delete b;
  delete f;
  delete arguments;
}

try {
  throw "hello";
} catch(e) {
  delete e; //沒用
}

eval("var a = "ccc"; delete a; console.log(a)");//能刪掉,最后的console.log會報錯
總結

對于對象屬性而言,delete a.b,取決于configurable屬性。

Object Environment Record 上的binding也取決于其configurable屬性,然而一個OER的binding的創建方式有兩種,一種是用戶代碼自己賦上去,如a = 123,另一種是引擎采用CreateBinding來創建,如在全局作用域下的var x = 123,就會在GLOBAL對象上創建一個configurable為false的binding

對于Environment Record而言,取決于CreateBinding時是否指定了這個Binding是一個可刪除了,除了eval中的變量聲明和函數聲明是可刪除的外,其它所有binding均不可刪除

- 完 -

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78099.html

相關文章

  • 談談Javascript中的void作符

    摘要:由于表達式偏啰嗦,于是最近便開始采用來減輕負擔。規范是這么說的在規范,有如下描述搬譯一下操作符產生式按如下流程解釋令為解釋執行的結果。而如果把換成操作符寫成,她的就不會減了,因為操作符不會對求值。 由于JS表達式偏啰嗦,于是最近便開始采用Coffeescript來減輕負擔。舉個栗子,當我想取屋子里的第一條dog時,首先要判斷house對象是否存在,然后再判斷house.dogs是否存在...

    Barrior 評論0 收藏0
  • 談談JavaScript中嚴格模式你應該遵守的那些事

    嚴格模式 首先來了解一下嚴格模式是什么?嚴格模式是JavaScript中的一種限制性更強的變種方式,不是一個子集:它在語義上與正常代碼有明顯的差異,不支持嚴格模式的瀏覽器與支持嚴格模式的瀏覽器行為上也不一樣,所以不要在未經嚴格模式特性測試情況下使用嚴格模式,嚴格模式可以與非嚴格模式共存,所以腳本可以逐漸的選擇性加入嚴格模式 嚴格模式的目的 首先,嚴格模式會將JavaScript陷阱直接變成明顯的錯...

    MingjunYang 評論0 收藏0
  • 談談瀏覽器里的JavaScript

    摘要:而與最大的區別在于與瀏覽器溝通的窗口,不涉及網頁內容。完全依賴于瀏覽器廠商實作本身無標準規范,而有著所制定的標準來規范。而透過選取出來的節點,我們可以通過操作屬性來變更它的文字。在許多的網頁前端教學或是文章書籍當中,你可能常常聽到這樣的說法:「HTML、CSS 與JavaScript 是網頁前端三大要素」,其中: HTML 負責資料與結構 CSS 負責樣式與呈現 JavaScript 負責...

    CastlePeaK 評論0 收藏0
  • 關于JavaScript對象,你所不知道的事(一)- 先談對象

    摘要:對象與屬性讓我們保持耐心,再梳理一下對象與屬性的關系對象是屬性的集合,當對象的屬性是函數時,我們將其稱之為方法。 這篇博文的主要目的是為了填坑,很久之前我發表了一篇名為關于JavaScript對象中的一切(一) — 對象屬性的文章,想要談一談JavaScript對象,可那時只是貼了一張關于這個主題的思維導圖,今天我會針對這一主題進行展開,將JavaScript對象一些平常不太常用的知識...

    mykurisu 評論0 收藏0
  • 前端面試:談談 JS 垃圾回收機制

    摘要:例如本地函數的局部變量和參數當前嵌套調用鏈上的其他函數的變量和參數全局變量還有一些其他的,內部的這些值稱為根。例如,如果局部變量中有對象,并且該對象具有引用另一個對象的屬性,則該對象被視為可達性,它引用的那些也是可以訪問的,詳細的例子如下。 最近看到一些面試的回顧,不少有被面試官問到談談JS 垃圾回收機制,說實話,面試官會問這個問題,說明他最近看到一些關于 JS 垃圾回收機制的相關的文...

    孫淑建 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<