成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Functions

sydMobile / 2692人閱讀

摘要:如果我們把這樣的定義放在全局代碼中,解析器會把函數(shù)當作聲明,因為它以關(guān)鍵字開頭,在第一種情況中,我們會得到,因為我們?nèi)鄙俸瘮?shù)名。

原文

ECMA-262-3 in detail. Chapter 5. Functions.

簡介

在這篇文章中,我們將討論一個ESCMAScript對象,函數(shù)。我們將討論不同類型的函數(shù),每個類型是如何影響環(huán)境中的變量對象(variables object)以及內(nèi)部的作用域的。我們將回答以下經(jīng)常會出現(xiàn)的問題,“下面的函數(shù)有什么不同嗎?”[譯者注:當進入一個函數(shù)時,會創(chuàng)建一個對象,里面保存了函數(shù)運行需要的各種變量]

var foo = function () {
  ...
};
function foo() {
  ...
}
(function () {
  ...
})();
函數(shù)類型 函數(shù)聲明

函數(shù)聲明(Funcation Declaration),縮寫FD,是一個函數(shù):

必須擁有一個名字

在源碼中的位置,要么在程序級別,要么在其他函數(shù)體的體內(nèi)

在進入上下文時創(chuàng)建

會影響變量對象

用以下方式聲明

function exampleFunc() {
  ...
}

這個類型的函數(shù)的主要特點是它影響變量對象(它們被存在環(huán)境的變量對象中)。這導致了第二個重要的點,在代碼執(zhí)行期間,他們已經(jīng)被用了,因為當一進入環(huán)境后,函數(shù)聲明就被保存在環(huán)境的變量對象中,在代碼開始執(zhí)行之前。例如函數(shù)可以被調(diào)用在它被聲明之前,從源碼的角度看過去

foo();
  
function foo() {
  console.log("foo");
}
// function can be declared:
// 1) directly in the global context
function globalFD() {
  // 2) or inside the body
  // of another function
  function innerFD() {}
}

函數(shù)聲明要么出現(xiàn)在全局環(huán)境中,要么出現(xiàn)在其他函數(shù)體內(nèi)。

函數(shù)表達式

函數(shù)表達式(Function Expression),縮寫FE,是一個函數(shù)

在源碼中可以在表達式位置被定義

可以選擇不要名字

對環(huán)境的變量對象沒有影響

在代碼執(zhí)行的階段被創(chuàng)建

常見的賦值表達式

var foo = function () {
  ...
};

也可以給函數(shù)個名字

var foo = function _foo() {
  ...
};

在這值得注意的是,在函數(shù)表達式的外面通過變量foo訪問函數(shù),而在函數(shù)內(nèi)部,例如遞歸調(diào)用,用_foo訪問。函數(shù)表達式總在表達式的位置,例如以下的例子都是函數(shù)表達式

// in parentheses (grouping operator) can be only an expression
(function foo() {});
 
// in the array initialiser – also only expressions
[function bar() {}];
 
// comma also operates with expressions 
1, function baz() {}; 

函數(shù)表達式在代碼執(zhí)行的階段才會創(chuàng)建,并不會被存在環(huán)境變量對象中

// FE is not available neither before the definition
// (because it is created at code execution phase),
  
console.log(foo); // "foo" is not defined
  
(function foo() {});
  
// nor after, because it is not in the VO
  
console.log(foo);  // "foo" is not defined

為什么需要函數(shù)表達式?是為了不污染環(huán)境變量對象,同時作為其他函數(shù)的參數(shù)。

function foo(callback) {
  callback();
}
  
foo(function bar() {
  console.log("foo.bar");
});
  
foo(function baz() {
  console.log("foo.baz");
});

函數(shù)表達式被賦值給了個變量,我們可以通過該變量來訪問它

var foo = function () {
  console.log("foo");
};
  
foo();

還可以創(chuàng)建封閉的作用域,對外隱藏內(nèi)部數(shù)據(jù)。

var foo = {};
  
(function initialize() {
  
  var x = 10;
  
  foo.bar = function () {
    console.log(x);
  };
  
})();
  
foo.bar(); // 10;
  
console.log(x); // "x" is not defined

我們可以看到foo.bar通過[[Scope]]可以訪問函數(shù)initialize內(nèi)部變量x,與此同時,x不能被外界直接訪問。這個策略經(jīng)常被用來創(chuàng)建私有變量和隱藏輔助實體。通常initialize函數(shù)表達式的名字是被忽略的。

(function () {
  
  // initializing scope
  
})();

這有個函數(shù)表達式,根據(jù)運行情況來創(chuàng)建,且不污染環(huán)境變量對象。

var foo = 10;
  
var bar = (foo % 2 == 0
  ? function () { console.log(0); }
  : function () { console.log(1); }
);
  
bar(); // 0

注意,ES5中有bind函數(shù),鎖定this的值。

var boundFn = function () {
  return this.x;
}.bind({x: 10});
 
