var functionName = function () {} so với function functionName () {}


6874

Gần đây tôi đã bắt đầu duy trì mã JavaScript của người khác. Tôi đang sửa lỗi, thêm các tính năng và cũng đang cố gắng dọn dẹp mã và làm cho nó phù hợp hơn.

Nhà phát triển trước đó đã sử dụng hai cách khai báo hàm và tôi không thể tìm ra liệu có lý do nào đằng sau nó hay không.

Hai cách là:

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

Các lý do để sử dụng hai phương pháp khác nhau này là gì và ưu và nhược điểm của mỗi phương pháp là gì? Có bất cứ điều gì có thể được thực hiện với một phương pháp không thể được thực hiện với phương pháp kia không?


198
permadi.com/tutorial/jsFunc/index.html là trang rất hay về các chức năng javascript
uzay95

67
Liên quan là bài viết tuyệt vời này về Biểu thức chức năng được đặt tên .
Phrogz

12
@CMS tham khảo bài viết này: kangax.github.com/nfe/#expr-vs-decl
Upperstage

106
Có hai điều bạn cần lưu ý: # 1 Trong JavaScript, các khai báo được nâng lên. Có nghĩa là var a = 1; var b = 2;trở thành var a; var b; a = 1; b = 2. Vì vậy, khi bạn khai báo functionOne, nó sẽ được khai báo nhưng giá trị của nó không được đặt ngay lập tức. Trong khi vì hàmTwo chỉ là một khai báo, nó được đặt ở đầu phạm vi. # 2 functionTwo cho phép bạn truy cập thuộc tính name và điều đó giúp ích rất nhiều khi cố gắng gỡ lỗi một cái gì đó.
xavierm02

65
Oh và btw, cú pháp đúng là với dấu ";" sau khi chuyển nhượng và không có sau khi khai báo. Ví dụ function f(){}vs var f = function(){};.
xavierm02

Câu trả lời:


5045

Sự khác biệt là functionOnebiểu thức hàm và do đó chỉ được xác định khi đạt đến dòng đó, trong khi đó functionTwolà khai báo hàm và được xác định ngay khi hàm hoặc tập lệnh xung quanh của nó được thực thi (do cẩu ).

Ví dụ: một biểu thức hàm:

// TypeError: functionOne is not a function
functionOne();

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

Và, một khai báo hàm:

// Outputs: "Hello!"
functionTwo();

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

Trong lịch sử, các khai báo hàm được xác định trong các khối được xử lý không nhất quán giữa các trình duyệt. Chế độ nghiêm ngặt (được giới thiệu trong ES5) đã giải quyết điều này bằng cách khai báo hàm phạm vi cho khối kèm theo của chúng.

'use strict';    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError


632
@Greg: Nhân tiện, sự khác biệt không chỉ là chúng được phân tích cú pháp tại các thời điểm khác nhau. Về cơ bản, của bạn functionOnechỉ là một biến có chức năng ẩn danh được gán cho nó, trong khi functionTwothực tế là một hàm được đặt tên. Gọi .toString()cho cả hai để thấy sự khác biệt. Điều này rất có ý nghĩa trong một số trường hợp bạn muốn lấy tên của hàm theo chương trình.
Jason Bunting

6
@Jason Bunting .. không chắc chắn những gì bạn nhận được ở đây, .toString () dường như trả về cơ bản cùng một giá trị (định nghĩa hàm) cho cả hai: cl.ly/2a2C2Y1r0J451o0q0B1B
Jon z

124
Có cả hai khác nhau. Cái đầu tiên là cái function expressionthứ hai là mộtfunction declaration . Bạn có thể đọc thêm về chủ đề ở đây: javascriptweblog.wordpress.com/2010/07/06/
Khăn

127
@Greg Phần câu trả lời của bạn liên quan đến thời gian phân tích so với thời gian chạy là không chính xác. Trong JavaScript, khai báo hàm không được xác định trong thời gian phân tích cú pháp, nhưng trong thời gian chạy. Quá trình diễn ra như sau: Mã nguồn được phân tích cú pháp -> Chương trình JavaScript được đánh giá -> Bối cảnh thực thi toàn cầu được khởi tạo -> khởi tạo ràng buộc khai báo được thực hiện. Trong quá trình này, các khai báo hàm được khởi tạo (xem bước 5 của Chương 10.5 ).
Vidime Vidas

102
Thuật ngữ cho hiện tượng này được gọi là cẩu.
Colin lê

1942

Đầu tiên tôi muốn sửa Greg: function abc(){}cũng nằm trong phạm vi - tên abcđược xác định trong phạm vi mà định nghĩa này gặp phải. Thí dụ:

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

Thứ hai, có thể kết hợp cả hai phong cách:

var xyz = function abc(){};

xyzsẽ được định nghĩa như bình thường, abckhông được xác định trong tất cả các trình duyệt nhưng Internet Explorer - không dựa vào nó được xác định. Nhưng nó sẽ được xác định bên trong cơ thể của nó:

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

Nếu bạn muốn bí danh các chức năng trên tất cả các trình duyệt, hãy sử dụng loại khai báo này:

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

Trong trường hợp này, cả hai xyzabclà bí danh của cùng một đối tượng:

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

Một lý do thuyết phục để sử dụng kiểu kết hợp là thuộc tính "tên" của các đối tượng hàm ( không được Internet Explorer hỗ trợ ). Về cơ bản khi bạn xác định một chức năng như

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

tên của nó được gán tự động. Nhưng khi bạn định nghĩa nó như thế nào

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

tên của nó trống - chúng tôi đã tạo một hàm ẩn danh và gán nó cho một số biến.

Một lý do chính đáng khác để sử dụng kiểu kết hợp là sử dụng một tên nội bộ ngắn để chỉ chính nó, đồng thời cung cấp một tên dài không xung đột cho người dùng bên ngoài:

// 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);
  // ...
}

Trong ví dụ trên, chúng ta có thể làm tương tự với một tên bên ngoài, nhưng nó sẽ quá khó sử dụng (và chậm hơn).

(Một cách khác để tham khảo chính nó là sử dụng arguments.callee, vẫn còn tương đối dài và không được hỗ trợ trong chế độ nghiêm ngặt.)

Sâu xa hơn, JavaScript xử lý cả hai câu lệnh khác nhau. Đây là một khai báo hàm:

function abc(){}

abc ở đây được định nghĩa ở mọi nơi trong phạm vi hiện tại:

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

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

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

Ngoài ra, nó đã được nâng lên thông qua một returntuyên bố:

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

Đây là một biểu thức chức năng:

var xyz = function(){};

xyz ở đây được định nghĩa từ điểm gán:

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

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

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

Khai báo hàm so với biểu thức hàm là lý do thực sự tại sao có sự khác biệt được thể hiện bởi Greg.

Sự thật thú vị:

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

Cá nhân, tôi thích khai báo "biểu thức hàm" hơn vì cách này tôi có thể kiểm soát mức độ hiển thị. Khi tôi định nghĩa hàm như

var abc = function(){};

Tôi biết rằng tôi đã xác định chức năng địa phương. Khi tôi định nghĩa hàm như

abc = function(){};

Tôi biết rằng tôi đã xác định nó trên toàn cầu với điều kiện là tôi không định nghĩa abcbất cứ nơi nào trong chuỗi phạm vi. Phong cách định nghĩa này là kiên cường ngay cả khi được sử dụng bên trong eval(). Trong khi định nghĩa

function abc(){};

phụ thuộc vào ngữ cảnh và có thể khiến bạn đoán nó thực sự được xác định ở đâu, đặc biệt là trong trường hợp eval()- câu trả lời là: Nó phụ thuộc vào trình duyệt.


69
Tôi đề cập đến RoBorg nhưng anh ta không tìm thấy ở đâu. Đơn giản: RoBorg === Greg. Đó là cách lịch sử có thể được viết lại trong thời đại internet. ;-)
Eugene Lazutkin

