เหตุใดชื่อฟังก์ชัน JavaScript ของฉันจึงไม่ตรงกัน


97

ฉันเขียนสคริปต์ต่อไปนี้เพื่อดูว่าเกิดอะไรขึ้นเมื่อตัวแปรและฟังก์ชันที่มีฟังก์ชันที่กำหนดให้มีชื่อของพวกเขาขัดแย้งกัน:

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

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

f();

ผลลัพธ์ที่ได้คือ "ฉันเป็นต้นฉบับ" เหตุใดจึงไม่เรียกฟังก์ชันอื่น ๆ

นอกจากนี้ถ้าผมเปลี่ยนการกำหนดเดิมของฉันไปvar f = new function() {ฉันได้รับ "ฉันต้นฉบับ" ตามด้วย TypeError object is not a functionพูด ใครช่วยอธิบายหน่อยได้ไหม


26
@ Dean.DePue - ส่วนของ JavaScript ไม่มีความสับสน กฎสำหรับการจัดการนั้นค่อนข้างชัดเจน (และเบนจามินอธิบายในคำตอบของเขา)
Quentin

4
ความอยากรู้อยากเห็นยังคงเป็นวิธีที่ดีที่สุดในการเรียนรู้ภาษา :-D
Cerbrus

2
นอกจากนี้ฉันคิดว่ามันเป็นไปไม่ได้เลยที่บางสิ่งที่ไร้สาระอย่าง "JavaScript" จะ "รู้สึก" สับสน (หรืออารมณ์ใด ๆ สำหรับเรื่องนั้น) ;-)
Cerbrus

2
เหตุใดจึงควรชักรอกกลับลำดับในตัวอย่างที่สอง
Cerbrus

5
ขั้นตอนในการเพิ่มพูนความรู้เกี่ยวกับจาวาสคริปต์: 1) ใช้ 'ใช้อย่างเข้มงวด' 2) ใช้ jslint หรือ jshint เสมอ 3) ค้นหาสิ่งที่ jslint หรือ jshint บ่น 4) ล้างและทำซ้ำ
steve-er-rino

คำตอบ:


170

การประกาศฟังก์ชันถูกยกขึ้น (ย้ายไปด้านบน) ใน JavaScript แม้ว่าจะไม่ถูกต้องในแง่ของลำดับการแยกวิเคราะห์ แต่รหัสที่คุณมีจะตรงกับความหมายต่อไปนี้เนื่องจากการประกาศฟังก์ชันจะถูกยกขึ้น:

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


f();

ซึ่งในทางกลับกันยกเว้นชื่อของฟังก์ชันจะเหมือนกับ:

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


f();

ซึ่งในทางกลับกันเนื่องจากการยกแบบแปรผันจึงเหมือนกับ:

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

f();

ซึ่งอธิบายถึงสิ่งที่คุณได้รับคุณกำลังลบล้างฟังก์ชัน โดยทั่วไปvarอนุญาตให้มีการประกาศหลายรายการใน JavaScript ซึ่งvar x = 3; var x = 5เป็นสิ่งที่ถูกกฎหมายอย่างสมบูรณ์ ในมาตรฐาน ECMAScript 6 ใหม่letข้อความห้ามสิ่งนี้

บทความนี้โดย @kangax ทำงานได้อย่างยอดเยี่ยมในฟังก์ชั่น demystifying ใน javascript


2
จริงๆคุณสามารถลดความซับซ้อนของfunction f()การvar f = function()มากว่า? ชื่อรอกและชื่อฟังก์ชันเป็นความแตกต่างเพียงอย่างเดียวหรือไม่?
djechlin

6
@djechlin ในบริบทของคำถามนี้ - ใช่ โดยทั่วไปจะลึกซึ้งยิ่งขึ้น - เห็นstackoverflow.com/questions/336859/... จากมุมมองของคอมไพเลอร์พวกมันแตกต่างกัน - แต่จากมุมมองของโปรแกรมเมอร์ - เราอยู่ใกล้มากพอที่จะอ้างได้ นั่นเป็นเหตุผลว่าทำไมฉันจึงเพิ่มความยาวนั้น "ในขณะที่ไม่ถูกต้องในแง่ของลำดับการแยกวิเคราะห์รหัสที่คุณมีจะเหมือนกับ" แทนที่จะพูด "เหมือนกับ" จุดดี.
Benjamin Gruenbaum

1
@dotslash โปรดอย่าแก้ไขคำถามเดิมของคุณและเปลี่ยนมันถือว่าเป็นมารยาทที่ไม่ดีที่นี่ - นอกจากนี้การผสมคำถามหลาย ๆ คำถามเข้าด้วยกันก็ถือว่าเป็นมารยาทที่ไม่ดีเช่นกัน คุณสามารถถามคำถามใหม่แทนหรือหากคุณคิดว่ามันน้อยเกินไปให้ขอคำชี้แจงในความคิดเห็น (นั่นคือสิ่งที่พวกเขาต้องการอยู่ดี) ในรหัสด้านบนทั้งสองเวอร์ชันของfget hoisted และ"Me Original"เวอร์ชันที่เพิ่งถูกยกขึ้นในภายหลังพวกเขาแต่ละคนจะถูกย้ายไปที่ด้านบน แต่อยู่ในลำดับเดียวกัน ฉันแค่อยากจะเพิ่มว่าโดยทั่วไปคุณไม่ควรตั้งชื่อฟังก์ชันหลายอย่างในลักษณะเดียวกัน :)
Benjamin Gruenbaum

