a

Câu hỏi var functionName = function () {} vs hàm functionName () {}


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 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 được nếu có lý do đằng sau nó hay không.

Hai cách là:

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

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


6059
2017-12-03 11:31


gốc


permadi.com/tutorial/jsFunc/index.html là trang rất tốt về chức năng javascript - uzay95
Liên quan là điều này Xuất sắc bài viết trên Biểu thức hàm được đặt tên. - Phrogz
@CMS tham khảo bài viết này: kangax.github.com/nfe/#expr-vs-decl - Upperstage
Có hai điều bạn cần phải biết: # 1 Trong JavaScript, các khai báo được treo. Điều đó 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 hàm, nó được khai báo nhưng giá trị của nó không được đặt ngay lập tức. Trong khi đó, kể từ khi functionTwo chỉ là một khai báo, nó được đặt ở trên cùng của phạm vi. # 2 functionTwo cho phép bạn truy cập vào thuộc tính name và giúp ích rất nhiều khi cố gắng debug một cái gì đó. - xavierm02
Oh và btw, cú pháp đúng là ";" sau khi chuyển nhượng và không có sau khi tuyên bố. Ví dụ. function f(){} so với var f = function(){};. - xavierm02


Các câu trả lời:


Sự khác biệt là functionOne là biểu thức hàm và do đó chỉ được xác định khi dòng đó đạt được, trong khi functionTwo là một khai báo hàm và được định nghĩa ngay khi hàm hoặc kịch bản xung quanh của nó được thực hiện (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!");
}

Điều này cũng có nghĩa là bạn không thể định nghĩa điều kiện các hàm bằng cách sử dụng các khai báo hàm:

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

Trên thực tế xác định functionThree không phân biệt testgiá trị của - trừ khi use strict có hiệu lực, trong trường hợp đó, nó chỉ gây ra lỗi.


4493
2017-12-03 11:37



@ Greg: Nhân tiện, sự khác biệt không chỉ là chúng được phân tích cú pháp vào những thời điểm khác nhau. Về cơ bản, của bạn functionOne chỉ là một biến có hàm ẩn danh được gán cho nó, trong khi functionTwo thực sự là một hàm được đặt tên. Gọi điện .toString() trên cả hai để thấy sự khác biệt. Điều này là quan trọng trong một số trường hợp, nơi bạn muốn nhận được tên của một hàm lập trình. - Jason Bunting
Có cả hai khác nhau. Cái đầu tiên là function expression thứ hai là một function declaration. Bạn có thể đọc thêm về chủ đề ở đây: javascriptweblog.wordpress.com/2010/07/06/… - Michal Kuklis
@Greg Phần trả lời của bạn liên quan đến thời gian phân tích cú pháp so với thời gian chạy không chính xác. Trong JavaScript, các khai báo hàm không được định nghĩa 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á -> Ngữ cảnh thực thi toàn cục được khởi tạo -> instantiation binding declaration đượ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). - Šime Vidas
Thuật ngữ cho hiện tượng này được gọi là cẩu. - Colin Pear
Câu trả lời này không có gì trên Eugene. Và nó khá gây hiểu lầm với câu lệnh phân tích cú pháp-thời gian chạy so với thời gian chạy. - Griffin


Đầu tiên tôi muốn sửa Greg: function abc(){} cũng bị scoped - tên abc được định nghĩa 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 kiểu:

var xyz = function abc(){};

xyz sẽ được định nghĩa như bình thường, abc khô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ó đang được xác định. Nhưng nó sẽ được định nghĩa 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 các chức năng bí danh trên tất cả các trình duyệt, hãy sử dụng loại khai báo sau:

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

Trong trường hợp này, cả hai xyz và abc là 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 "name" 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 hàm 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ư

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

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

Một lý do khác để sử dụng kiểu kết hợp là sử dụng tên nội bộ ngắn để tự giới thiệu, trong khi cung cấp tên không xung đột dài 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ợ ở chế độ nghiêm ngặt.)

Sâu 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 treo qua return tuyên bố:

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

Đây là một biểu thức hàm:

var xyz = function(){};

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

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

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

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

Tuyên bố 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 minh họa 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 tuyên ngôn "biểu hiện hàm" vì cách này tôi có thể kiểm soát khả năng hiển thị. Khi tôi xác định hàm như