10
var xyz = hàm abc () {}; console.log (xyz === abc); Tất cả các trình duyệt tôi đã thử nghiệm (Safari 4, Firefox 3.5.5, Opera 10.10) cung cấp cho tôi "Biến không xác định: abc".
NVI

7
Nhìn chung, tôi nghĩ rằng bài viết này làm tốt công việc giải thích sự khác biệt và lợi thế của việc sử dụng khai báo hàm. Tôi sẽ đồng ý không đồng ý với các lợi ích của việc sử dụng các phép gán biểu thức hàm cho một biến đặc biệt là vì "lợi ích" dường như là một sự ủng hộ khi tuyên bố một thực thể toàn cầu ... và mọi người đều biết rằng bạn không nên làm lộn xộn không gian tên toàn cầu , đúng? ;-)
natlee75

83
Tôi có một lý do rất lớn để sử dụng chức năng được đặt tên là vì trình gỡ lỗi có thể sử dụng tên để giúp bạn hiểu ý nghĩa của ngăn xếp cuộc gọi hoặc ngăn xếp cuộc gọi. nó thật tệ khi bạn nhìn vào ngăn xếp cuộc gọi và thấy "hàm ẩn danh" sâu 10 cấp ...
con dê

3
var abc = function(){}; console.log(abc.name);không sản xuất ""nữa, nhưng "abc"thay vào đó.
Qwerty

632

Dưới đây là danh sách các biểu mẫu tiêu chuẩn tạo ra các hàm: (Ban đầu được viết cho một câu hỏi khác, nhưng được điều chỉnh sau khi được chuyển sang câu hỏi chính tắc.)

Điều kiện:

Danh sách nhanh:

  • Tuyên bố chức năng

  • functionBiểu thức "ẩn danh" (bất chấp thuật ngữ, đôi khi tạo hàm với tên)

  • functionBiểu hiện được đặt tên

  • Trình khởi tạo chức năng Accessor (ES5 +)

  • Biểu thức hàm mũi tên (ES2015 +) (giống như các biểu thức hàm ẩn danh, không liên quan đến một tên rõ ràng và vẫn có thể tạo các hàm có tên)

  • Khai báo phương thức trong Trình khởi tạo đối tượng (ES2015 +)

  • Trình xây dựng và khai báo phương thức trong class(ES2015 +)

Tuyên bố chức năng

Biểu mẫu đầu tiên là một khai báo hàm , trông như thế này:

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

Một khai báo chức năng là một khai báo ; nó không phải là một tuyên bố hay biểu hiện. Như vậy, bạn không làm theo nó với một; (mặc dù làm như vậy là vô hại).

Một khai báo hàm được xử lý khi thực thi đi vào ngữ cảnh mà nó xuất hiện, trước khi bất kỳ mã từng bước nào được thực thi. Hàm nó tạo ra được đặt tên thích hợp (x trong ví dụ trên) và tên đó được đặt trong phạm vi mà khai báo xuất hiện.

Vì nó được xử lý trước bất kỳ mã từng bước nào trong cùng một ngữ cảnh, bạn có thể thực hiện những việc như sau:

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

Cho đến ES2015, spec không bao gồm những gì một công cụ JavaScript nên làm gì nếu bạn đặt một khai báo hàm bên trong một cấu trúc điều khiển như try, if, switch, whilevv, như thế này:

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

Và vì chúng được xử lý trước mã từng bước được chạy, thật khó để biết phải làm gì khi chúng ở trong cấu trúc điều khiển.

Mặc dù việc này không được chỉ định cho đến ES2015, nhưng đây là một phần mở rộng được phép để hỗ trợ khai báo hàm theo khối. Thật không may (và chắc chắn), các động cơ khác nhau đã làm những điều khác nhau.

Kể từ ES2015, thông số kỹ thuật cho biết phải làm gì. Trong thực tế, nó cung cấp ba điều riêng biệt để làm:

  1. Nếu ở chế độ lỏng lẻo không có trên trình duyệt web, công cụ JavaScript có nghĩa vụ phải làm một việc
  2. Nếu ở chế độ lỏng trên trình duyệt web, công cụ JavaScript có nghĩa vụ phải làm một việc khác
  3. Nếu ở chế độ nghiêm ngặt (trình duyệt hoặc không), công cụ JavaScript có nghĩa vụ phải làm một việc khác

Các quy tắc cho các chế độ lỏng lẻo rất khó, nhưng ở chế độ nghiêm ngặt , việc khai báo hàm trong các khối rất dễ dàng: Chúng cục bộ với khối (chúng có phạm vi khối , cũng mới trong ES2015) và chúng được đưa lên đầu của khối. Vì thế:

"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)

functionBiểu hiện "ẩn danh"

Dạng phổ biến thứ hai được gọi là biểu thức hàm ẩn danh :

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

Giống như tất cả các biểu thức, nó được đánh giá khi nó đạt được trong quá trình thực thi mã từng bước.

Trong ES5, chức năng mà trình tạo này không có tên (nó ẩn danh). Trong ES2015, hàm được gán tên nếu có thể bằng cách suy ra nó từ ngữ cảnh. Trong ví dụ trên, tên sẽ là y. Một cái gì đó tương tự được thực hiện khi hàm là giá trị của bộ khởi tạo thuộc tính. (Để biết chi tiết về thời điểm điều này xảy ra và các quy tắc, hãy tìm kiếm SetFunctionNametrong thông số kỹ thuật  - nó xuất hiện ở mọi nơi.)

functionBiểu hiện được đặt tên

Dạng thứ ba là một biểu thức hàm được đặt tên ("NFE"):

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

Hàm này tạo ra có một tên thích hợp ( wtrong trường hợp này). Giống như tất cả các biểu thức, điều này được đánh giá khi nó đạt được trong quá trình thực thi mã từng bước. Tên của hàm không được thêm vào phạm vi mà biểu thức xuất hiện; Tên nằm trong phạm vi trong chính chức năng:

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

Lưu ý rằng NFE thường là nguồn gây ra lỗi khi triển khai JavaScript. Ví dụ, IE8 và trước đó, xử lý NFE hoàn toàn không chính xác , tạo hai chức năng khác nhau ở hai thời điểm khác nhau. Các phiên bản đầu của Safari cũng có vấn đề. Tin vui là các phiên bản hiện tại của trình duyệt (IE9 trở lên, Safari hiện tại) không còn gặp phải những vấn đề đó nữa. (Nhưng về văn bản này, thật đáng buồn, IE8 vẫn được sử dụng rộng rãi và vì vậy việc sử dụng NFE với mã cho web nói chung vẫn còn có vấn đề.)

Trình khởi tạo chức năng Accessor (ES5 +)

Đôi khi các chức năng có thể lẻn vào phần lớn không được chú ý; đó là trường hợp với các hàm accessor . Đây là một ví dụ:

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"

Lưu ý rằng khi tôi sử dụng chức năng, tôi đã không sử dụng () ! Đó là bởi vì đó là chức năng truy cập cho một tài sản. Chúng tôi nhận và thiết lập tài sản theo cách thông thường, nhưng đằng sau hậu trường, chức năng được gọi.

Bạn cũng có thể tạo các hàm accessor với Object.defineProperty , Object.definePropertiesvà đối số thứ hai ít được biết đến Object.create.

Biểu thức chức năng mũi tên (ES2015 +)

ES2015 mang đến cho chúng ta chức năng mũi tên . Đây là một ví dụ:

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

Xem đó n => n * 2 điều đó ẩn trong map()cuộc gọi? Đó là một chức năng.

Một vài điều về chức năng mũi tên:

  1. Họ không có cái riêng của họ this. Thay vào đó, họ gần hơn với thisbối cảnh nơi chúng được xác định. (Họ cũng đóng cửa argumentsvà, khi có liên quan , super.) Điều này có nghĩa là thisbên trong họ giống nhưthis nơi chúng được tạo và không thể thay đổi.

  2. Như bạn đã nhận thấy ở trên, bạn không sử dụng từ khóa function; thay vào đó, bạn sử dụng =>.

Các n => n * 2ví dụ trên là một trong những hình thức của họ. Nếu bạn có nhiều đối số để truyền hàm, bạn sử dụng parens:

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