5
ในโหมดเข้มงวดคุณไม่สามารถvarใช้ชื่อเดียวกันซ้ำสองครั้งในขอบเขตเดียวกันได้
Hoffmann

4
"นั่นควรจะชัดเจน" - สำหรับคุณบางที แต่มันก็ไม่ชัดเจนสำหรับฉันในตอนนั้นและมันก็ไม่ชัดเจนสำหรับ OP เมื่อเขาถามและการตั้งชื่อและโดยทั่วไปแล้ววิธีการจัดการสภาพแวดล้อมคำศัพท์ใน JavaScript เป็นอย่างหนึ่ง สิ่งที่ยากที่สุดที่จะเข้าใจเมื่อเรียน JavaScript ครั้งแรกกับฉัน ฉันจะไม่ด่วนดูถูกคนที่ไม่เข้าใจมัน
Benjamin Gruenbaum

10

หากดูเหมือนไม่มีใครตอบคำถามติดตามของคุณฉันจะตอบที่นี่แม้ว่าโดยทั่วไปคุณควรถามคำถามติดตามผลเป็นคำถามแยกต่างหาก

คุณถามว่าทำไมถึงเป็นแบบนี้:

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

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

f();

พิมพ์ "Me original" แล้วเกิดข้อผิดพลาด

สิ่งที่เกิดขึ้นที่นี่คือnewสาเหตุที่ฟังก์ชันถูกใช้เป็นตัวสร้าง ดังนั้นสิ่งนี้จึงเทียบเท่ากับสิ่งต่อไปนี้:

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

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

f();

และต้องขอบคุณฟังก์ชั่นยกที่เบนจามินอธิบายข้างต้นนั้นเทียบเท่ากับสิ่งนี้:

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

f = new myConstructor();

f();

นิพจน์นี้:

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

ทำให้อ็อบเจ็กต์ใหม่ถูกสร้างและกำหนดให้fโดยใช้ฟังก์ชันที่ไม่ระบุชื่อเป็นตัวสร้าง "ฉันเป็นคนเดิม" ถูกพิมพ์ออกมาในขณะที่ตัวสร้างดำเนินการ แต่อ็อบเจ็กต์ที่สร้างขึ้นนั้นไม่ใช่ฟังก์ชันดังนั้นเมื่อดำเนินการในที่สุด:

f();

คุณได้รับข้อผิดพลาดเนื่องจากfไม่ใช่ฟังก์ชัน


โอ้วิเศษมาก! ขอบคุณมากที่ตอบปัญหา! :) :)
ankush981

2

ยกโทษให้ฉันหากนี่เป็นวิธีที่ผิดในการเพิ่มประเด็น ฉันไม่ได้อยู่แถวนี้มากนักและยินดีต้อนรับการชี้นำและ / หรือคำวิจารณ์ที่สร้างสรรค์

คำตอบของเบนจามินตอบคำถามของ OP ได้อย่างดีเยี่ยม แต่ฉันต้องการเพิ่มการปรับแต่งหนึ่งอย่างที่จะทำให้เราได้รับชมการชักรอกและความแปลกประหลาดของมัน

หากเราเริ่มต้นรหัสเดิมด้วยการเรียกร้องfให้ทำดังนี้:

f();

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

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

f();

ผลลัพธ์จะเป็น:

Me duplicate.
Me original.

เหตุผลที่เป็นเช่นนั้นvarและfunctionข้อความถูกยกขึ้นในรูปแบบที่แตกต่างกันเล็กน้อย

สำหรับvarการประกาศถูกย้ายไปที่ด้านบนสุดของขอบเขตปัจจุบัน * แต่การมอบหมายใด ๆจะไม่ถูกยกขึ้น เท่าที่ค่าของ var ที่ประกาศไปจะไม่มีการกำหนดจนกว่าจะถึงบรรทัดการกำหนดเดิม

สำหรับfunctionข้อความทั้งคำประกาศและคำจำกัดความจะถูกยกขึ้น นิพจน์ฟังก์ชันที่ใช้ในvar f = function() {...โครงสร้างจะไม่ถูกยกขึ้น

ดังนั้นหลังจากการชักรอกการดำเนินการจะเหมือนกับว่ารหัสคือ:

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

* ขอบเขต JavaScript ทั้งหมดเป็นศัพท์หรือฟังก์ชันขอบเขต แต่ดูเหมือนว่ามันจะสับสนในการใช้คำ f ณ จุดนั้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.