var functionName = function(){}與function functionName(){}

var functionName = function() {} vs function functionName() {}
投票:6622回答:38

我最近開始維護別人的JavaScript代碼。 我正在修復錯誤,添加功能,還試圖整理代碼並使其更加一致。

以前的開發人員使用兩種方法來聲明函數,如果背後沒有原因,我將無法解決。

兩種方式是:

var functionOne = function() {
    // Some code
};
function functionTwo() {
    // Some code
}

使用這兩種不同方法的原因是什麼,每種方法的利弊是什麼? 有什麼方法可以用另一種方​​法不能完成的?

tags:javascript,function,syntax,idioms
38回答
18
投票

@EugeneLazutkin給出了一個示例,其中他命名了一個分配的函數,以便能夠將shortcut()用作自身的內部引用。 John Resig給出了另一個示例-在他的Learning Advanced Javascript教程中復制分配給另一個對象的遞歸函數 雖然在這里分配功能並不是嚴格的問題,但我還是建議您積極嘗試使用本教程-單擊右上角的按鈕運行代碼,然後雙擊該代碼進行編輯。

教程中的示例: yell()遞歸調用:

刪除原始忍者對像後,測試將失敗。 (第13頁)

var ninja = { 
  yell: function(n){ 
    return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 

var samurai = { yell: ninja.yell }; 
var ninja = null; 

try { 
  samurai.yell(4); 
} catch(e){ 
  assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); 
}

如果您命名將被遞歸調用的函數,則測試將通過。 (第14頁)

var ninja = { 
  yell: function yell(n){ 
    return n > 0 ? yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); 

var samurai = { yell: ninja.yell }; 
var ninja = {}; 
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );

16
投票

其他答案中未提及的另一個區別是,如果您使用匿名函數

var functionOne = function() {
    // Some code
};

並將其用作構造函數,如

var one = new functionOne();

那麼將不會定義one.constructor.name Function.name是非標準的,但受Firefox,Chrome,其他Webkit衍生的瀏覽器和IE 9+支持。

function functionTwo() {
    // Some code
}
two = new functionTwo();

可以使用two.constructor.name作為字符串檢索構造函數的名稱。


24
投票

我在代碼中使用可變方法的原因非常特殊,上面已以抽象的方式介紹了該方法的理論,但是一個示例可能會幫助一些像我這樣的人,而他們的JavaScript專業知識有限。

我有需要與160個獨立設計的品牌一起運行的代碼。 大多數代碼位於共享文件中,而與品牌有關的內容位於單獨的文件中,每個品牌一個。

有些品牌需要特定的功能,而有些則不需要。 有時,我必須添加新功能來進行特定於品牌的事情。 我很樂意更改共享編碼,但是我不想更改所有160套品牌文件。

通過使用變量語法,我可以在共享代碼中聲明變量(本質上是一個函數指針),然後分配一個簡單的存根函數,或者設置為null。

然後,需要該功能的特定實現的一個或兩個品牌可以定義其功能版本,然後根據需要將其分配給變量,其餘的則不執行任何操作。 我可以在共享代碼中執行null函數之前對其進行測試。

從上面的評論中,我認為也許也可以重新定義靜態函數,但是我認為變量解決方案很好而且很明確。


14
投票

第一個(函數doSomething(x))應該​​是對象表示法的一部分。

第二個var doSomething = function(x){ alert(x);}var doSomething = function(x){ alert(x);} )只是創建一個匿名函數並將其分配給變量doSomething 因此doSomething()將調用該函數。

您可能想知道什麼是函數聲明函數表達式

函數聲明定義了一個命名函數變量,而無需分配變量。 函數聲明作為獨立的構造出現,並且不能嵌套在非函數塊中。

function foo() {
    return 3;
}

ECMA 5(13.0)將語法定義為
函數標識符(FormalParameterList opt ){FunctionBody}

在上述條件下,函數名稱在其作用域和其父作用域的作用域內可見(否則它將不可訪問)。

並在函數表達式中

函數表達式將函數定義為較大的表達式語法(通常是變量賦值)的一部分。 通過函數表達式定義的函數可以命名或匿名。 函數表達式不應以“函數”開頭。

// Anonymous function expression
var a = function() {
    return 3;
}

// Named function expression
var a = function foo() {
    return 3;
}

// Self-invoking function expression
(function foo() {
    alert("hello!");
})();

ECMA 5(13.0)將語法定義為
函數標識符opt (FormalParameterList opt ){FunctionBody}


32
投票

建立綁定後,分配給變量的函數聲明和函數表達式的行為相同。

但是,在功能對象實際與其變量相關聯的方式時間方面存在差異。 這種差異是由於JavaScript中稱為變量提升的機制引起的。

基本上,所有函數聲明和變量聲明都被提升到聲明所在函數的頂部(這就是我們說JavaScript具有函數作用域的原因 )。

  • 吊起函數聲明時,函數主體將“跟隨”,因此在評估函數主體時,變量將立即綁定到函數對象。

  • 當一個變量聲明懸掛,初始化遵循,但“留守”。 變量在函數主體的開頭被初始化為undefined ,並將在代碼的原始位置被分配一個值。 (實際上,將在每個聲明具有相同名稱的變量的每個位置分配一個值。)

提升的順序也很重要:函數聲明優先於具有相同名稱的變量聲明,而最後一個函數聲明優先於具有相同名稱的先前函數聲明。

一些例子...

var foo = 1;
function bar() {
  if (!foo) {
    var foo = 10 }
  return foo; }
bar() // 10

變量foo提升到函數的頂部,初始化為undefined ,因此!footrue ,因此foo分配為10 bar範圍之外的foo ,並且未被更改。

function f() {
  return a; 
  function a() {return 1}; 
  var a = 4;
  function a() {return 2}}