(Hãy nhớ rằng Array#mapvượt qua mục nhập làm đối số đầu tiên và chỉ mục là đối số thứ hai.)

Trong cả hai trường hợp, phần thân của hàm chỉ là một biểu thức; giá trị trả về của hàm sẽ tự động là kết quả của biểu thức đó (bạn không sử dụng rõ ràng return).

Nếu bạn đang làm nhiều hơn chỉ một biểu thức, hãy sử dụng {}và rõ ràng return(nếu bạn cần trả về một giá trị), như bình thường:

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));

Phiên bản không có { ... }được gọi là hàm mũi tên có thân biểu thức hoặc thân ngắn gọn . (Ngoài ra: Hàm mũi tên ngắn gọn .) Hàm có { ... }xác định thân là hàm mũi tên có thân hàm . (Ngoài ra: Một chức năng mũi tên dài dòng .)

Khai báo phương thức trong Trình khởi tạo đối tượng (ES2015 +)

ES2015 cho phép một hình thức khai báo một thuộc tính ngắn hơn tham chiếu đến một hàm gọi là định nghĩa phương thức ; nó trông như thế này:

var o = {
    foo() {
    }
};

gần như tương đương trong ES5 và trước đó sẽ là:

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

sự khác biệt (khác với mức độ dài) là một phương thức có thể sử dụng super, nhưng một chức năng thì không thể. Vì vậy, ví dụ, nếu bạn có một đối tượng xác định (nói) valueOfsử dụng cú pháp phương thức, thì nó có thể sử dụng super.valueOf()để lấy giá trị Object.prototype.valueOfsẽ trả về (trước khi có thể làm gì đó với nó), trong khi phiên bản ES5 sẽ phải Object.prototype.valueOf.call(this)thay thế.

Điều đó cũng có nghĩa là phương thức có tham chiếu đến đối tượng mà nó được định nghĩa, vì vậy nếu đối tượng đó là tạm thời (ví dụ: bạn chuyển nó vào Object.assignnhư một trong các đối tượng nguồn), cú pháp phương thức có thể có nghĩa là đối tượng được giữ lại trong bộ nhớ khi không nó có thể là rác được thu thập (nếu công cụ JavaScript không phát hiện ra tình huống đó và xử lý nó nếu không có phương pháp nào sử dụngsuper ).

Trình xây dựng và khai báo phương thức trong class(ES2015 +)

ES2015 mang đến cho chúng ta classcú pháp, bao gồm các hàm tạo và phương thức khai báo:

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

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

Có hai khai báo hàm ở trên: Một cho hàm tạo, lấy tên Personvà một cho getFullName, là hàm được gán cho Person.prototype.


3
Sau đó, tên wchỉ đơn giản là bỏ qua?
BiAiB

8
@PellePenna: Tên hàm hữu ích cho nhiều thứ. Hai biggies theo quan điểm của tôi là đệ quy và tên của hàm được hiển thị trong ngăn xếp cuộc gọi, dấu vết ngoại lệ, v.v.
TJ Crowder

4
@ChaimEliyah - "Chấp nhận không có nghĩa đó là câu trả lời hay nhất, nó chỉ có nghĩa là nó hiệu quả với người hỏi." nguồn
ScrapCode

6
@AR: Hoàn toàn đúng. Mặc dù vậy, thật thú vị, ngay phía trên có ghi "Các câu trả lời hay nhất xuất hiện đầu tiên để chúng luôn dễ tìm." Vì câu trả lời được chấp nhận xuất hiện đầu tiên ngay cả trên các câu trả lời được bình chọn cao hơn, chuyến tham quan có thể hơi mâu thuẫn. ;-) Cũng có một chút không chính xác, nếu chúng tôi xác định "tốt nhất" bằng phiếu bầu (không đáng tin cậy, đó chỉ là những gì chúng tôi có), câu trả lời "tốt nhất" chỉ hiển thị đầu tiên nếu bạn sử dụng tab "Phiếu bầu" - mặt khác, câu trả lời đầu tiên là câu trả lời tích cực hoặc câu trả lời cũ nhất.
TJ Crowder

1
@TJCrowder: Đồng ý. "Sắp xếp theo ngày" đôi khi gây phiền nhiễu.
ScrapCode

144

Nói về bối cảnh toàn cầu, cả hai, vartuyên bố và FunctionDeclarationcuối cùng sẽ tạo ra một tài sản không thể xóa trên đối tượng toàn cầu, nhưng giá trị của cả hai có thể được ghi đè .

Sự khác biệt tinh tế giữa hai cách là khi quá trình Biến đổi tức thời chạy (trước khi thực thi mã thực tế), tất cả các mã định danh được khai báo varsẽ được khởi tạo cùng với undefined, và những cái được sử dụng FunctionDeclarationsẽ có sẵn kể từ thời điểm đó, ví dụ:

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

Sự phân công bar FunctionExpressiondiễn ra cho đến khi thời gian chạy.

Một thuộc tính toàn cầu được tạo bởi một FunctionDeclarationcó thể được ghi đè mà không có bất kỳ vấn đề nào giống như một giá trị biến, ví dụ:

 function test () {}
 test = null;

Một sự khác biệt rõ ràng khác giữa hai ví dụ của bạn là hàm đầu tiên không có tên, nhưng hàm thứ hai có nó, có thể thực sự hữu ích khi gỡ lỗi (tức là kiểm tra ngăn xếp cuộc gọi).

Về ví dụ đầu tiên được chỉnh sửa của bạn ( foo = function() { alert('hello!'); };), đó là một bài tập chưa được khai báo, tôi rất khuyến khích bạn luôn sử dụng vartừ khóa.

Với một phép gán, không có varcâu lệnh, nếu không tìm thấy mã định danh được tham chiếu trong chuỗi phạm vi, nó sẽ trở thành một thuộc tính có thể xóa của đối tượng toàn cục.

Ngoài ra, các bài tập chưa được khai báo sẽ ném ReferenceErrorvào ECMAScript 5 trong Chế độ nghiêm ngặt .

Phải đọc:

Lưu ý : Câu trả lời này đã được hợp nhất từ một câu hỏi khác , trong đó nghi ngờ và quan niệm sai lầm lớn từ OP là các định danh được khai báo với một FunctionDeclaration, không thể được ghi đè không phải là trường hợp.


Tôi không biết rằng các hàm có thể được ghi đè bằng JavaScript! Ngoài ra, lệnh phân tích đó là điểm bán hàng lớn đối với tôi. Tôi đoán tôi cần xem cách tôi tạo các chức năng.
Xeoncross

2
+0 cho bài viết "Tên biểu thức chức năng làm sáng tỏ" khi nó là 404ing. Gương có thể?: Kangax.github.com/nfe
Mr_Chimp

@CMS Đẹp một cái. Hãy nhớ rằng tôi chưa bao giờ nhìn thấy bản gốc vì vậy tôi không biết đó là một tấm gương hay chỉ là một bài viết khác có cùng tiêu đề!
Mr_Chimp

@Mr_Chimp Tôi khá chắc chắn rằng, thewaybackmachine đang nói rằng nó có 302 khi thu thập thông tin và chuyển hướng là liên kết bạn cung cấp.
Giăng

124

Hai đoạn mã bạn đã đăng ở đó, trong hầu hết các mục đích, sẽ hoạt động theo cùng một cách.

Tuy nhiên, sự khác biệt trong hành vi là với biến thể đầu tiên ( var functionOne = function() {}), hàm đó chỉ có thể được gọi sau điểm đó trong mã.

Với biến thể thứ hai ( function functionTwo()), hàm có sẵn cho mã chạy phía trên nơi hàm được khai báo.

Điều này là do với biến thể đầu tiên, hàm được gán cho biến fookhi chạy. Trong lần thứ hai, hàm được gán cho mã định danh đó foo, tại thời điểm phân tích cú pháp.

Thêm thông tin kỹ thuật