var abc = function(){};

Tôi biết rằng tôi đã xác định chức năng cục bộ. Khi tôi xác định hàm như

abc = function(){};

Tôi biết rằng tôi đã xác định nó trên toàn cầu cung cấp mà tôi đã không xác định abc bất cứ nơi nào trong chuỗi phạm vi. Kiểu định nghĩa này có khả năng đàn hồi 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ể để bạn đoán nơi nó thực sự được xác định, đặ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.


1805
2017-12-03 17:43



Tôi đề cập đến RoBorg nhưng anh ta không tìm được nơi nào. Đơ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
var xyz = function 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) cho tôi "Biến không xác định: abc". - NVI
Nói chung tôi nghĩ rằng bài viết này là một công việc tốt để 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 đồng ý không đồng ý với 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 vì "lợi ích" có vẻ là một lời tuyên bố 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 chung , đúng? ;-) - natlee75
imo một lý do rất lớn để sử dụng chức năng được đặt tên là bởi vì debuggers có thể sử dụng tên để giúp bạn có ý nghĩa của stack cuộc gọi của bạn hoặc ngăn xếp dấu vết. nó hút khi bạn nhìn vào ngăn xếp cuộc gọi và xem "chức năng ẩn danh" 10 cấp sâu ... - goat
@Antimony Một khai báo hàm không giống như một khối. Điều này sẽ giải thích tốt hơn: stackoverflow.com/questions/17409945/… - Cypher


Dưới đây là tóm tắt về các biểu mẫu chuẩn tạo chức năng: (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 kinh điển.)

Điều kiện:

Danh sách nhanh:

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

  • "Vô danh" function Biểu hiện (mặc dù thuật ngữ này, đôi khi tạo ra các hàm có tên)

  • Đã đặt tên function Biểu hiện

  • Trình khởi tạo hàm Accessor (ES5 +)

  • Biểu thức hàm mũi tên (ES2015 +) (mà, giống như các biểu thức hàm ẩn danh, không liên quan đến tên rõ ràng và 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 +)

  • Tuyên bố xây dựng và phương pháp trong class (ES2015 +)

Tuyên bố chức năng

Biểu mẫu đầu tiên là khai báo chức năng, trông như thế này:

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

Một khai báo hàm là một tờ khai; 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).

Khai báo hàm được xử lý khi thực thi nhập vào ngữ cảnh mà nó xuất hiện, trước bất kỳ mã từng bước nào được thực hiện. Hàm mà nó tạo ra được đưa ra mộ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.

Do đượ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, thông số kỹ thuật không bao gồm những gì một công cụ JavaScript nên làm 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, while, v.v., như sau:

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ù làm điều này không quy định cho đến ES2015, đó là tiện ích cho phép để hỗ trợ khai báo hàm trong khối. Thật không may (và chắc chắn), động cơ khác nhau đã làm những việc khác nhau.

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

  1. Nếu ở chế độ lỏng lẻo không phải trên trình duyệt web, công cụ JavaScript phải làm một việc
  2. Nếu ở chế độ lỏng lẻo 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 ở nghiêm khắc chế độ (trình duyệt hay không), công cụ JavaScript có nghĩa vụ phải làm một điều khác

Các quy tắc cho các chế độ lỏng lẻo là khó khăn, nhưng trong nghiêm khắc chế độ, khai báo hàm trong các khối dễ dàng: Chúng là cục bộ cho khối (chúng có phạm vi khối, cũng mới trong ES2015), và chúng được nâng lên đỉnh 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)

"Vô danh" function Biểu hiện

Dạng chung 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 việc thực hiện từng bước của mã.

Trong ES5, hàm này tạo ra không có tên (nó ẩn danh). Trong ES2015, hàm được gán một 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 một 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 SetFunctionName bên trong các đặc điểm kỹ thuật- nó xuất hiện trên hết nơi đo.)