boundFn(); // 10
boundFn.call({x: 20}); // still 10

這通常在事件監(jiān)聽,或延遲函數(shù)(setTimeout)中被使用。

括號問題

根據(jù)規(guī)范,表達式聲明(expression statement),不能以{開始,這會被當作塊,也不能以關(guān)鍵字function開始,會被當作函數(shù)聲明。所以,如果我們想定義一個函數(shù),用下面的方式(以function關(guān)鍵字開頭)立馬調(diào)用。

function () {
  ...
}();
 
// or even with a name
 
function foo() {
  ...
}();

我們處理函數(shù)聲明,同時會產(chǎn)生解析錯誤。如果我們把這樣的定義放在全局代碼中,解析器會把函數(shù)當作聲明,因為它以function關(guān)鍵字開頭,在第一種情況中,我們會得到SyntaxError,因為我們?nèi)鄙俸瘮?shù)名。在第二個情況中,我們確實有了函數(shù)名,函數(shù)聲明應該被正常的創(chuàng)建。但是我們有另一個語法錯誤,一個組操作符中沒有表達式。所以在這個場合下,括號只是函數(shù)聲明后面的組操作符,而不是函數(shù)調(diào)用。

// "foo" is a function declaration
// and is created on entering the context
 
console.log(foo); // function
 
function foo(x) {
  console.log(x);
}(1); // and this is just a grouping operator, not a call!
 
foo(10); // and this is already a call, 10
// function declaration
function foo(x) {
  console.log(x);
}
 
// a grouping operator
// with the expression
(1);
 
// another grouping operator with
// another (function) expression
(function () {});
 
// also - the expression inside
("foo");
 
// etc

如果我們在語句中有函數(shù)聲明,也會報錯

if (true) function foo() {console.log(1)}

我們?nèi)绾蝿?chuàng)建一個函數(shù)立刻調(diào)用它?它應該是個函數(shù)表達式,創(chuàng)建函數(shù)表達式最簡單的操作就是組操作符。如此,一個函數(shù)會在執(zhí)行時創(chuàng)建,調(diào)用,移除,如果沒有引用指向它。

(function foo(x) {
  console.log(x);
})(1); // OK, it"s a call, not a grouping operator, 1

注意在下面的例子中,括號已經(jīng)不需要了,因為函數(shù)已經(jīng)在表達式的位置了,解析器知道把它當作函數(shù)表達式,在執(zhí)行的時候被創(chuàng)建。

var foo = {
  
  bar: function (x) {
    return x % 2 != 0 ? "yes" : "no";
  }(1)
  
};
  
console.log(foo.bar); // "yes"

正如我們所見,foo.bar是一個字符串而不是函數(shù),函數(shù)在初始化屬性時就被調(diào)用了。括號是需要的,如果我們想立刻調(diào)用函數(shù)在創(chuàng)建它后,而函數(shù)并不在表達式的位置,如果函數(shù)已經(jīng)在表達式的位置,括號就不需要了。除了括號,還有其他轉(zhuǎn)換函數(shù)表達式的方法

1, function () {
  console.log("anonymous function is called");
}();
 
// or this one
!function () {
  console.log("ECMAScript");
}();
 
// and any other manual
// transformation
(function () {})();
(function () {}());
實現(xiàn)擴展: Function Statement
if (true) {
  
  function foo() {
    console.log(0);
  }
  
} else {
  
  function foo() {
    console.log(1);
  }
  
}
  
foo(); // 1 or 0 ? test in different implementations

這里要說的是,根據(jù)規(guī)范,這種語法構(gòu)造是不正確的。因為函數(shù)聲明不能出現(xiàn)在代碼塊中,而這里有if/else的代碼塊,函數(shù)聲明只能出現(xiàn)在程序級別(program level)或其他函數(shù)體內(nèi)。然而在規(guī)范的錯誤處理中,允許了這種實現(xiàn)擴展。但是有各自不同的實現(xiàn)方式。if/else是希望我們能根據(jù)運行時的情況,來創(chuàng)建函數(shù),這暗示了把它們當作函數(shù)表達式,實際上,主要的實現(xiàn)中,在進入上下文時,就會創(chuàng)建函數(shù)聲明,因為它們同名,所以最后一個會被調(diào)用,所以打印1。然后spidermonkey(一種js引擎)以不同的方式實現(xiàn)這個。

有名字的函數(shù)表達式的特點 (NFE)

如果函數(shù)表達式有名字(named function expression),縮寫NFE。由定義可知,函數(shù)表達式并不影響環(huán)境的變量對象。然后,函數(shù)表達式有時候需要在遞歸中調(diào)用自己。

(function foo(bar) {
  
  if (bar) {
    return;
  }
  
  foo(true); // "foo" name is available
  
})();
  
// but from the outside, correctly, is not
  
foo(); // "foo" is not defined