JavaScript có ba cách xác định hàm.

  1. Đoạn đầu tiên của bạn hiển thị một biểu thức chức năng . Điều này liên quan đến việc sử dụng toán tử "hàm" để tạo một hàm - kết quả của toán tử đó có thể được lưu trữ trong bất kỳ thuộc tính biến hoặc đối tượng nào. Các biểu thức chức năng là mạnh mẽ theo cách đó. Biểu thức hàm thường được gọi là "hàm ẩn danh", vì nó không phải có tên,
  2. Ví dụ thứ hai của bạn là một khai báo hàm . Cái này sử dụng câu lệnh "function" để tạo một hàm. Hàm này được cung cấp vào lúc phân tích cú pháp và có thể được gọi ở bất cứ đâu trong phạm vi đó. Bạn vẫn có thể lưu trữ nó trong một thuộc tính biến hoặc đối tượng sau này.
  3. Cách thứ ba để xác định hàm là hàm tạo "Hàm ()" , không được hiển thị trong bài viết gốc của bạn. Không nên sử dụng cái này vì nó hoạt động giống như eval()cái có vấn đề của nó.

103

Một lời giải thích tốt hơn cho câu trả lời của Greg

functionTwo();
function functionTwo() {
}

Tại sao không có lỗi? Chúng tôi luôn được dạy rằng các biểu thức được thực hiện từ trên xuống dưới (??)

Bởi vì:

Các khai báo hàm và khai báo biến luôn được di chuyển ( hoisted) vô hình lên đầu phạm vi chứa của chúng bởi trình thông dịch JavaScript. Các tham số chức năng và tên do ngôn ngữ xác định rõ ràng là đã có. anh đào

Điều này có nghĩa là mã như thế này:

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

Lưu ý rằng phần chuyển nhượng của các tờ khai không được nâng lên. Chỉ có cái tên được nâng lên.

Nhưng trong trường hợp với khai báo hàm, toàn bộ thân hàm cũng sẽ được nâng lên :

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

HI suhail cảm ơn cho thông tin rõ ràng về chủ đề chức năng. Bây giờ câu hỏi của tôi là cái nào sẽ là khai báo đầu tiên trong hệ thống phân cấp khai báo cho dù khai báo biến (functionOne) hay khai báo hàm (functionTwo)?
Sharathi RB

91

Các nhà bình luận khác đã đề cập đến sự khác biệt về ngữ nghĩa của hai biến thể ở trên. Tôi muốn lưu ý một sự khác biệt về phong cách: Chỉ có biến thể "gán" mới có thể đặt thuộc tính của một đối tượng khác.

Tôi thường xây dựng các mô-đun JavaScript với một mẫu như thế này:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();

Với mẫu này, tất cả các hàm công khai của bạn sẽ sử dụng phép gán, trong khi các hàm riêng của bạn sử dụng khai báo.

(Cũng lưu ý rằng bài tập nên yêu cầu dấu chấm phẩy sau câu lệnh, trong khi khai báo cấm nó.)


4
yuiblog.com/blog/2007/06/12/module-potype là tài liệu tham khảo nguyên thủy cho mẫu mô-đun, theo như tôi có thể nói. (Mặc dù bài viết đó sử dụng var foo = function(){...}cú pháp ngay cả đối với các biến riêng tư.
Sean McMillan

Điều này không hoàn toàn đúng trong một số phiên bản IE cũ hơn. ( function window.onload() {}là một điều.)
Ry-

77

Một minh họa về thời điểm thích phương thức thứ nhất so với phương thức thứ hai là khi bạn cần tránh ghi đè các định nghĩa trước đó của hàm.

Với

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

, định nghĩa này myfunctionsẽ ghi đè bất kỳ định nghĩa trước đó, vì nó sẽ được thực hiện tại thời điểm phân tích.

Trong khi

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

không đúng công việc xác định myfunctionchỉ khi conditionđược đáp ứng.


1
ví dụ này là tốt và gần với sự hoàn hảo, nhưng có thể được cải thiện. ví dụ tốt hơn sẽ được xác định var myFunc = null;bên ngoài vòng lặp hoặc bên ngoài khối if / otherif / other. Sau đó, bạn có thể điều kiện gán các chức năng khác nhau cho cùng một biến. Trong JS, quy ước tốt hơn là gán giá trị còn thiếu thành null, sau đó không xác định. Do đó, bạn nên khai báo myFactor là null trước, sau đó gán nó sau, theo điều kiện.
Alexander Mills

62

Một lý do quan trọng là thêm một và chỉ một biến là "Root" của không gian tên của bạn ...

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

}

hoặc là

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

Có nhiều kỹ thuật để đặt tên. Nó trở nên quan trọng hơn với rất nhiều mô-đun JavaScript có sẵn.

Xem thêm Làm cách nào để khai báo một không gian tên trong JavaScript?


3
Có vẻ như câu trả lời này đã được hợp nhất vào câu hỏi này từ một câu hỏi khác, và từ ngữ vẻ là một chút nhỏ không liên quan đến câu hỏi này . Bạn có xem xét việc chỉnh sửa câu trả lời để nó có vẻ được định hướng cụ thể hơn cho câu hỏi này không? (để nhắc lại; đây hoàn toàn không phải là lỗi của bạn ... chỉ là tác dụng phụ của câu hỏi được hợp nhất). Bạn cũng có thể xóa nó, và tôi nghĩ bạn sẽ giữ được danh tiếng của mình. Hoặc bạn có thể để nó; vì nó cũ, nó có thể không tạo ra sự khác biệt lớn.
Andrew Barber

55

Tời kéo là hành động của trình thông dịch JavaScript để di chuyển tất cả các khai báo biến và hàm lên trên cùng của phạm vi hiện tại.

Tuy nhiên, chỉ có các tuyên bố thực tế được nâng lên. bằng cách để lại bài tập ở nơi họ đang ở

  • Biến của / Hàm được khai báo bên trong trang là toàn cục có thể truy cập mọi nơi trong trang đó.
  • biến / Hàm được khai báo bên trong hàm đang có phạm vi cục bộ. có nghĩa là chúng có sẵn / được truy cập bên trong thân hàm (phạm vi), chúng không có sẵn bên ngoài thân hàm.

Biến đổi

Javascript được gọi là ngôn ngữ gõ lỏng lẻo. Điều đó có nghĩa là các biến Javascript có thể giữ giá trị của bất kỳ Kiểu dữ liệu nào . Javascript tự động đảm nhiệm việc thay đổi loại biến dựa trên giá trị / nghĩa đen được cung cấp trong thời gian chạy.

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.
};  

Chức năng

function Identifier_opt ( FormalParameterList_opt ) { 
      FunctionBody | sequence of statements

      « return;  Default undefined
      « return 'some data';
}
  • các chức năng được khai báo bên trong trang được kéo lên đầu trang có quyền truy cập toàn cầu.
  • các hàm được khai báo bên trong khối chức năng được nâng lên đỉnh của khối.
  • Giá trị trả về mặc định của hàm là ' không xác định ', giá trị mặc định khai báo biến cũng 'không xác định'

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

Tuyên bố chức năng

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

Biểu thức chức năng

        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'
    };

Hàm được gán cho biến Ví dụ:

(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 được hiểu là

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();

Bạn có thể kiểm tra khai báo hàm, kiểm tra biểu thức trên các trình duyệt khác nhau bằng cách sử dụng jsperf Test Runner


Các lớp chức năng của Trình xây dựng ES5 : Các đối tượng hàm được tạo bằng Function.prototype.bind

JavaScript coi các hàm là các đối tượng hạng nhất, vì vậy là một đối tượng, bạn có thể gán các thuộc tính cho một hàm.

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 đã giới thiệu hàm Mũi tên : Biểu thức hàm mũi tên có cú pháp ngắn hơn, chúng phù hợp nhất cho các hàm không phải phương thức và chúng không thể được sử dụng làm hàm tạo.

ArrowFunction : ArrowParameters => ConciseBody.

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

3
ahm, câu trả lời của bạn ... không phải là mơ hồ sao? được viết tốt mặc dù vậy +1 để chi tiêu và viết quá nhiều thông tin.
Đan Mạch

40

Tôi đang thêm câu trả lời của riêng mình chỉ vì những người khác đã che đậy phần cẩu một cách kỹ lưỡng.

Tôi đã tự hỏi về cách nào là tốt hơn trong một thời gian dài bây giờ, và nhờ http://jsperf.com bây giờ tôi biết :)

nhập mô tả hình ảnh ở đây

Khai báo hàm nhanh hơn và đó là điều thực sự quan trọng trong web dev phải không? ;)