f()() // 2

function f() {
  return a;
  var a = 4;
  function a() {return 1};
  function a() {return 2}}
f()() // 2

函數聲明優先於變量聲明,最後一個函數聲明為“ sticks”。

function f() {
  var a = 4;
  function a() {return 1}; 
  function a() {return 2}; 
  return a; }
f() // 4

在此示例中, a通過評估第二個函數聲明得到的函數對像初始化a ,然後將其賦值為4

var a = 1;
function b() {
  a = 10;
  return;
  function a() {}}
b();
a // 1

這里首先懸掛函數聲明,聲明並初始化變量a 接下來,將此變量分配給10 換句話說:分配沒有分配給外部變量a


75
投票

當您需要避免覆蓋函數的先前定義時,最好使用第一種方法而不是第二種方法。

if (condition){
    function myfunction(){
        // Some code
    }
}

,此myfunction定義將覆蓋任何先前的定義,因為它將在解析時完成。

if (condition){
    var myfunction = function (){
        // Some code
    }
}

僅在滿足condition時才能正確定義myfunction


14
投票

如果使用這些函數創建對象,則會得到:

var objectOne = new functionOne();
console.log(objectOne.__proto__); // prints "Object {}" because constructor is an anonymous function

var objectTwo = new functionTwo();
console.log(objectTwo.__proto__); // prints "functionTwo {}" because constructor is a named function

28
投票

就代碼維護成本而言,更可取的是命名函數:

  • 與聲明它們的位置無關(但仍受範圍限制)。
  • 更能抵抗諸如條件初始化之類的錯誤(如果需要,您仍然可以覆蓋)。
  • 通過與作用域功能分開分配局部功能,代碼變得更具可讀性。 通常在範圍內,功能優先,然後是局部功能的聲明。
  • 在調試器中,您將清楚地在調用堆棧上看到函數名稱,而不是“匿名/求值”函數。

我懷疑後面還會有更多針對命名函數的PROS。 命名函數的優點被列為匿名函數的缺點。

從歷史上看,匿名函數是由於JavaScript無法作為一種語言列出具有命名函數的成員而出現的:

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}

606
投票

這是創建函數的標準表單的摘要:( 本來是為另一個問題而寫的,但是在移入規範問題後進行了修改。)

條款:

快速清單:

  • 功能聲明

  • “匿名” function表達式(儘管使用了術語,但有時會創建帶有名稱的函數)

  • 命名function表達式

  • 訪問器功能初始化器(ES5 +)

  • 箭頭函數表達式(ES2015 +) (與匿名函數表達式一樣,它不涉及顯式名稱,但可以使用名稱創建函數)

  • 對像初始化器中的方法聲明(ES2015 +)

  • 構造和方法聲明在class (ES2015 +)

功能聲明

第一種形式是函數聲明 ,如下所示:

function x() {
    console.log('x');
}

函數聲明是一個聲明 ; 它不是語句或表達式。 因此,您不要在它後面加上; (儘管這樣做是無害的)。

當執行進入任何分步代碼之前 ,執行進入其出現的上下文時,將處理該函數聲明。 它創建的函數被賦予適當的名稱(在上面的示例中為x ),並且該名稱被放置在聲明出現的範圍內。

由於它是在相同上下文中的任何分步代碼之前進行處理的,因此您可以執行以下操作:

x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}

在ES2015之前,規範未涵蓋將功能聲明放入tryifswitchwhile等控件結構中時JavaScript引擎應該做什麼,如下所示:

if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}

而且由於它們是運行分步代碼之前進行處理的,所以要知道它們在控制結構中時該怎麼做是很棘手的。

儘管直到2015年才指定執行此操作,但這是允許的擴展,以支持塊中的函數聲明。 不幸的是(不可避免),不同的引擎做了不同的事情。

從ES2015開始,該規範說明了怎麼做。 實際上,它提供了三個獨立的操作:

  1. 如果鬆散模式不能在Web瀏覽器,JavaScript引擎是應該做的一件事
  2. 如果在網絡瀏覽器上處於鬆散模式,則JavaScript引擎應該做其他事情
  3. 如果處於嚴格模式(是否使用瀏覽器),則JavaScript引擎應該做另一件事

鬆散模式的規則很棘手,但是在嚴格模式下,塊中的函數聲明很容易:它們在塊中是本地的(它們具有塊作用域 ,這在ES2015中也是新功能),並且被提升到頂部的塊。 所以:

"use strict";
if (someCondition) {
    foo();               // Works just fine
    function foo() {
    }
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
                         // because it's not in the same block)

“匿名” function表達式

第二種常見形式稱為匿名函數表達式

var y = function () {
    console.log('y');
};

與所有表達式一樣,在逐步執行代碼時會對其進行評估。

在ES5中,此函數創建的函數沒有名稱(匿名)。 在ES2015中,如果可能,可以通過從上下文推斷功能來為其分配名稱。 在上面的示例中,名稱為y 當函數是屬性初始值設定項的值時,將執行類似的操作。 (有關何時發生這種情況的細節和規則,搜索SetFunctionName規範 -它似乎所有的地方。)

命名function表達式

第三種形式是命名函數表達式 (“ NFE”):

var z = function w() {
    console.log('zw')
};

所創建的函數具有專有名稱(在這種情況下為w )。 與所有表達式一樣,在逐步執行代碼時會對其進行評估。 函數名稱不會添加到表達式出現的範圍內; 名稱在函數內部範圍:

var z = function w() {
    console.log(typeof w); // "function"
};
console.log(typeof w);     // "undefined"

請注意,NFE經常是JavaScript實現錯誤的來源。 例如,IE8和更早版本完全無法正確處理NFE,從而在兩個不同的時間創建了兩個不同的功能。 Safari的早期版本也存在問題。 好消息是,當前版本的瀏覽器(IE9及更高版本,當前的Safari)不再存在這些問題。 (但是,在撰寫本文時,令人遺憾的是,IE8仍在廣泛使用,因此,將NFE與Web代碼一起使用通常仍然存在問題。)

訪問器功能初始化器(ES5 +)

有時,功能可能會在很大程度上被忽視。 訪問器函數就是這種情況。 這是一個例子:

var obj = {
    value: 0,
    get f() {
        return this.value;
    },
    set f(v) {
        this.value = v;
    }
};
console.log(obj.f);         // 0
console.log(typeof obj.f);  // "number"

請注意,當我使用該函數時,沒有使用() 那是因為它是屬性的訪問器函數 我們以常規方式獲取並設置屬性,但是在後台調用了該函數。

您還可以使用Object.definePropertyObject.defineProperties以及鮮為人知的Object.create第二個參數創建訪問器函數。

箭頭函數表達式(ES2015 +)

ES2015帶給我們箭頭功能 這是一個例子:

var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6

看到n => n * 2隱藏在map()調用中了嗎? 這是一個功能。

關於箭頭功能的幾件事:

  1. 他們沒有自己的this 相反,他們密切在 this背景下,他們正在定義。 (它們還會關閉arguments以及相關的super 。)這意味著它們中的this與創建它們時的this相同,並且無法更改。

  2. 正如您在上面已經註意到的那樣,您沒有使用關鍵字function 而是使用=>

上面的n => n * 2示例是它們的一種形式。 如果您有多個參數來傳遞函數,請使用parens:

var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6

(請記住, Array#map將條目作為第一個參數傳遞,將索引作為第二個參數傳遞。)

在這兩種情況下,函數的主體都只是一個表達式; 該函數的返回值將自動是該表達式的結果(您無需使用顯式的return )。

如果您不僅要執行單個表達式,還可以像往常一樣使用{}和一個顯式的return (如果需要返回值):

var a = [
  {first: "Joe", last: "Bloggs"},
  {first: "Albert", last: "Bloggs"},
  {first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
  var rv = a.last.localeCompare(b.last);
  if (rv === 0) {
    rv = a.first.localeCompare(b.first);
  }
  return rv;
});
console.log(JSON.stringify(a));

沒有{ ... }的版本稱為帶有表達式主體簡潔主體的箭頭函數。 (也: 簡潔的箭頭函數。)帶有{ ... }定義body的函數是帶有函數body的箭頭函數。 (還: 詳細的箭頭功能。)

對像初始化器中的方法聲明(ES2015 +)

ES2015允許使用一種簡短形式來聲明引用一個稱為方法定義的函數的屬性; 它看起來像這樣:

var o = {
    foo() {
    }
};

ES5和更早版本中幾乎相等的是:

var o = {
    foo: function foo() {
    }
};

區別(除了冗長)是方法可以使用super ,而函數不能。 因此,例如,如果您有一個使用方法語法定義(例如) valueOf的對象,則它可以使用super.valueOf()來獲取值Object.prototype.valueOf將返回(之前可能會對它進行其他操作),而ES5版本將不得不執行Object.prototype.valueOf.call(this)

這也意味著該方法引用了對其定義的對象,因此,如果該對像是臨時的(例如,您將其作為源對象之一傳遞給Object.assign ),則方法語法可能意味著否則可能會對其進行垃圾回收(如果JavaScript引擎未檢測到這種情況並在沒有方法使用super情況下進行處理),則將該對象保留在內存中。

構造和方法聲明在class (ES2015 +)

ES2015為我們帶來了class語法,包括聲明的構造函數和方法:

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName() {
        return this.firstName + " " + this.lastName;
    }
}

上面有兩個函數聲明:一個用於構造函數,其名稱為Person ,另一個用於getFullName ,其是分配給Person.prototype的函數。


31
投票

第一個示例是函數聲明:

function abc(){}

第二個示例是一個函數表達式:

var abc = function() {};

主要區別在於如何吊起(吊起和聲明)它們。 在第一個示例中,整個函數聲明被提升。 在第二個示例中,僅吊起var'abc',其值(函數)將是未定義的,並且函數本身保持在聲明的位置。

簡而言之:

//this will work
abc(param);
function abc(){}

//this would fail
abc(param);
var abc = function() {}

要研究有關此主題的更多信息,強烈建議您使用此鏈接


99
投票

格雷格答案的更好解釋

functionTwo();
function functionTwo() {
}

為什麼沒有錯誤? 我們總是被教導表達式從上到下執行(??)

因為:

JavaScript解釋器總是將函數聲明和變量聲明不可見地移動( hoisted )到其包含範圍的頂部。 函數參數和語言定義的名稱顯然已經存在。 本櫻桃

這意味著這樣的代碼:

functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };

請注意,聲明的賦值部分未懸掛。 僅懸掛名稱。

但是對於函數聲明,整個函數體也將被提升

functionTwo();              ---------------      function functionTwo() {
                            | is actually |      };
function functionTwo() {    | interpreted |-->
}                           |    like     |      functionTwo();
                            ---------------

11
投票

根據“命名函數顯示在堆棧跟踪中”的說法,現代JavaScript引擎實際上具有表示匿名函數的能力。

在撰寫本文時,V8,SpiderMonkey,Chakra和Nitro始終通過名稱來引用命名函數。 如果有匿名函數,它們幾乎總是通過其標識符來引用它。

SpiderMonkey可以找出從另一個函數返回的匿名函數的名稱。 其餘的不能。

如果您確實非常希望迭代器和成功回調顯示在跟踪中,那麼您也可以為其命名...

[].forEach(function iterator() {});

但是在大多數情況下,不值得強調。

