Tại sao các tên hàm JavaScript của tôi xung đột?


97

Tôi đã viết đoạn mã sau chỉ để xem điều gì sẽ xảy ra khi một biến và một hàm có chức năng được gán cho nó có tên xung đột:

var f = function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

Đầu ra tôi nhận được là "Bản gốc của tôi". Tại sao hàm khác không được gọi?

Ngoài ra, nếu tôi thay đổi bài tập ban đầu của mình thành var f = new function() {, tôi nhận được "Tôi là bản gốc", theo sau là câu nói TypeError object is not a function. Ai đó có thể vui lòng giải thích?


26
@ Dean.DePue - Không có gì nhầm lẫn về phần JavaScript. Các quy tắc xử lý chúng khá rõ ràng (và được Benjamin giải thích trong câu trả lời của mình).
Quentin

4
Tò mò, vẫn là cách tốt nhất để học về một ngôn ngữ. :-D
Cerbrus

2
Ngoài ra, tôi tưởng tượng rằng thật không thể để một thứ gì đó phi vật chất như "JavaScript" "cảm thấy" bối rối (hoặc bất kỳ cảm xúc nào đối với vấn đề đó) ;-)
Cerbrus

2
Tại sao cần cẩu ngược lại thứ tự trong ví dụ thứ hai?
Cerbrus

5
Các bước để nâng cao kiến ​​thức về javascript: 1) Sử dụng 'sử dụng nghiêm ngặt' 2) Luôn sử dụng jslint hoặc jshint 3) Tra cứu những điều mà jslint hoặc jshint phàn nàn về 4) Rửa sạch và lặp lại
steve-er-rino

Câu trả lời:


170

Khai báo hàm được kéo (chuyển lên trên cùng) trong JavaScript. Mặc dù không chính xác về thứ tự phân tích cú pháp, mã bạn có về mặt ngữ nghĩa giống như mã sau vì khai báo hàm được lưu trữ:

function f() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Lần lượt, ngoại trừ tên của hàm giống như:

var f = function() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Do đó, do cẩu biến đổi giống như:

var f;
f = function() {
    console.log("Me duplicate.");
}
f = function() {
    console.log("Me original.");
}

f();

Điều này giải thích những gì bạn nhận được, bạn đang ghi đè chức năng. Nói chung, nhiều varkhai báo được cho phép trong JavaScript - var x = 3; var x = 5hoàn toàn hợp pháp. Trong tiêu chuẩn ECMAScript 6 mới, các letcâu lệnh cấm điều này.

Bài viết này của @kangax thực hiện một công việc tuyệt vời trong việc giải mã các hàm trong javascript


2
Bạn có thể thực sự đơn giản hóa function f()đến var f = function()mức đó không? Có phải tên chức năng và tên gọi thực sự là sự khác biệt duy nhất ?
djechlin

6
@djechlin trong ngữ cảnh của câu hỏi này - có. Nói chung, nó tinh tế hơn - xem stackoverflow.com/questions/336859/… . Từ góc độ trình biên dịch, chúng khác nhau - nhưng từ góc độ lập trình viên - chúng tôi đủ gần để khẳng định điều đó. Đó là lý do tại sao tôi thêm dài "trong khi không chính xác về thứ tự phân tích cú pháp, mã bạn có về ngữ nghĩa giống như" thay vì nói "giống như". Điểm tốt.
Benjamin Gruenbaum

1
@dotslash vui lòng không chỉnh sửa câu hỏi ban đầu của bạn và thay đổi câu hỏi, điều đó được coi là có cách cư xử không tốt ở đây - ngoài ra, trộn nhiều câu hỏi vào một câu hỏi cũng được coi là có cách cư xử xấu ở đây. Thay vào đó, bạn có thể đặt một câu hỏi mới hoặc nếu bạn cho rằng nó quá nhỏ, hãy yêu cầu giải thích rõ ràng trong các nhận xét (dù sao thì đó cũng là lý do của chúng). Trong đoạn mã trên, cả hai phiên bản fđược nâng lên và "Me Original"phiên bản chỉ được nâng sau đó , chúng đều được chuyển lên đầu nhưng theo cùng một thứ tự. Tôi chỉ muốn nói thêm rằng nói chung, bạn không nên đặt tên một số chức năng cùng một cách :)
Benjamin Gruenbaum