8
Tôi muốn nói rằng khả năng bảo trì là khía cạnh quan trọng nhất của hầu hết các mã. Hiệu suất rất quan trọng, nhưng trong hầu hết các trường hợp, IO có thể là một nút cổ chai lớn hơn theo cách bạn xác định các chức năng của mình. Tuy nhiên, có một số vấn đề mà bạn cần mỗi bit hiệu suất bạn có thể nhận được và điều này rất hữu ích trong những trường hợp đó. Cũng tốt để có một câu trả lời ở đây rằng câu trả lời rõ ràng một phần được xác định rõ của câu hỏi.
Richard Garside

3
Chà, tôi thấy nó khác với Firefox. jsperf.com/sandytest
Sandeep Nayak

Chỉ là một bản cập nhật, vì bây giờ tôi đã có phong cách lập trình chức năng đầy đủ trong JavaScript, tôi không bao giờ sử dụng khai báo, chỉ có các biểu thức hàm để tôi có thể xâu chuỗi và gọi các hàm của mình bằng tên biến của chúng. Hãy xem RamdaJS ...
Leon Gaban

1
@SandeepNayak Tôi chỉ chạy thử nghiệm của riêng bạn trong Firefox 50.0.0 / Windows 7 0.0.0 và nó thực sự giống như của Leon. Vì vậy, nếu thử nghiệm của bạn là chính xác, tôi sẽ kết luận rằng các thử nghiệm của jsperf không phải là chỉ định và tất cả phụ thuộc vào trình duyệt và / hoặc phiên bản HĐH của bạn, hoặc trong trạng thái cụ thể của máy hiện tại trong thời điểm cụ thể đó.
ocramot

33

Một khai báo hàm và một biểu thức hàm được gán cho một biến hoạt động giống nhau sau khi liên kết được thiết lập.

Tuy nhiên, có một sự khác biệt về cách thứcthời điểm đối tượng hàm thực sự được liên kết với biến của nó. Sự khác biệt này là do cơ chế được gọi là cẩu biến trong JavaScript.

Về cơ bản, tất cả các khai báo hàm và khai báo biến được đưa lên đỉnh của hàm trong đó khai báo xảy ra (đây là lý do tại sao chúng ta nói rằng JavaScript có phạm vi hàm ).

  • Khi một khai báo hàm được nâng lên, thân hàm "theo" vì vậy khi thân hàm được ước tính, biến sẽ ngay lập tức được liên kết với một đối tượng hàm.

  • Khi một khai báo biến được nâng lên, việc khởi tạo không tuân theo mà là "bỏ lại phía sau". Biến được khởi tạo undefinedở đầu thân hàm và sẽ được gán một giá trị tại vị trí ban đầu của nó trong mã. (Trên thực tế, nó sẽ được gán một giá trị tại mọi vị trí nơi xảy ra khai báo một biến có cùng tên.)

Thứ tự của cẩu cũng rất quan trọng: khai báo hàm được ưu tiên hơn các khai báo biến có cùng tên và khai báo hàm cuối cùng được ưu tiên hơn các khai báo hàm trước có cùng tên.

Vài ví dụ...

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

Biến foođược kéo lên tới đỉnh của hàm, khởi tạo undefined, do đó !footrue, vì vậy foođược gán 10. Các foobên ngoài barcủa phạm vi không có vai trò và ảnh hưởng.

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

Khai báo hàm được ưu tiên hơn các khai báo biến và khai báo hàm cuối "gậy".

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

Trong ví dụ anày được khởi tạo với đối tượng hàm kết quả từ việc đánh giá khai báo hàm thứ hai và sau đó được gán 4.

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

Ở đây, khai báo hàm được nâng lên trước, khai báo và khởi tạo biến a. Tiếp theo, biến này được chỉ định 10. Nói cách khác: bài tập không gán cho biến ngoài a.


3
Bạn có một cách hơi kỳ lạ để đặt niềng răng đóng cửa. Bạn có phải là một lập trình viên Python? Có vẻ như bạn cố gắng làm cho Javascript trông giống như Python. Tôi sợ nó gây nhầm lẫn cho người khác. Nếu tôi phải duy trì mã JavaScript của bạn, trước tiên tôi sẽ cho phép mã của bạn thông qua một trình tạo đẹp tự động.
nalply

1
Tuyệt vời bài. Một 'chức năng tự thực hiện' hoặc 'biểu hiện chức năng được gọi ngay lập tức' phải đủ dễ thấy và sở thích phong cách của anh ta không nên làm mất đi bài đăng của anh ta - điều này chính xác và tóm tắt 'hoàn hảo'. +1
Ricalsin

32

Ví dụ đầu tiên là một khai báo hàm:

function abc(){}

Ví dụ thứ hai là một biểu thức hàm:

var abc = function() {};

Sự khác biệt chính là cách chúng được nâng lên (nâng và khai báo). Trong ví dụ đầu tiên, toàn bộ khai báo hàm được nâng lên. Trong ví dụ thứ hai, chỉ có var 'abc' được nâng lên, giá trị của nó (hàm) sẽ không được xác định và chính hàm đó vẫn ở vị trí được khai báo.

Nói một cách đơn giản:

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

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

Để nghiên cứu thêm về chủ đề này, tôi thực sự khuyên bạn nên liên kết này


1
Ví dụ của bạn giống như câu trả lời hàng đầu
GôTô

Lý do chính để đăng câu trả lời này là để cung cấp liên kết ở phía dưới. Đây là phần còn thiếu để tôi hiểu đầy đủ câu hỏi trên.
sla55er

1
Thật tuyệt khi bạn muốn chia sẻ liên kết. Nhưng các liên kết đến thông tin bổ sung, trong SO, chỉ nên là một nhận xét về câu hỏi hoặc câu trả lời yêu thích của bạn. Rất tối ưu để làm lộn xộn một trang dài, phức tạp như thế này với thông tin lặp đi lặp lại chỉ để thêm một liên kết hữu ích duy nhất ở cuối trang. Không, bạn sẽ không nhận được điểm đại diện khi cung cấp liên kết, nhưng bạn sẽ giúp cộng đồng.
XML

31

Về chi phí bảo trì mã, các chức năng được đặt tên được ưu tiên hơn:

  • Độc lập với nơi mà chúng được khai báo (nhưng vẫn bị giới hạn bởi phạm vi).
  • Khả năng chống lại các lỗi như khởi tạo có điều kiện hơn (bạn vẫn có thể ghi đè nếu muốn).
  • Mã trở nên dễ đọc hơn bằng cách phân bổ các chức năng cục bộ riêng biệt của chức năng phạm vi. Thông thường trong phạm vi chức năng đi trước, tiếp theo là khai báo các hàm cục bộ.
  • Trong trình gỡ lỗi, bạn sẽ thấy rõ tên hàm trên ngăn xếp cuộc gọi thay vì chức năng "ẩn danh / đánh giá".

Tôi nghi ngờ nhiều PROS cho các chức năng được đặt tên được theo dõi. Và những gì được liệt kê là một lợi thế của các chức năng được đặt tên là một bất lợi cho những người ẩn danh.

Trong lịch sử, các hàm ẩn danh xuất hiện từ việc JavaScript không có khả năng là ngôn ngữ để liệt kê các thành viên có chức năng được đặt tên:

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

2
Có một bài kiểm tra để xác nhận: blog.firsov.net/2010/01/ Kiểm tra hiệu năng của JS - phạm vi và các hàm được đặt tên - Analytics
Sasha Firsov

25