線束( 小提琴

'use strict';

var a = function () {
    throw new Error();
},
    b = function b() {
        throw new Error();
    },
    c = function d() {
        throw new Error();
    },
    e = {
        f: a,
        g: b,
        h: c,
        i: function () {
            throw new Error();
        },
        j: function j() {
            throw new Error();
        },
        k: function l() {
            throw new Error();
        }
    },
    m = (function () {
        return function () {
            throw new Error();
        };
    }()),
    n = (function () {
        return function n() {
            throw new Error();
        };
    }()),
    o = (function () {
        return function p() {
            throw new Error();
        };
    }());

console.log([a, b, c].concat(Object.keys(e).reduce(function (values, key) {
    return values.concat(e[key]);
}, [])).concat([m, n, o]).reduce(function (logs, func) {

    try {
        func();
    } catch (error) {
        return logs.concat('func.name: ' + func.name + '\n' +
                           'Trace:\n' +
                           error.stack);
        // Need to manually log the error object in Nitro.
    }

}, []).join('\n\n'));

V8

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at e.i (http://localhost:8000/test.js:17:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: j
Trace:
Error
    at j (http://localhost:8000/test.js:20:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: l
Trace:
Error
    at l (http://localhost:8000/test.js:23:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at http://localhost:8000/test.js:28:19
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: n
Trace:
Error
    at n (http://localhost:8000/test.js:33:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: p
Trace:
Error
    at p (http://localhost:8000/test.js:38:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27 test.js:42

蜘蛛猴

func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
e.i@http://localhost:8000/test.js:17:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: j
Trace:
j@http://localhost:8000/test.js:20:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: l
Trace:
l@http://localhost:8000/test.js:23:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
m</<@http://localhost:8000/test.js:28:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: n
Trace:
n@http://localhost:8000/test.js:33:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: p
Trace:
p@http://localhost:8000/test.js:38:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1

脈輪

func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at e.i (http://localhost:8000/test.js:17:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at j (http://localhost:8000/test.js:20:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at l (http://localhost:8000/test.js:23:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at Anonymous function (http://localhost:8000/test.js:28:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at n (http://localhost:8000/test.js:33:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at p (http://localhost:8000/test.js:38:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)

硝基

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
i@http://localhost:8000/test.js:17:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: j
Trace:
j@http://localhost:8000/test.js:20:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: l
Trace:
l@http://localhost:8000/test.js:23:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
http://localhost:8000/test.js:28:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: n
Trace:
n@http://localhost:8000/test.js:33:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: p
Trace:
p@http://localhost:8000/test.js:38:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

120
投票

您在此處張貼的兩個代碼段幾乎可以出於所有目的以相同的方式運行。

但是,行為上的差異在於,對於第一個變量( var functionOne = function() {} ),只能在代碼中的該點之後調用該函數。

使用第二個變體( function functionTwo() ),該函數可用於在聲明函數上方運行的代碼。

這是因為在第一個變量中,該函數在運行時分配給了變量foo 在第二個中,在解析時將函數分配給該標識符foo

更多技術信息

JavaScript具有三種定義函數的方式。

  1. 您的第一個代碼片段顯示了一個函數表達式 這涉及使用“函數”運算符創建函數-該運算符的結果可以存儲在任何變量或對象屬性中。 這樣函數表達式功能強大。 函數表達式通常稱為“匿名函數”,因為它不必具有名稱,
  2. 您的第二個示例是函數聲明 這使用“功能”語句創建功能。 該函數在解析時可用,並且可以在該範圍內的任何位置調用。 以後您仍然可以將其存儲在變量或對象屬性中。
  3. 定義函數的第三種方式是“ Function()”構造函數 ,該函數未在原始文章中顯示。 不建議使用此方法,因為它的工作方式與eval()相同,但存在問題。

39
投票

我添加自己的答案只是因為其他所有人都已經完全涵蓋了吊裝部分。

我想知道很長一段時間以來哪種方法更好,並且感謝http://jsperf.com,現在我知道了:)

在此處輸入圖片說明

函數聲明更快,這就是Web開發人員真正重要的事情吧? ;)


6
投票

這只是聲明函數的兩種可能方法,第二種方法是可以在聲明之前使用該函數。


22
投票

Greg的Answer足夠好,但是我仍然想在我剛剛看過Douglas Crockford的視頻時學到一些東西。

函數表達式:

var foo = function foo() {};

功能說明:

function foo() {};

函數語句只是帶有function值的var語句的簡寫。

所以

function foo() {};

擴展到

var foo = function foo() {};

進一步擴展為:

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

它們都被提升到了代碼的頂部。

視頻截圖


14
投票

我列出了以下差異:

  1. 函數聲明可以放在代碼中的任何位置。 即使在定義出現在代碼中之前被調用,它也會在頁面中的任何其他代碼開始執行之前,因為函數聲明被提交到內存或以某種方式提升而執行。

    看一下下面的函數:

     function outerFunction() { function foo() { return 1; } return foo(); function foo() { return 2; } } alert(outerFunction()); // Displays 2 

    這是因為在執行過程中,它看起來像:

     function foo() { // The first function declaration is moved to top return 1; } function foo() { // The second function declaration is moved to top return 2; } function outerFunction() { return foo(); } alert(outerFunction()); //So executing from top to bottom, //the last foo() returns 2 which gets displayed 

    如果在調用函數表達式之前未對其進行定義,則會導致錯誤。 同樣,在這裡,函數定義本身不會像函數聲明中那樣移到頂部或提交到內存中。 但是我們分配給函數的變量被提升,而未定義的變量被賦給它。

    使用函數表達式的相同函數:

     function outerFunction() { var foo = function() { return 1; } return foo(); var foo = function() { return 2; } } alert(outerFunction()); // Displays 1 

    這是因為在執行期間,它看起來像:

     function outerFunction() { var foo = undefined; var foo = undefined; foo = function() { return 1; }; return foo (); foo = function() { // This function expression is not reachable return 2; }; } alert(outerFunction()); // Displays 1 
  2. 它是不是安全寫在非功能塊函數聲明一樣,如果因為他們將無法訪問。

     if (test) { function x() { doSomething(); } } 
  3. 如下所示的命名函數表達式可能無法在版本9之前的Internet Explorer瀏覽器中使用。

     var today = function today() {return new Date()} 

4895
投票

不同之處在於functionOne是函數表達式,因此僅在到達該行時定義,而functionTwo是函數聲明,並在其周圍的函數或腳本執行後(由於提升 )而定義。

例如,一個函數表達式:

 // TypeError: functionOne is not a function functionOne(); var functionOne = function() { console.log("Hello!"); }; 

並且,一個函數聲明:

 // Outputs: "Hello!" functionTwo(); function functionTwo() { console.log("Hello!"); } 

這也意味著您不能使用函數聲明有條件地定義函數:

if (test) {
   // Error or misbehavior
   function functionThree() { doSomething(); }
}

上面實際上定義了functionThree而與test的值無關—除非use strict生效,否則只會引發錯誤。


1903
投票

首先,我想更正Greg: function abc(){}具有作用域-名稱abc是在遇到該定義的作用域中定義的。 例:

function xyz(){
  function abc(){};
  // abc is defined here...
}
// ...but not here

其次,可以將兩種樣式結合起來:

var xyz = function abc(){};

xyz將照常定義,在所有瀏覽器中abc都是未定義的,但是Internet Explorer –不依賴於它的定義。 但它將在其內部定義:

var xyz = function abc(){
  // xyz is visible here
  // abc is visible here
}
// xyz is visible here
// abc is undefined here

如果要在所有瀏覽器上使用別名函數,請使用以下聲明:

function abc(){};
var xyz = abc;

在這種情況下, xyzabc都是同一對象的別名:

console.log(xyz === abc); // prints "true"

使用組合樣式的一個令人信服的原因是功能對象的“名稱”屬性( Internet Explorer不支持 )。 基本上,當您定義類似

function abc(){};
console.log(abc.name); // prints "abc"

它的名稱是自動分配的。 但是當你定義它像

var abc = function(){};
console.log(abc.name); // prints ""

它的名稱為空-我們創建了一個匿名函數並將其分配給某個變量。

使用組合樣式的另一個很好的理由是使用一個簡短的內部名稱來引用自身,同時為外部用戶提供一個長而不會衝突的名稱:

// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
  // Let it call itself recursively:
  shortcut(n - 1);
  // ...
  // Let it pass itself as a callback:
  someFunction(shortcut);
  // ...
}

在上面的示例中,我們可以對外部名稱執行相同的操作,但是它太笨拙(而且速度較慢)。

(引用自身的另一種方法是使用arguments.callee ,它仍然相對較長,並且在嚴格模式下不受支持。)

內心深處,JavaScript對兩種語句的處理方式有所不同。 這是一個函數聲明:

function abc(){}

abc在當前範圍的任何地方都定義如下:

// We can call it here
abc(); // Works

// Yet, it is defined down there.
function abc(){}

// We can call it again
abc(); // Works

此外,它還通過return語句進行了掛起:

// We can call it here
abc(); // Works
return;
function abc(){}

這是一個函數表達式:

var xyz = function(){};

xyz是從分配的角度定義的:

// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works

函數聲明與函數表達式是Greg證明存在差異的真正原因。

有趣的事實:

var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"

就我個人而言,我更喜歡“函數表達式”聲明,因為這樣我可以控制可見性。 當我定義函數時

var abc = function(){};

我知道我在本地定義了函數。 當我定義函數時

abc = function(){};

我知道我是在全局範圍內定義的,前提是我沒有在範圍鏈中的任何地方定義abc 即使在eval()內使用時,這種定義方式也具有彈性。 而定義

function abc(){};

取決於上下文,並且可能讓您猜測它的實際定義位置,尤其是在eval()的情況下—答案是:它取決於瀏覽器。


138
投票

說到全局上下文,最後的var語句和FunctionDeclaration都會在全局對像上創建不可刪除的屬性,但是兩者的值都可以被覆蓋

兩種方式之間的細微差別是,當變量實例化過程運行時(在實際代碼執行之前),所有用var聲明的標識符都將用undefined初始化,並且從那一刻起, FunctionDeclaration所使用的標識符將可用,對於例:

 alert(typeof foo); // 'function', it's already available
 alert(typeof bar); // 'undefined'
 function foo () {}
 var bar = function () {};
 alert(typeof bar); // 'function'

FunctionExpression表達式bar的分配一直進行到運行時。

FunctionDeclaration創建的全局屬性可以被覆蓋而沒有任何問題,就像變量值一樣,例如:

 function test () {}
 test = null;

兩個示例之間的另一個明顯區別是,第一個函數沒有名稱,而第二個函數具有名稱,這在調試(即檢查調用堆棧)時確實非常有用。

關於您編輯的第一個示例( foo = function() { alert('hello!'); }; ),這是一個未聲明的賦值,我強烈建議您始終使用var關鍵字。

在沒有var語句的情況下進行賦值時,如果在作用域鏈中找不到引用的標識符,它將成為全局對象的可刪除屬性。

另外,在嚴格模式下,未聲明的分配會在ECMAScript 5上引發ReferenceError

必須閱讀:

注意 :此答案已與另一個問題合併,在OP中,主要的疑問和誤解是使用FunctionDeclaration聲明的標識符不能被覆蓋,事實並非如此。


60
投票

一個重要的原因是要添加一個且僅一個變量作為名稱空間的“根”。

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

}

要么

var MyNamespace = {
  foo: function() {
  },
  ...
}

有很多命名空間的技術。 可用的JavaScript模塊數量越來越多,這一點變得越來越重要。

另請參閱如何在JavaScript中聲明名稱空間?


9
投票

兩者都是定義函數的不同方式。 不同之處在於瀏覽器如何解釋並將其加載到執行上下文中。

第一種情況是函數表達式,僅在解釋器到達該行代碼時才加載。 因此,如果按照以下方式進行操作,則會收到錯誤消息,提示functionOne不是function

functionOne();
var functionOne = function() {
    // Some code
};

原因是在第一行沒有將任何值分配給functionOne,因此未定義。 我們試圖將其稱為函數,因此會出現錯誤。

在第二行中,我們將匿名函數的引用分配給functionOne。

第二種情況是在執行任何代碼之前加載的函數聲明。 因此,如果您喜歡以下內容,則在代碼執行之前加載聲明不會有任何錯誤。

functionOne();
function functionOne() {
   // Some code
}

54
投票

提升 是JavaScript解釋器將所有變量和函數聲明移到當前作用域頂部的操作。

但是,僅懸掛實際的聲明。 通過將作業留在原處。

  • 頁面內聲明的變量/函數是全局的,可以在該頁面的任何位置訪問。
  • 在函數內部聲明的變量/函數具有局部作用域。 表示它們在功能主體(作用域)內部可用/訪問,在功能主體外部不可用。

變量

Javascript被稱為鬆散類型語言。 這意味著Javascript變量可以保存任何Data-Type的值。 Javascript會根據運行時提供的值/字面值自動更改變量類型。

global_Page = 10;                                               var global_Page;      « undefined
    « Integer literal, Number Type.   -------------------       global_Page = 10;     « Number         
global_Page = 'Yash';                 |   Interpreted   |       global_Page = 'Yash'; « String
    « String literal, String Type.    «       AS        «       global_Page = true;   « Boolean 
var global_Page = true;               |                 |       global_Page = function (){          « function
    « Boolean Type                    -------------------                 var local_functionblock;  « undefined
global_Page = function (){                                                local_functionblock = 777;« Number
    var local_functionblock = 777;                              };  
    // Assigning function as a data.
};  

功能

function Identifier_opt ( FormalParameterList_opt ) { 
      FunctionBody | sequence of statements

      « return;  Default undefined
      « return 'some data';
}
  • 頁面內部聲明的函數被提升到具有全局訪問權限的頁面頂部。
  • 在功能塊內部聲明的功能被提升到該塊的頂部。
  • 函數的默認返回值為“ undefined ”, 變量聲明的默認值也為“ undefined”

     Scope with respect to function-block global. Scope with respect to page undefined | not available. 

功能聲明

function globalAccess() {                                  function globalAccess() {      
}                                  -------------------     }
globalAccess();                    |                 |     function globalAccess() { « Re-Defined / overridden.
localAccess();                     «   Hoisted  As   «         function localAccess() {
function globalAccess() {          |                 |         }
     localAccess();                -------------------         localAccess(); « function accessed with in globalAccess() only.
     function localAccess() {                              }
     }                                                     globalAccess();
}                                                          localAccess(); « ReferenceError as the function is not defined

函數表達式

        10;                 « literal
       (10);                « Expression                (10).toString() -> '10'
var a;                      
    a = 10;                 « Expression var              a.toString()  -> '10'
(function invoke() {        « Expression Function
 console.log('Self Invoking');                      (function () {
});                                                               }) () -> 'Self Invoking'

var f; 
    f = function (){        « Expression var Function
    console.log('var Function');                                   f ()  -> 'var Function'
    };

分配給變量的函數示例:

(function selfExecuting(){
    console.log('IIFE - Immediately-Invoked Function Expression');
}());

var anonymous = function (){
    console.log('anonymous function Expression');
};

var namedExpression = function for_InternalUSE(fact){
    if(fact === 1){
        return 1;
    }

    var localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    //return; //undefined.
    return fact * for_InternalUSE( fact - 1);   
};

namedExpression();
globalExpression();

javascript解釋為

var anonymous;
var namedExpression;
var globalExpression;

anonymous = function (){
    console.log('anonymous function Expression');
};

namedExpression = function for_InternalUSE(fact){
    var localExpression;

    if(fact === 1){
        return 1;
    }
    localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    return fact * for_InternalUSE( fact - 1);    // DEFAULT UNDEFINED.
};

namedExpression(10);
globalExpression();

您可以使用jsperf Test Runner在不同的瀏覽器上檢查函數聲明,表達式測試


ES5構造函數類 :使用Function.prototype.bind創建的函數對象

JavaScript將函數視為一流的對象,因此作為對象,您可以將屬性分配給函數。

function Shape(id) { // Function Declaration
    this.id = id;
};
    // Adding a prototyped method to a function.
    Shape.prototype.getID = function () {
        return this.id;
    };
    Shape.prototype.setID = function ( id ) {
        this.id = id;
    };

var expFn = Shape; // Function Expression

var funObj = new Shape( ); // Function Object
funObj.hasOwnProperty('prototype'); // false
funObj.setID( 10 );
console.log( funObj.getID() ); // 10

ES6引入了Arrow函數Arrow函數表達式具有較短的語法,它們最適合於非方法函數,並且不能用作構造函數。

ArrowFunction : ArrowParameters => ConciseBody

 const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; }; console.log( fn(2) ); // Even console.log( fn(3) ); // Odd 

5
投票

new Function()可用於在字符串中傳遞函數的主體。 因此,可以將其用於創建動態功能。 也傳遞腳本而不執行腳本。

var func = new Function("x", "y", "return x*y;");
function secondFunction(){
   var result;
   result = func(10,20);
   console.log ( result );
}

secondFunction()

23
投票

用計算機科學術語,我們談論匿名函數和命名函數。 我認為最重要的區別是,匿名函數未綁定到名稱,因此名稱匿名函數。 在JavaScript中,它是在運行時動態聲明的一流對象。

有關匿名函數和lambda演算的更多信息,Wikipedia是一個好的開始( http://en.wikipedia.org/wiki/Anonymous_function )。


10
投票

在JavaScript中,有兩種​​創建函數的方法:

  1. 函數聲明:

     function fn(){ console.log("Hello"); } fn(); 

    這是非常基本的,不言自明的,適用於多種語言,並跨C語言家族使用。 我們聲明了一個定義它的函數,並通過調用它來執行它。

    您應該知道的是,函數實際上是JavaScript中的對象。 在內部,我們為上述函數創建了一個對象,並為其命名為fn或對該對象的引用存儲在fn中。 函數是JavaScript中的對象; 函數的實例實際上是一個對象實例。

  2. 函數表達式:

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

    JavaScript具有一流的函數,即創建函數並將其分配給變量,就像創建字符串或數字並將其分配給變量一樣。 在此,將fn變量分配給一個函數。 這個概念的原因是函數是JavaScript中的對象。 fn指向上述函數的對象實例。 我們已經初始化了一個函數並將其分配給變量。 它沒有執行功能並沒有分配結果。

參考: JavaScript函數聲明語法:var fn = function(){}與函數fn(){}


8
投票

它們非常相似,但有一些細微的差別,第一個是分配給匿名函數的變量(函數聲明),第二個是在JavaScript中創建函數的正常方式(匿名函數聲明),兩者都有用法,缺點和優點:

1.函數表達式

var functionOne = function() {
    // Some code
};

函數表達式將函數定義為更大的表達式語法(通常是變量賦值)的一部分。 通過函數表達式定義的函數可以命名或匿名。 函數表達式不能以“函數”開頭(因此,下面的自調用示例周圍帶有括號)。

為函數分配變量意味著沒有提升,因為我們知道JavaScript中的函數可以提升,意味著可以在聲明它們之前調用它們,而在訪問它們之前需要先聲明變量,因此在這種情況下,我們不能在聲明函數之前訪問函數,這也可能是您編寫函數的一種方式,對於返回另一個函數的函數,這種聲明很有意義,同樣在ECMA6及更高版本中,您可以將其分配給箭頭函數,可以用來調用匿名函數,這種聲明方式也是在JavaScript中創建構造函數的更好方法。

2.功能聲明

function functionTwo() {
    // Some code
}

函數聲明定義了一個命名函數變量,而無需分配變量。 函數聲明作為獨立的構造出現,不能嵌套在非函數塊中。 將它們視為變量聲明的兄弟是有幫助的。 就像變量聲明必須以“ var”開始一樣,函數聲明也必須以“ function”開始。

這是在JavaScript中調用函數的通常方式,可以在甚至聲明它之前就調用該函數,因為在JavaScript中所有函數都被提升了,但是如果您“嚴格”使用,則不會按預期進行提升,這是一個好方法調用所有普通函數,這些函數不是很大,也不是構造函數。

另外,如果您需要有關提昇在JavaScript中的工作方式的更多信息,請訪問以下鏈接:

https://developer.mozilla.org/zh-CN/docs/Glossary/Hoisting


9
投票

關於效果:

V8新版本引入了一些SpiderMonkey優化, SpiderMonkey也是如此。

現在,表達式和聲明之間幾乎沒有區別。
現在函數表達似乎更快

鉻62.0.3202 鍍鉻測試

火狐55 Firefox測試

鉻金絲雀63.0.3225 Chrome Canary測試


Anonymous函數表達式似乎比 Named函數表達式具有更好的性能


火狐瀏覽器 Firefox named_anonymous 鉻金絲雀 Chrome金絲雀named_anonymous Chrome named_anonymous


13
投票

𝗯𝗲𝗹𝗼𝘄𝗯𝗲𝗹𝗼𝘄𝗯𝗲𝗹𝗼𝘄𝗯𝗲𝗹𝗼𝘄𝗯𝗲𝗹𝗼𝘄𝗯𝗲𝗹𝗼𝘄𝗯𝗲𝗹𝗼𝘄𝗯𝗲𝗹𝗼𝘄𝗯𝗲𝗹𝗼𝘄𝗯𝗲𝗹𝗼𝘄

  1. 功能的可用性(範圍)

由於function add()的作用域範圍是最近的塊,因此可以使用以下代碼:

 try { console.log("Success: ", add(1, 1)); } catch(e) { console.log("ERROR: " + e); } function add(a, b){ return a + b; } 

以下內容不起作用,因為在將函數值分配給變量add之前調用了該變量。

 try { console.log("Success: ", add(1, 1)); } catch(e) { console.log("ERROR: " + e); } var add=function(a, b){ return a + b; } 

上面的代碼在功能上與下面的代碼相同。 注意,顯式分配add = undefined是多餘的,因為只需執行var add;即可var add; var add=undefined完全相同。

 var add = undefined; try { console.log("Success: ", add(1, 1)); } catch(e) { console.log("ERROR: " + e); } add = function(a, b){ return a + b; } 

由於var add=取代了function add()因此以下命令不起作用。

 try { console.log("Success: ", add(1, 1)); } catch(e) { console.log("ERROR: " + e); } var add=function add(a, b){ return a + b; } 

  1. (功能) .name

以這種方式聲明時,函數function thefuncname(){}thefuncname

 function foobar(a, b){} console.log(foobar.name); 

 var a = function foobar(){}; console.log(a.name); 

否則,如果將一個函數聲明為function(){} ,則函數 .name是用於存儲該函數的第一個變量。

 var a = function(){}; var b = (function(){ return function(){} }); console.log(a.name); console.log(b.name); 

如果沒有為函數設置任何變量,則函數名稱為空字符串( "" )。

 console.log((function(){}).name === ""); 

最後,雖然分配給函數的變量最初設置了名稱,但設置給函數的連續變量不會更改名稱。

 var a = function(){}; var b = a; var c = b; console.log(a.name); console.log(b.name); console.log(c.name); 

  1. 性能

在Google的V8和Firefox的Spidermonkey中,可能會有幾微秒的JIST編譯差異,但最終結果是完全相同的。 為了證明這一點,讓我們通過比較兩個空白代碼段的速度來檢驗JSPerf在微基準測試中的效率。 此可以找到JSPerf測試 並且, jsben.ch測試位於此處 如您所見,什麼都不應該有明顯的區別。 如果您確實像我一樣是個性能怪胎,那麼嘗試減少範圍中的變量和函數的數量,尤其是消除多態性(例如使用相同的變量存儲兩種不同的類型)時,可能更值得。

  1. 可變性

當使用var關鍵字聲明變量時,可以像這樣將另一個值重新分配給該變量。

 (function(){ "use strict"; var foobar = function(){}; // initial value try { foobar = "Hello World!"; // new value console.log("[no error]"); } catch(error) { console.log("ERROR: " + error.message); } console.log(foobar, window.foobar); })(); 

但是,當我們使用const語句時,變量引用變得不可變。 這意味著我們不能為變量分配新值。 但是請注意,這不會使變量的內容不可變:如果您執行const arr = [] ,那麼仍然可以執行arr[10] = "example" 僅執行類似arr = "new value"arr = []會引發錯誤,如下所示。

 (function(){ "use strict"; const foobar = function(){}; // initial value try { foobar = "Hello World!"; // new value console.log("[no error]"); } catch(error) { console.log("ERROR: " + error.message); } console.log(foobar, window.foobar); })(); 

有趣的是,如果我們將變量聲明為function funcName(){} ,則變量的不變性與使用var聲明變量相同。

 (function(){ "use strict"; function foobar(){}; // initial value try { foobar = "Hello World!"; // new value console.log("[no error]"); } catch(error) { console.log("ERROR: " + error.message); } console.log(foobar, window.foobar); })(); 

𝗧𝗵𝗲“𝗧𝗵𝗲”

“最近的塊”是最接近的“函數”(包括異步函數,生成器函數和異步生成器函數)。 然而,有趣的是,一個function functionName() {}表現得像var functionName = function() {}在非閉合塊項目時外部,所述閉合。 觀察一下。

  • 普通的var add=function(){}

 try { // typeof will simply return "undefined" if the variable does not exist if (typeof add !== "undefined") { add(1, 1); // just to prove it console.log("Not a block"); }else if(add===undefined){ // this throws an exception if add doesn't exist console.log('Behaves like var add=function(a,b){return a+b}'); } } catch(e) { console.log("Is a block"); } var add=function(a, b){return a + b} 

  • 正常function add(){}

 try { // typeof will simply return "undefined" if the variable does not exist if (typeof add !== "undefined") { add(1, 1); // just to prove it console.log("Not a block"); }else if(add===undefined){ // this throws an exception if add doesn't exist console.log('Behaves like var add=function(a,b){return a+b}') } } catch(e) { console.log("Is a block"); } function add(a, b){ return a + b; } 

  • 功能

 try { // typeof will simply return "undefined" if the variable does not exist if (typeof add !== "undefined") { add(1, 1); // just to prove it console.log("Not a block"); }else if(add===undefined){ // this throws an exception if add doesn't exist console.log('Behaves like var add=function(a,b){return a+b}') } } catch(e) { console.log("Is a block"); } (function () { function add(a, b){ return a + b; } })(); 

  • 聲明(例如, ifelseforwhiletry / catch / finallyswitchdo / whilewith

 try { // typeof will simply return "undefined" if the variable does not exist if (typeof add !== "undefined") { add(1, 1); // just to prove it console.log("Not a block"); }else if(add===undefined){ // this throws an exception if add doesn't exist console.log('Behaves like var add=function(a,b){return a+b}') } } catch(e) { console.log("Is a block"); } { function add(a, b){ return a + b; } } 

  • 帶有var add=function()箭頭函數

 try { // typeof will simply return "undefined" if the variable does not exist if (typeof add !== "undefined") { add(1, 1); // just to prove it console.log("Not a block"); }else if(add===undefined){ // this throws an exception if add doesn't exist console.log('Behaves like var add=function(a,b){return a+b}') } } catch(e) { console.log("Is a block"); } (() => { var add=function(a, b){ return a + b; } })(); 

  • 帶有function add()箭頭功能

 try { // typeof will simply return "undefined" if the variable does not exist if (typeof add !== "undefined") { add(1, 1); // just to prove it console.log("Not a block"); }else if(add===undefined){ // this throws an exception if add doesn't exist console.log('Behaves like var add=function(a,b){return a+b}') } } catch(e) { console.log("Is a block"); } (() => { function add(a, b){ return a + b; } })(); 


88
投票

其他評論者已經涵蓋了以上兩個變體的語義差異。 我想指出一種風格上的差異:只有“賦值”變體可以設置另一個對象的屬性。

我經常用以下模式構建JavaScript模塊:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

    exports.publicUtil = function() {
            ...
    };

    return exports;
})();

通過這種模式,您的公共函數將全部使用賦值,而私有函數將使用聲明。

(還請注意,在聲明之後,賦值應使用分號,而在聲明中則禁止使用分號。)


©2020 sofbug.com - All rights reserved.