5
Trong chế độ nghiêm ngặt, bạn không thể vartrùng tên hai lần trong cùng một phạm vi.
Hoffmann

4
"Điều đó hẳn là hiển nhiên" - có lẽ đối với bạn , nhưng nó không rõ ràng đối với tôi ở một thời điểm, và OP cũng không rõ ràng khi anh ấy hỏi nó, và việc đặt tên, và nói chung hơn là cách môi trường từ vựng được quản lý trong JavaScript là một những điều khó nắm bắt nhất khi lần đầu tiên học JavaScript đối với tôi. Tôi sẽ không nhanh chóng xúc phạm những người không hiểu nó.
Benjamin Gruenbaum,

10

Nếu có vẻ như không có ai trả lời câu hỏi tiếp theo của bạn, vì vậy tôi sẽ trả lời nó ở đây, mặc dù bạn thường đặt các câu hỏi tiếp theo dưới dạng các câu hỏi riêng biệt.

Bạn đã hỏi tại sao điều này:

var f = new function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

in ra "Bản gốc của tôi." và sau đó là một lỗi.

Điều đang xảy ra ở đây là newnguyên nhân khiến hàm được sử dụng như một hàm tạo. Vì vậy, điều này tương đương với sau:

function myConstructor() {
    console.log("Me original.");
}
var f = new myConstructor();

function f() {
    console.log("Me duplicate.");
}

f();

Và nhờ chức năng nâng đỡ mà Benjamin đã giải thích, phần trên về cơ bản tương đương với cái này:

var myConstructor = function() {
    console.log("Me original.");
};
var f = function() {
    console.log("Me duplicate.");
};

f = new myConstructor();

f();

Biểu thức này:

var f = new function() {
    console.log("Me original.");
}

khiến một đối tượng mới được xây dựng và gán cho f, bằng cách sử dụng một hàm ẩn danh làm phương thức khởi tạo. "Tôi nguyên bản." được in ra khi hàm tạo thực thi. Nhưng đối tượng được xây dựng tự nó không phải là một hàm, vì vậy khi điều này cuối cùng thực thi:

f();

bạn gặp lỗi, vì fkhông phải là một hàm.


Ồ, tuyệt vời! Cảm ơn rất nhiều vì đã bỏ qua khó khăn để trả lời nó! :) :)
ankush981

2

Thứ lỗi cho tôi nếu đây là cách tiếp cận sai để thêm điểm. Tôi đã không ở đây nhiều, và sẽ hoan nghênh sự chỉ đạo và / hoặc chỉ trích mang tính xây dựng.

Câu trả lời của Benjamin giải quyết câu hỏi của OP một cách xuất sắc, nhưng tôi muốn thêm một điều chỉnh sẽ cho chúng ta một chuyến tham quan đầy đủ về việc nâng cẩu và những điều kỳ lạ của nó.

Nếu chúng ta bắt đầu mã gốc bằng lệnh gọi tới f, như sau:

f();

var f = function() {
   console.log("Me original.");
};

function f() {
   console.log("Me duplicate.");
}

f();

Đầu ra sau đó sẽ là:

Me duplicate.
Me original.

Lý do là varvà các functiontuyên bố được lưu trữ theo những cách hơi khác nhau.

Đối với varcác tờ khai được chuyển đến phía trên của phạm vi * hiện tại, nhưng bất kỳ chuyển nhượng không được kéo lên. Theo như giá trị của var đã khai báo, nó không được xác định cho đến khi đạt đến dòng gán ban đầu.

Đối với các functioncâu lệnh , cả khai báo định nghĩa đều được lưu trữ. Biểu thức hàm , như được sử dụng trong var f = function() {...cấu trúc, không được nâng lên.

Vì vậy, sau khi lưu trữ, việc thực thi giống như mã là:

var f; // declares var f, but does not assign it.

// name and define function f, shadowing the variable
function f() { 
  console.log("Me duplicate.");
}

// call the currently defined function f
f(); 

// assigns the result of a function expression to the var f,
// which shadows the hoisted function definition once past this point lexically
f = function() { 
  console.log("Me original."); 
}

// calls the function referenced by the var f
f();

* Tất cả phạm vi JavaScript đều là từ vựng, hoặc hàm, phạm vi, nhưng có vẻ như nó sẽ khiến mọi thứ nhầm lẫn khi sử dụng từ f vào thời điểm đó.

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.