Theo thuật ngữ khoa học máy tính, chúng ta nói về các hàm ẩn danh và các hàm được đặt tên. Tôi nghĩ rằng sự khác biệt quan trọng nhất là một chức năng ẩn danh không bị ràng buộc với một tên, do đó chức năng ẩn danh tên. Trong JavaScript, nó là một đối tượng lớp đầu tiên được khai báo động khi chạy.

Để biết thêm thông tin về các hàm ẩn danh và phép tính lambda, Wikipedia là một khởi đầu tốt ( http://en.wikipedia.org/wiki/Anonymous_feft ).


Kể từ ES2015 (sáu năm rưỡi sau khi câu trả lời của bạn được đăng), cả hai chức năng trong câu hỏi đều được đặt tên.
TJ Crowder

25

Tôi sử dụng cách tiếp cận biến trong mã của mình vì một lý do rất cụ thể, lý thuyết về nó đã được trình bày theo một cách trừu tượng ở trên, nhưng một ví dụ có thể giúp một số người như tôi, với chuyên môn JavaScript hạn chế.

Tôi có mã mà tôi cần để chạy với 160 nhãn hiệu được thiết kế độc lập. Hầu hết các mã nằm trong các tệp được chia sẻ, nhưng các nội dung dành riêng cho thương hiệu nằm trong một tệp riêng biệt, một cho mỗi nhãn hiệu.

Một số nhãn hiệu yêu cầu các chức năng cụ thể, và một số thì không. Đôi khi tôi phải thêm các chức năng mới để làm những việc cụ thể về thương hiệu mới. Tôi rất vui khi thay đổi mã được chia sẻ, nhưng tôi không muốn phải thay đổi tất cả 160 bộ tệp nhãn hiệu.

Bằng cách sử dụng cú pháp biến, tôi có thể khai báo biến (về cơ bản là một con trỏ hàm) trong mã được chia sẻ và gán một hàm sơ khai tầm thường hoặc được đặt thành null.

Sau đó, một hoặc hai nhãn hiệu cần triển khai cụ thể của hàm có thể xác định phiên bản hàm của chúng và gán giá trị này cho biến nếu chúng muốn và phần còn lại không làm gì cả. Tôi có thể kiểm tra hàm null trước khi thực thi nó trong mã được chia sẻ.

Từ ý kiến ​​của mọi người ở trên, tôi thu thập được có thể xác định lại hàm tĩnh, nhưng tôi nghĩ giải pháp biến là tốt và rõ ràng.


25

Câu trả lời của Greg là đủ tốt, nhưng tôi vẫn muốn thêm một cái gì đó vào đó mà tôi vừa học được khi xem Douglas Crockford video .

Biểu thức chức năng:

var foo = function foo() {};

Tuyên bố chức năng:

function foo() {};

Câu lệnh hàm chỉ là một tốc ký cho varcâu lệnh với mộtfunction giá trị.

Vì thế

function foo() {};

mở rộng đến

var foo = function foo() {};

Mà mở rộng hơn nữa để:

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

Và cả hai đều được nâng lên đầu mã.

Ảnh chụp màn hình từ video


7
Xin lỗi nhưng điều này không chính xác - tôi không biết Crockford đang cố nói gì trong slide đó. Cả hai khai báo hàm và biến luôn được nâng lên trên phạm vi của chúng. Sự khác biệt là các phép gán biến (cho dù bạn đang gán nó với một chuỗi, boolean hoặc hàm) không được kéo lên trên trong khi các thân hàm (sử dụng khai báo hàm).
Thomas Heymann


24

Có bốn so sánh đáng chú ý giữa hai khai báo chức năng khác nhau như được liệt kê dưới đây.

  1. Tính khả dụng (phạm vi) của chức năng

Các công việc sau đây vì function add()nằm trong khối gần nhất:

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

function add(a, b){
  return a + b;
}

Điều sau đây không hoạt động vì biến được gọi trước khi giá trị hàm được gán cho biến add.

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

var add=function(a, b){
  return a + b;
}

Các mã trên là giống hệt nhau về chức năng với mã dưới đây. Lưu ý rằng việc gán rõ ràng add = undefinedlà không cần thiết bởi vì đơn giản là thực hiện var add;chính xác như 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;
}

Những điều sau đây không hoạt động bởi vì các siêu var add=lớp 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. (chức năng) .name

Tên của một hàm function thefuncname(){}thefuncname khi nó được khai báo theo cách này.

function foobar(a, b){}

console.log(foobar.name);

var a = function foobar(){};

console.log(a.name);

Ngược lại, nếu một hàm được khai báo là function(){}, các chức năng .name là biến đầu tiên sử dụng để lưu trữ các chức năng.

var a = function(){};
var b = (function(){ return function(){} });

console.log(a.name);
console.log(b.name);

Nếu không có biến nào được đặt thành hàm, thì tên hàm là chuỗi rỗng ( "").

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

Cuối cùng, trong khi biến số được gán cho chức năng ban đầu đặt tên, các biến liên tiếp được đặt thành hàm không thay đổi tên.

var a = function(){};
var b = a;
var c = b;

console.log(a.name);
console.log(b.name);
console.log(c.name);

  1. Hiệu suất

Trong Spidermonkey của V8 và Firefox, có thể có một vài khác biệt biên dịch JIST trong vài micrô giây, nhưng cuối cùng kết quả vẫn giống hệt nhau. Để chứng minh điều này, chúng ta hãy kiểm tra hiệu quả của JSPerf ở microbenchmark bằng cách so sánh tốc độ của hai đoạn mã trống. Các bài kiểm tra JSPerf được tìm thấy ở đây . Và, các bài kiểm tra jsben.ch được tìm thấy ở đây . Như bạn có thể thấy, có một sự khác biệt đáng chú ý khi không nên có. Nếu bạn thực sự là một người thích hiệu suất như tôi, thì có lẽ sẽ đáng giá hơn trong khi bạn cố gắng giảm số lượng biến và hàm trong phạm vi và đặc biệt là loại bỏ đa hình (chẳng hạn như sử dụng cùng một biến để lưu trữ hai loại khác nhau).

  1. Biến đổi

Khi bạn sử dụng vartừ khóa để khai báo một biến, sau đó bạn có thể gán lại một giá trị khác cho biến đó.

(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);
})();

Tuy nhiên, khi chúng ta sử dụng câu lệnh const, tham chiếu biến trở thành bất biến. Điều này có nghĩa là chúng ta không thể gán giá trị mới cho biến. Tuy nhiên, xin lưu ý rằng điều này không làm cho nội dung của biến không thay đổi: nếu bạn làm như vậy const arr = [], thì bạn vẫn có thể làm arr[10] = "example". Chỉ làm một cái gì đó như arr = "new value"hoặc arr = []sẽ ném một lỗi như nhìn thấy dưới đây.

(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);
})();

Thật thú vị, nếu chúng ta khai báo biến là function funcName(){}, thì tính bất biến của biến cũng giống như khai báo với 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);
})();

"Khối gần nhất" là gì

"Khối gần nhất" là "hàm" gần nhất (bao gồm các hàm không đồng bộ, hàm tạo và các hàm tạo không đồng bộ). Tuy nhiên, thật thú vị, một function functionName() {}hành vi giống như var functionName = function() {}khi ở trong một khối không đóng cửa đối với các mặt hàng bên ngoài cho biết đóng cửa. Quan sát.

  • Bình thường 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}

  • Bình thường 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;
}

  • Chức năng

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;
    }
})();

  • Statement (chẳng hạn như if, else, for, while, try/ catch/ finally, switch, do/ while, with)

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;
    }
}

  • Chức năng mũi tên với 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;
    }
})();

  • Chức năng mũi tên với 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;
    }
})();


Đây xứng đáng là câu trả lời được chấp nhận và đánh giá cao nhất
Aaron John Sabu

18