當解釋器在代碼執(zhí)行的過程中遇到有名字的函數(shù)表達式,在創(chuàng)建函數(shù)表達式之前,解釋器創(chuàng)建了一個輔助特殊的對象,把它加在當前的作用域鏈前面。然后它創(chuàng)建函數(shù)表達式,函數(shù)獲得[[Scope]]屬性。在那之后,有名字的函數(shù)表達式被作為一個屬性,添加到了特殊的對象上,對象的值是函數(shù)表達式的引用。最后一步是從父作用域鏈中移除特殊的對象。

specialObject = {};
  
Scope = specialObject + Scope;
  
foo = new FunctionExpression;
foo.[[Scope]] = Scope;
specialObject.foo = foo; // {DontDelete}, {ReadOnly}
  
delete Scope[0]; // remove specialObject from the front of scope chain
NFE and SpiderMonkey

[譯者注:講述SpiderMonkey(Mozilla火狐c/c++)引擎如何處理有名函數(shù)表達式,不譯了]
[譯者注:講述Rhino(Mozilla java)引擎如何處理有名函數(shù)表達式,不譯了]

NFE and JScript

[譯者注:講述JScript(微軟)引擎如何處理有名函數(shù)表達式,不譯了]

通過函數(shù)構(gòu)造器創(chuàng)建函數(shù)

由函數(shù)構(gòu)造器構(gòu)造的函數(shù),它的[[Scope]]只包括全局對象。

var x = 10;
  
function foo() {
  
  var x = 20;
  var y = 30;
  
  var bar = new Function("console.log(x); console.log(y);");
  
  bar(); // 10, "y" is not defined
  
}
函數(shù)創(chuàng)建算法
F = new NativeObject();
  
// property [[Class]] is "Function"
F.[[Class]] = "Function"
  
// a prototype of a function object
F.[[Prototype]] = Function.prototype
  
// reference to function itself
// [[Call]] is activated by call expression F()
// and creates a new execution context
F.[[Call]] = 
  
// built in general constructor of objects
// [[Construct]] is activated via "new" keyword
// and it is the one who allocates memory for new
// objects; then it calls F.[[Call]]
// to initialize created objects passing as
// "this" value newly created object 
F.[[Construct]] = internalConstructor
  
// scope chain of the current context
// i.e. context which creates function F
F.[[Scope]] = activeContext.Scope
// if this functions is created 
// via new Function(...), then
F.[[Scope]] = globalContext.Scope
  
// number of formal parameters
F.length = countParameters
  
// a prototype of created by F objects
__objectPrototype = new Object();
__objectPrototype.constructor = F // {DontEnum}, is not enumerable in loops
F.prototype = __objectPrototype
  
return F

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

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/89625.html

相關(guān)文章

  • Javascript anonymous functions

    Javascript anonymous functions Anonymous functions are functions that are dynamically declared at runtime. They’re called anonymous functions because they aren’t given a name in the same way as no...

    劉福 評論0 收藏0
  • Study Blazor .NET(四)數(shù)據(jù)綁定

    摘要:下面是支持的事件參數(shù)方法方法這里是組件間雙向綁定的另一種方法,我們可以基于任何事件方法的執(zhí)行手動觸發(fā)一個,在中,用內(nèi)置函數(shù)手動觸發(fā)屬性,同時回調(diào)父組件中相應的方法,在父組件中,通過通知狀態(tài)已經(jīng)改變。翻譯自:Study Blazor .NET,轉(zhuǎn)載請注明。數(shù)據(jù)綁定單向綁定在blazor中單向綁定簡單而直接,無需UI刷新或渲染。下面示例展示了單向數(shù)據(jù)綁定://Counter.razor@page...

    incredible 評論0 收藏0
  • Flask注冊視圖函數(shù)

    摘要:鍵是函數(shù)名,值是函數(shù)對象,函數(shù)名也用于生成。注冊一個視圖函數(shù),用裝飾器。獲取儲存視圖函數(shù)字典中的函數(shù)對象視圖函數(shù)類中的字典儲存了注冊的視圖函數(shù)名和視圖函數(shù)對象。輸出視圖函數(shù)視圖函數(shù)名重復修改解決 那天莫名其妙出了個錯。。就順便看了看Flask路由 在flask存儲路由函數(shù)是以函數(shù)名為鍵,函數(shù)對象為值 class Flask: def __init__(self, *args, ...

    2bdenny 評論0 收藏0
  • 【underscore 源碼解讀】Array Functions 相關(guān)源碼拾遺 & 小結(jié)

    摘要:最近開始看源碼,并將源碼解讀放在了我的計劃中。將轉(zhuǎn)為數(shù)組同時去掉第一個元素之后便可以調(diào)用方法總結(jié)數(shù)組的擴展方法就解讀到這里了,相關(guān)源碼可以參考這部分。放個預告,下一篇會暫緩下,講下相關(guān)的東西,敬請期待。 Why underscore 最近開始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 2016 計劃中。 閱讀一些著名框架類庫的源碼,就好...

    SimpleTriangle 評論0 收藏0

發(fā)表評論

0條評論

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