Đã đặt tên function Biểu hiệ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 riêng (w trong 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 việc thực hiện từng bước của mã. Tên hàm là không phải được thêm vào phạm vi mà biểu thức xuất hiện; tên  trong phạm vi bên trong hàm:

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

Lưu ý rằng các NFE thường xuyên là một nguồn lỗi cho việc triển khai JavaScript. IE8 và trước đó, ví dụ, xử lý NFE hoàn toàn không chính xác, tạo hai hàm khác nhau ở hai thời điểm khác nhau. Phiên bản Safari sớm cũng có vấn đề. Tin tốt 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 các vấn đề đó nữa. (Nhưng khi viết bài này, thật đáng buồn, IE8 vẫn còn được sử dụng rộng rãi, và vì vậy việc sử dụng các NFE với mã cho web nói chung vẫn còn có vấn đề.)

Trình khởi tạo hàm 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 chức năng truy cập. Đâ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 hàm, tôi đã không sử dụng ()! Đó là bởi vì nó là một chức năng truy cập cho một tài sản. Chúng tôi nhận được và thiết lập các 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 truy cập với Object.defineProperty, Object.definePropertiesvà đối số thứ hai ít được biết đến hơn Object.create.

Biểu thức hàm mũi tên (ES2015 +)

ES2015 mang đến cho chúng tôi 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() gọi điện? Đó 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ủa riêng mình this. Thay vào đó họ đóng lại các this trong bối cảnh chúng được xác định. (Họ cũng đóng cửa arguments và, nếu có liên quan, super.) Điều này có nghĩa là this bên trong chúng cũng 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 * 2 ví dụ trên là một dạng của chúng. Nếu bạn có nhiều đối số để chuyển hàm, bạn sử dụng các dấu ngoặc đơn:

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

(Nhớ lấy Array#map chuyển mục nhập làm đối số đầu tiên và chỉ mục làm 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 một cách rõ ràng return).

Nếu bạn đang làm nhiều hơn chỉ là 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à một chức năng mũi tên với một thân thể biểu đạt hoặc là cơ thể ngắn gọn. (Vừa là ngắn gọn chức năng mũi tên.) { ... } xác định cơ thể là một chức năng mũi tên với một chức năng cơ thể. (Vừa là dài dòng chức năng mũi tên.)

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

ES2015 cho phép một dạng khai báo ngắn hơn một thuộc tính tham chiếu đến một hàm; nó trông như thế này:

var o = {
    foo() {
    }
};

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

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

Tuyên bố xây dựng và phương pháp trong class (ES2015 +)

ES2015 mang đến cho chúng tôi class cú 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 khai báo hàm tạo tên Personvà một cho getFullName, là một hàm được gán cho Person.prototype.


544
2018-03-04 13:35



sau đó tên w chỉ đơn giản là bị bỏ qua? - BiAiB
@ PellePenna: Tên hàm rất hữu ích cho rất nhiều thứ. Hai biggies trong quan điểm của tôi là đệ quy, và tên của chức năng được hiển thị trong ngăn xếp cuộc gọi, dấu vết ngoại lệ, và như vậy. - T.J. Crowder
Điều này bây giờ sẽ là câu trả lời được chấp nhận. Nó gần đây hơn cái ở trên. - Chaim Eliyah
@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ó làm việc cho người hỏi." nguồn - ScrapCode
@ A.R .: Khá đúng. Tuy nhiên, thật thú vị, ngay phía trên nó nói rằng "Những câu trả lời hay nhất hiện lên trước 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ầu chọn cao hơn, chuyến tham quan có thể hơi tự mâu thuẫn. ;-) Ngoài ra một chút không chính xác, nếu chúng tôi xác định "tốt nhất" theo 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ị trước nếu bạn đang sử dụng tab "Phiếu bầu" - nếu không, các câu trả lời đầu tiên là các hoạt động hoặc câu trả lời lâu đời nhất. - T.J. Crowder


Nói về bối cảnh toàn cầu, cả hai, var tuyên bố và một FunctionDeclaration ở cuối sẽ tạo ra không thể xóa thuộc tính trên đối tượng toàn cục, 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 Biến thể tức thì quá trình chạy (trước khi thực thi mã thực tế) tất cả các số nhận dạng được khai báo với var sẽ được khởi tạo với undefinedvà những cái được sử dụng bởi 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 của bar  FunctionExpression diễn ra cho đến khi thời gian chạy.

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

 function test () {}
 test = null;

Một khác biệt rõ ràng 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ó, điều này có thể thực sự hữu ích khi gỡ lỗi (nghĩa là kiểm tra một ngăn xếp cuộc gọi).

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

Với nhiệm vụ, không có var tuyên bố, nếu số nhận dạng được tham chiếu không được tìm thấy trong chuỗi phạm vi, nó sẽ trở thành có thể xóa tài sản của đối tượng toàn cục.

Ngoài ra, các bài tập không khai báo sẽ ném một ReferenceError trên ECMAScript 5 dưới chế độ nghiêm ngặt.

A phải đọc:

chú thích: Câu trả lời này đã được hợp nhất từ câu hỏi khác, trong đó nghi ngờ chính và quan niệm sai lầm từ OP là các số nhận dạng được khai báo với FunctionDeclaration, không thể được ghi đè mà không phải là trường hợp.


133
2017-08-08 19:32



Tôi không biết rằng các chức năng có thể được ghi đè bằng JavaScript! Ngoài ra, thứ tự phân tích cú pháp đó là điểm bán hàng lớn đối với tôi. Tôi đoán tôi cần phải xem cách tôi tạo ra các chức năng. - Xeoncross
+0 đến "Các biểu thức chức năng tên được làm sáng tỏ" bài viết vì nó là 404ing. Gương có thể ?: kangax.github.com/nfe - Mr_Chimp
@CMS Đẹp nhất. Ghi nhớ mặc dù tôi không bao giờ nhìn thấy bản gốc vì vậy tôi không biết nếu đó là một tấm gương hoặc chỉ là một bài viết với cùng một tiêu đề! - Mr_Chimp
@Mr_Chimp Tôi khá chắc chắn nó là, thewaybackmachine nói rằng nó có một 302 tại thời gian thu thập dữ liệu và chuyển hướng là liên kết bạn cung cấp. - John


Hai đoạn mã bạn đã đăng ở đó sẽ cho hầu như tất cả các mục đích, 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() {}), chức năng đó 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 ở 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 foo trong thời gian chạy. Trong lần thứ hai, hàm được gán cho số nhận dạng đó, foo, tại thời gian phân tích cú pháp.

Thông tin kỹ thuật khác

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

  1. Đoạn mã đầu tiên của bạn hiển thị biểu thức hàm. Đ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ỳ biến hoặc thuộc tính đối tượng nào. Biểu thức hàm là mạnh mẽ theo cách đó. Biểu thức hàm thường được gọi là "hàm ẩn danh", bởi vì nó không phải có tên,
  2. Ví dụ thứ hai của bạn là khai báo chức năng. Điều này sử dụng câu lệnh "hàm" để tạo một hàm. Hàm này có sẵn ở thời gian phân tích cú pháp và có thể được gọi ở bất kỳ đâ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 để định nghĩa một hàm là Hàm tạo hàm "Function ()", không được hiển thị trong bài đăng gốc của bạn. Bạn không nên sử dụng nó vì nó hoạt động theo cách tương tự eval(), có vấn đề của nó.

111
2018-04-20 04:54





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ì:

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 họ bằng trình thông dịch JavaScript. Các thông số chức năng và tên được xác định theo ngôn ngữ rõ ràng là đã có. ben cherry

Đ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 khai báo không được treo. Chỉ có tên được treo.

Nhưng trong trường hợp có khai báo hàm, toàn bộ phần thân hàm cũng sẽ được hoisted:

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

91
2017-08-09 02:45



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


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

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();

Với mẫu này, các chức năng công cộng của bạn sẽ sử dụng nhiệm vụ, trong khi các chức năng riêng của bạn sử dụng khai báo.

(Lưu ý rằng nhiệm vụ phải yêu cầu một dấu chấm phẩy sau khi tuyên bố, trong khi tuyên bố cấm nó.)


83
2018-03-03 19:19



yuiblog.com/blog/2007/06/12/module-pattern là tham chiếu 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 hoàn toàn không đúng trong một số phiên bản cũ của IE. (function window.onload() {} là một điều.) - Ry-♦


Một minh họa khi thích phương pháp đầu tiên cho phương thức thứ hai là khi bạn cần tránh ghi đè các định nghĩa trước đó của một hàm.

Với

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

, định nghĩa này của myfunction sẽ ghi đè lên bất kỳ định nghĩa nào trước đó, vì nó sẽ được thực hiện tại thời gian phân tích cú pháp.

Trong khi

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

công việc chính xác của việc xác định myfunction chỉ khi condition được đáp ứng.


68
2018-03-29 13:26



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 / elseif / else. Sau đó, bạn có thể gán các hàm khác nhau cho cùng một biến điều kiện. Trong JS, nó là một quy ước tốt hơn để gán một giá trị còn thiếu cho null, sau đó là không xác định. Do đó, bạn nên khai báo myFunction là null trước, sau đó gán nó sau, có điều kiện. - Alexander Mills


Một lý do quan trọng là thêm một và chỉ một biến là "Gốc" 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 không gian. Nó trở nên quan trọng hơn với rất nhiều các mô-đun JavaScript có sẵn.

Cũng thấy Làm thế nào để khai báo một không gian tên trong JavaScript?


55
2017-08-08 19:44



Có vẻ 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ữ có thể dường như là một chút không liên quan đến điều này câu hỏi. Bạn có xem xét chỉnh sửa câu trả lời để nó có vẻ hướng cụ thể hơn vào câu hỏi này không? (để nhắc lại; đây không phải là lỗi của bạn chút nào ... chỉ là một tác dụng phụ của một câu hỏi được kết hợp). 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ể bỏ nó; vì nó cũ, nó có thể không tạo nên sự khác biệt lớn. - Andrew Barber


Cẩu  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 tới đầu phạm vi hiện tại. 

Tuy nhiên, chỉ những tờ khai thực tế mới được treo. bằng cách rời khỏi bài tập họ đang ở đâu.

  • biến của / Chức năng được khai báo bên trong trang là toàn cầu có thể truy cập bất cứ nơi nào trong trang đó.
  • biến / Hàm được khai báo bên trong hàm có phạm vi cục bộ. có nghĩa là chúng có sẵn / 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

Javascript được gọi là ngôn ngữ được nhập lỏng lẻo. Có nghĩa là các biến Javascript có thể giữ giá trị của bất kỳ Loại dữ liệu. Javascript tự động quản lý việc thay đổi kiểu biến dựa trên giá trị / chữ đượ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 hàm được khai báo bên trong trang được treo lên đầu trang có quyền truy cập toàn cục.
  • các hàm được khai báo bên trong khối chức năng được treo lên đầu khối.
  • Giá trị trả về mặc định của hàm là 'chưa xác định', Biến giá trị mặc định khai báo 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 hàm

        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 hàm xây dựng ES5: Các đối tượng hàm được tạo bằng Function.prototype.bind

JavaScript xử lý các hàm như 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 đặ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 được giới thiệu Chức năng 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 với các hàm phi phương thức và chúng không thể được sử dụng làm các 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

47
2018-01-25 14:46



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


Tôi đang thêm câu trả lời của riêng mình chỉ vì mọi người khác đã che kín phần cẩu.

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

enter image description here

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


34
2018-05-01 15:06



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 năng là 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 định nghĩa 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 mà câu trả lời rõ ràng là một phần được xác định rõ ràng của câu hỏi. - Richard Garside
Vâng, tôi thấy nó là một cách khác xung quanh với Firefox. jsperf.com/sandytest - Sandeep Nayak
Microbenchmarks luôn thất bại. Đang tìm đến jsperf.com là mất thời gian. Những gì bạn thực sự cần là nhìn vào mã nguồn của JS engine, tài liệu chính thức hoặc ít nhất là sniff dev blog hoặc danh sách thư. - gavenkoa
Chỉ cần cập nhật, vì tôi đã có đầy đủ các chức năng lập trình hàm trong JavaScript, tôi không bao giờ sử dụng các khai báo, chỉ các biểu thức hàm để tôi có thể chuỗi và gọi các hàm của mình bằng các tên biến của chúng. Kiểm tra RamdaJS ... - Leon Gaban
@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ự là giống như cách 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 bài kiểm tra của jsperf không phải là chỉ dẫn, và tất cả phụ thuộc vào trình duyệt và / hoặc phiên bản hệ điều hành 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