@EugeneLazutkin đưa ra một ví dụ trong đó anh ta đặt tên cho một hàm được gán để có thể sử dụngshortcut() làm tham chiếu nội bộ cho chính nó. John Resig đưa ra một ví dụ khác - sao chép một hàm đệ quy được gán cho một đối tượng khác trong Javascript nâng cao học tập của mình hướng dẫn . Mặc dù việc gán các hàm cho các thuộc tính không hoàn toàn là câu hỏi ở đây, tôi khuyên bạn nên tích cực thử hướng dẫn - chạy mã bằng cách nhấp vào nút ở góc trên bên phải và nhấp đúp vào mã để chỉnh sửa theo ý thích của bạn.

Ví dụ từ hướng dẫn: các cuộc gọi đệ quy trong yell():

Các thử nghiệm thất bại khi đối tượng ninja ban đầu bị loại bỏ. (trang 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?" ); 
}

Nếu bạn đặt tên cho hàm sẽ được gọi đệ quy, các bài kiểm tra sẽ vượt qua. (trang 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." );

17

Một sự khác biệt khác không được đề cập trong các câu trả lời khác là nếu bạn sử dụng chức năng ẩn danh

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

và sử dụng nó như là một constructor như trong

var one = new functionOne();

sau đó one.constructor.namesẽ không được xác định. Function.namekhông chuẩn nhưng được hỗ trợ bởi Firefox, Chrome, các trình duyệt có nguồn gốc Webkit khác và IE 9+.

Với

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

có thể lấy tên của hàm tạo dưới dạng một chuỗi với two.constructor.name.


Tên trong trường hợp đầu tiên sẽ không được định nghĩa bởi vì một mình vô danh chức năng được gán cho một biến. Tôi nghĩ rằng từ ẩn danh đã được phát minh cho những thứ không có tên của chúng được định nghĩa :)
Om Shankar

Trong ví dụ này, hai = new trở thành một hàm toàn cục vì không có var
Waqas Tahir

16

Cái đầu tiên (hàm doS Something (x)) phải là một phần của ký hiệu đối tượng.

Cái thứ hai ( var doSomething = function(x){ alert(x);}) chỉ đơn giản là tạo một hàm ẩn danh và gán nó cho một biến , doSomething. Vì vậy, doS Something () sẽ gọi hàm.

Bạn có thể muốn biết những gì một khai báo hàmbiểu thức chức năng là gì.

Một khai báo hàm định nghĩa một biến hàm được đặt tên mà không yêu cầu gán biến. Khai báo hàm xảy ra dưới dạng các cấu trúc độc lập và không thể được lồng trong các khối không có chức năng.

function foo() {
    return 3;
}

ECMA 5 (13.0) định nghĩa cú pháp là
Mã định danh hàm (OfficialParameterList opt ) {FunctionBody}

Trong điều kiện trên, tên hàm được hiển thị trong phạm vi của nó và phạm vi của cha mẹ của nó (nếu không nó sẽ không thể truy cập được).

Và trong một biểu thức hàm

Một biểu thức hàm định nghĩa một hàm là một phần của cú pháp biểu thức lớn hơn (thường là gán biến). Các hàm được định nghĩa thông qua các biểu thức hàm có thể được đặt tên hoặc ẩn danh. Các biểu thức chức năng không nên bắt đầu với chức năng.

// 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) định nghĩa cú pháp là
hàm Định danh opt (optParameterList opt ) {FunctionBody}


16

Tôi đang liệt kê ra những khác biệt dưới đây:

  1. Một khai báo hàm có thể được đặt bất cứ nơi nào trong mã. Ngay cả khi nó được gọi trước khi định nghĩa xuất hiện trong mã, nó sẽ được thực thi khi khai báo hàm được cam kết với bộ nhớ hoặc theo cách nó được nâng lên, trước khi bất kỳ mã nào khác trong trang bắt đầu thực thi.

    Hãy xem chức năng dưới đây:

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

    Điều này là do, trong khi thực hiện, có vẻ như: -

    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

    Một biểu thức hàm, nếu không được xác định trước khi gọi nó, sẽ dẫn đến một lỗi. Ngoài ra, ở đây định nghĩa hàm tự nó không được di chuyển lên trên cùng hoặc được cam kết vào bộ nhớ như trong các khai báo hàm. Nhưng biến mà chúng ta gán cho hàm được nâng lên và không xác định được gán cho nó.

    Hàm tương tự sử dụng biểu thức hàm:

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

    Điều này là do trong khi thực hiện, nó trông giống như:

    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. Sẽ không an toàn khi viết khai báo hàm trong các khối không có chức năng như nếu chúng không thể truy cập được.

    if (test) {
        function x() { doSomething(); }
    }
  3. Biểu thức chức năng được đặt tên như bên dưới, có thể không hoạt động trong trình duyệt Internet Explorer trước phiên bản 9.

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

1
@Arjun Vấn đề là gì nếu một câu hỏi được hỏi nhiều năm trước? Một câu trả lời không chỉ có lợi cho OP mà còn có khả năng cho tất cả người dùng SO, bất kể khi câu hỏi được hỏi. Và có gì sai khi trả lời các câu hỏi đã có câu trả lời được chấp nhận?
SantiBailors

1
@Arjun bạn đã hiểu việc trả lời các câu hỏi cũ không phải là xấu. Nếu nó là như vậy, SO sẽ có một rào cản như vậy. Hãy tưởng tượng có một sự thay đổi trong API (mặc dù không phải trong bối cảnh của câu hỏi này) và ai đó phát hiện ra nó và cung cấp câu trả lời với API mới, không nên cho phép ?? Cho đến khi và trừ khi câu trả lời không có ý nghĩa và không thuộc về nơi này, nó sẽ được tự động hạ cấp và xóa. Bạn không cần phải bận tâm với nó !!!!
Sudhansu Choudhary

15

Nếu bạn sử dụng các hàm đó để tạo đối tượng, bạn sẽ nhận được:

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

Tôi dường như không thể tái tạo điều này. console.log(objectOne.__proto__);in "functionOne {}" trong bảng điều khiển của tôi. Bất kỳ ý tưởng tại sao điều này có thể là trường hợp?
Mike

Tôi dường như không thể tái tạo nó là tốt.
daremkd

1
Đây là khả năng của trình gỡ lỗi của bạn (để hiển thị "lớp" của đối tượng được ghi lại) và hầu hết những người có thể lấy được tên ngay cả đối với các biểu thức hàm ẩn danh ngày nay. Btw, bạn nên làm rõ rằng không có sự khác biệt về chức năng giữa hai trường hợp.
Bergi

12

Theo lý lẽ "các hàm được đặt tên hiển thị trong dấu vết ngăn xếp", các công cụ JavaScript hiện đại thực sự có khả năng đại diện cho các hàm ẩn danh.

Khi viết bài này, V8, SpiderMonkey, Chakra và Nitro luôn đề cập đến các chức năng được đặt tên theo tên của chúng. Họ hầu như luôn đề cập đến một chức năng ẩn danh bởi định danh của nó nếu có.

SpiderMonkey có thể tìm ra tên của một hàm ẩn danh được trả về từ một hàm khác. Phần còn lại thì không.

Nếu bạn thực sự, thực sự muốn trình lặp của bạn và các cuộc gọi lại thành công hiển thị trong dấu vết, bạn cũng có thể đặt tên cho chúng ...

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

Nhưng đối với hầu hết các phần, nó không đáng để nhấn mạnh hơn.

Khai thác ( Fiddle )

'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

Khỉ nhện

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

Luân xa

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)

Nitro

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

12

Trong JavaScript có hai cách để tạo hàm:

  1. Chức năng khai báo:

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

    Điều này rất cơ bản, tự giải thích, được sử dụng trong nhiều ngôn ngữ và tiêu chuẩn trên toàn họ ngôn ngữ C. Chúng tôi đã khai báo một hàm định nghĩa nó và thực hiện nó bằng cách gọi nó.

    Điều bạn nên biết là các hàm thực sự là các đối tượng trong JavaScript; Trong nội bộ, chúng tôi đã tạo một đối tượng cho hàm trên và đặt cho nó một tên gọi là fn hoặc tham chiếu đến đối tượng được lưu trữ trong fn. Hàm là các đối tượng trong JavaScript; một thể hiện của hàm thực sự là một thể hiện đối tượng.

  2. Biểu thức chức năng:

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

    JavaScript có các hàm hạng nhất, nghĩa là tạo một hàm và gán nó cho một biến giống như bạn tạo một chuỗi hoặc số và gán nó cho một biến. Ở đây, biến fn được gán cho một hàm. Lý do cho khái niệm này là các hàm là các đối tượng trong JavaScript; fn đang trỏ đến thể hiện đối tượng của hàm trên. Chúng tôi đã khởi tạo một hàm và gán nó cho một biến. Nó không thực thi chức năng và gán kết quả.

Tham khảo: Cú pháp khai báo hàm JavaScript: var fn = function () {} vs function fn () {}


1
Còn lựa chọn thứ ba thì var fn = function fn() {...}sao?
chharvey

Xin chào Chharvey, không chắc chắn về câu hỏi của bạn, tôi đoán bạn đang nói về biểu hiện chức năng mà tôi đã đề cập. Tuy nhiên, nếu vẫn còn một số nhầm lẫn chỉ là công phu hơn.
Anoop Rai

vâng tôi đã hỏi về một biểu thức chức năng được đặt tên . nó tương tự như tùy chọn # 2 của bạn ngoại trừ chức năng có mã định danh. thông thường định danh này giống với biến được gán cho nó, nhưng không phải lúc nào cũng như vậy.
chharvey

1
Có Biểu thức hàm được đặt tên tương tự như tùy chọn # 2 của tôi. Có một định danh là không bắt buộc vì nó không được sử dụng. Bất cứ khi nào bạn sẽ thực hiện biểu thức hàm, bạn sẽ sử dụng biến giữ đối tượng hàm. Định danh phục vụ không có mục đích.
Anoop Rai

11

Cả hai đều là những cách khác nhau để xác định một chức năng. Sự khác biệt là cách trình duyệt diễn giải và tải chúng vào bối cảnh thực thi.

Trường hợp đầu tiên là các biểu thức hàm chỉ tải khi trình thông dịch đạt đến dòng mã đó. Vì vậy, nếu bạn làm như sau, bạn sẽ gặp lỗi rằng hàmOne không phải là hàm .

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

Lý do là trên dòng đầu tiên không có giá trị nào được gán cho functionOne và do đó nó không được xác định. Chúng tôi đang cố gắng gọi nó là một chức năng, và do đó chúng tôi đang gặp lỗi.

Trên dòng thứ hai, chúng ta đang gán tham chiếu của một hàm ẩn danh cho functionOne.

Trường hợp thứ hai là khai báo hàm tải trước khi bất kỳ mã nào được thực thi. Vì vậy, nếu bạn làm như sau, bạn sẽ không gặp bất kỳ lỗi nào khi khai báo tải trước khi thực thi mã.

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

11

Về hiệu suất:

Các phiên bản mới được V8giới thiệu một số tối ưu hóa dưới mui xe và cũng vậy SpiderMonkey.

Gần như không có sự khác biệt giữa biểu thức và khai báo.
Biểu hiện chức năng dường như nhanh hơn bây giờ.

Chrome 62.0.3202 Kiểm tra Chrome

FireFox 55 Kiểm tra Firefox

Chrome Canary 63.0.3225 Kiểm tra Canary Chrome


Anonymousbiểu thức chức năng dường như có hiệu suất tốt hơn so với Namedbiểu thức chức năng.


Firefox Chrome Canary ChromeFirefox có tên_anonymous Chrome hoàng yến có tên_anonymous Chrome có tên_anonymous


1
Đúng, sự khác biệt này không đáng kể đến mức hy vọng các nhà phát triển sẽ quan tâm đến cách tiếp cận nào phù hợp hơn với nhu cầu cụ thể của họ thay vì cách nào thể nhanh hơn (bạn sẽ nhận được kết quả jsperf khác nhau trên mỗi lần thử tùy thuộc vào trình duyệt đang làm gì - phần lớn các tác vụ javascript không cần phải quan tâm đến việc tối ưu hóa vi mô ở mức độ này).
mực

@squidbe Không có sự khác biệt. Nhìn vào đây: jsperf.com/empty-tests-performance
Jack Giffin

10

Chúng khá giống nhau với một số khác biệt nhỏ, đầu tiên là một biến được gán cho hàm ẩn danh (Khai báo hàm) và thứ hai là cách thông thường để tạo một hàm trong JavaScript (Khai báo hàm ẩn danh), cả hai đều có cách sử dụng, nhược điểm và ưu điểm :

1. Biểu thức chức năng

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

Biểu thức hàm định nghĩa một hàm là một phần của cú pháp biểu thức lớn hơn (thường là gán biến). Các hàm được xác định thông qua Hàm biểu thức có thể được đặt tên hoặc ẩn danh. Biểu thức chức năng không được bắt đầu với chức năng của nhóm Cameron (do đó các dấu ngoặc đơn xung quanh ví dụ tự gọi bên dưới).

Gán một biến cho một hàm, có nghĩa là không cần Tời, vì chúng ta biết các hàm trong JavaScript có thể Hoist, có nghĩa là chúng có thể được gọi trước khi chúng được khai báo, trong khi các biến cần phải được khai báo trước khi truy cập vào chúng, vì vậy trong trường hợp này chúng ta không thể truy cập hàm trước khi khai báo, cũng có thể là cách bạn viết các hàm của mình, đối với các hàm trả về hàm khác, loại khai báo này có thể có ý nghĩa, trong ECMA6 và ở trên bạn có thể gán hàm này cho hàm mũi tên có thể được sử dụng để gọi các hàm ẩn danh, cũng là cách khai báo này là cách tốt hơn để tạo các hàm Xây dựng trong JavaScript.

2. Tuyên bố chức năng

function functionTwo() {
    // Some code
}

Một khai báo hàm định nghĩa một biến chức năng được đặt tên mà không yêu cầu gán biến. Khai báo hàm xảy ra dưới dạng các cấu trúc độc lập và không thể được lồng trong các khối không có chức năng. Thật hữu ích khi nghĩ về họ như anh em ruột của Tuyên bố biến. Cũng giống như các Tuyên bố biến đổi phải bắt đầu bằng hàm var var, các khai báo hàm phải bắt đầu bằng hàm Chức năng.

Đây là cách gọi bình thường của một hàm trong JavaScript, hàm này có thể được gọi trước khi bạn khai báo nó như trong JavaScript, tất cả các hàm đều được Hoisted, nhưng nếu bạn 'sử dụng nghiêm ngặt' thì điều này sẽ không được như mong đợi, đó là một cách tốt để gọi tất cả các hàm bình thường không lớn trong dòng và cũng không phải là hàm tạo.

Ngoài ra, nếu bạn cần thêm thông tin về cách hoạt động của cẩu trong JavaScript, hãy truy cập liên kết dưới đây:

https://developer.mozilla.org/en-US/docs/Glossary/Ho hiện


1
...also this way of declaring is a better way to create Constructor functions in JavaScript, có thể bạn vui lòng giải thích, tôi tò mò!
Karl Morrison

Một lý do là bởi vì tất cả các hàm Trình xây dựng tích hợp trong JavaScript được tạo như hàm này Số () {[mã gốc]} và bạn không nên nhầm lẫn với các hàm dựng sẵn, cũng tham khảo sau trong trường hợp này an toàn hơn và bạn kết thúc lên mã gọn gàng hơn nhưng không sử dụng cẩu ...
Alireza

8

Đây chỉ là hai cách có thể khai báo hàm và theo cách thứ hai, bạn có thể sử dụng hàm trước khi khai báo.


7

new Function()có thể được sử dụng để truyền cơ thể của hàm trong một chuỗi. Và do đó, điều này có thể được sử dụng để tạo ra các chức năng động. Cũng vượt qua kịch bản mà không thực hiện kịch bản.

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

secondFunction()

Trong khi điều này là tốt và đúng, làm thế nào chính xác điều này một mình liên quan đến câu hỏi được hỏi?
Jack Giffin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.