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


6871

ฉันเพิ่งเริ่มรักษารหัส JavaScript ของคนอื่น ฉันแก้ไขข้อบกพร่องเพิ่มคุณสมบัติและพยายามจัดระเบียบโค้ดและทำให้สอดคล้องกันมากขึ้น

ผู้พัฒนาก่อนหน้านี้ใช้การประกาศฟังก์ชั่นสองวิธีและฉันไม่สามารถทำงานได้หากมีเหตุผลที่อยู่เบื้องหลังหรือไม่

สองวิธีคือ:

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

อะไรคือสาเหตุของการใช้ทั้งสองวิธีที่แตกต่างกันและข้อดีและข้อเสียของแต่ละวิธีคืออะไร มีสิ่งใดบ้างที่สามารถทำได้ด้วยวิธีการหนึ่งที่ไม่สามารถทำได้ด้วยวิธีอื่น


198
permadi.com/tutorial/jsFunc/index.htmlเป็นหน้าดีมากเกี่ยวกับฟังก์ชั่นจาวาสคริปต์
uzay95

67
ที่เกี่ยวข้องเป็นอย่างนี้ดีเยี่ยมบทความเกี่ยวกับการตั้งชื่อการแสดงฟังก์ชั่น
Phrogz

12
@CMS อ้างอิงบทความนี้: kangax.github.com/nfe/#expr-vs-decl
Upperstage

106
มีสองสิ่งที่คุณต้องระวัง: # 1 ใน JavaScript การประกาศจะถูกยกขึ้น หมายความว่าจะกลายเป็นvar a = 1; var b = 2; var a; var b; a = 1; b = 2ดังนั้นเมื่อคุณประกาศ functionOne มันจะถูกประกาศ แต่ค่าจะไม่ถูกตั้งค่าทันที ในขณะที่ functionTwo เป็นเพียงการประกาศมันจะอยู่ที่ส่วนบนสุดของขอบเขต # 2 functionTwo ให้คุณเข้าถึงคุณสมบัติชื่อและช่วยได้มากเมื่อพยายามดีบักบางอย่าง
xavierm02

65
โอ้และ btw ไวยากรณ์ที่ถูกต้องอยู่กับ ";" หลังจากที่ได้รับมอบหมายและไม่มีหลังจากการประกาศ เช่นVSfunction f(){} var f = function(){};
xavierm02

คำตอบ:


5043

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

ตัวอย่างเช่นการแสดงออกของฟังก์ชั่น:

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

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

และการประกาศฟังก์ชั่น:

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

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

ในอดีตการประกาศฟังก์ชั่นที่กำหนดไว้ภายในบล็อกถูกจัดการอย่างไม่สอดคล้องกันระหว่างเบราว์เซอร์ โหมดเข้มงวด (นำมาใช้ใน ES5) แก้ไขสิ่งนี้ได้โดยการกำหนดขอบเขตการประกาศฟังก์ชั่นไปยังบล็อกล้อมรอบ

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


631
@Greg: โดยวิธีการที่แตกต่างไม่เพียง แต่พวกเขาจะแยกวิเคราะห์ในเวลาที่แตกต่างกัน โดยพื้นฐานแล้วคุณfunctionOneเป็นเพียงตัวแปรที่มีฟังก์ชั่นที่ไม่ระบุชื่อแต่ทว่าfunctionTwoเป็นฟังก์ชันที่มีชื่อ เรียก.toString()ทั้งคู่เพื่อดูความแตกต่าง นี่เป็นสิ่งสำคัญในบางกรณีที่คุณต้องการรับชื่อของฟังก์ชันโดยทางโปรแกรม
Jason Bunting

6
@ Jason Bunting .. ไม่แน่ใจว่าสิ่งที่คุณได้รับที่นี่. toString () ดูเหมือนว่าจะส่งกลับค่าเดียวกัน (นิยามฟังก์ชั่น) สำหรับทั้งคู่: cl.ly/2a2C2Y1r0J451o0q0B1B
Jon z

124
มีทั้งที่แตกต่างกัน คนแรกเป็นคนที่สองเป็นfunction expression function declarationคุณสามารถอ่านเพิ่มเติมเกี่ยวกับหัวข้อได้ที่นี่: javascriptweblog.wordpress.com/2010/07/06/…
Michal Kuklis

127
@Greg ส่วนหนึ่งของคำตอบของคุณเกี่ยวกับเวลาในการแยกวิเคราะห์กับเวลาทำงานไม่ถูกต้อง ใน JavaScript การประกาศฟังก์ชันไม่ได้ถูกกำหนดในระหว่างการแยกวิเคราะห์ แต่ในระหว่างเวลาทำงาน กระบวนการดำเนินการดังนี้: ซอร์สโค้ดถูกวิเคราะห์คำ -> โปรแกรมจาวาสคริปต์ถูกประเมิน -> บริบทการดำเนินการส่วนกลางถูกเตรียมใช้งาน -> การเริ่มต้นการเชื่อมโยงการประกาศจะดำเนินการ ในระหว่างกระบวนการนี้การประกาศฟังก์ชันถูกสร้างอินสแตนซ์ (ดูขั้นตอนที่ 5 ของบทที่ 10.5 )
Šime Vidas

102
คำศัพท์สำหรับปรากฏการณ์นี้เรียกว่าการยก
โคลินลูกแพร์

1941

ก่อนอื่นฉันต้องการแก้ไข Greg: function abc(){}ถูกกำหนดขอบเขตด้วย - ชื่อabcถูกกำหนดในขอบเขตที่คำจำกัดความนี้พบ ตัวอย่าง:

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

ประการที่สองเป็นไปได้ที่จะรวมสไตล์ทั้งสอง:

var xyz = function abc(){};

xyzกำลังจะถูกกำหนดตามปกติabcไม่ได้กำหนดในเบราว์เซอร์ทั้งหมด แต่ Internet Explorer - ไม่ต้องพึ่งพามันจะถูกกำหนด แต่มันจะถูกกำหนดไว้ภายในร่างกาย:

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

หากคุณต้องการนามแฝงฟังก์ชั่นในเบราว์เซอร์ทั้งหมดให้ใช้การประกาศประเภทนี้:

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

ในกรณีนี้ทั้งสองxyzและabcเป็นนามแฝงของวัตถุเดียวกัน:

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

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

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

ชื่อจะถูกกำหนดโดยอัตโนมัติ แต่เมื่อคุณกำหนดไว้เช่น

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

ชื่อของมันว่างเปล่า - เราสร้างฟังก์ชั่นที่ไม่ระบุชื่อและกำหนดให้กับตัวแปรบางตัว

อีกเหตุผลที่ดีในการใช้สไตล์ที่รวมกันคือการใช้ชื่อภายในแบบสั้นเพื่ออ้างถึงตัวเองในขณะที่ให้ชื่อที่ไม่ขัดแย้งกันยาวสำหรับผู้ใช้ภายนอก:

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

ในตัวอย่างด้านบนเราสามารถทำเช่นเดียวกันกับชื่อภายนอก แต่มันจะเทอะทะเกินไป (และช้ากว่า)

(อีกวิธีในการอ้างถึงตัวเองคือการใช้arguments.calleeซึ่งยังค่อนข้างยาวและไม่ได้รับการสนับสนุนในโหมดเข้มงวด)

ลึกลงไป JavaScript ใช้คำสั่งทั้งสองแตกต่างกัน นี่คือการประกาศฟังก์ชั่น:

function abc(){}

abc ที่นี่ถูกกำหนดทุกที่ในขอบเขตปัจจุบัน:

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

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

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

นอกจากนี้มันยกผ่านreturnคำสั่ง:

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

นี่คือการแสดงออกของฟังก์ชั่น:

var xyz = function(){};

xyz นี่ถูกกำหนดจากจุดที่ได้รับมอบหมาย:

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

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

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

การประกาศฟังก์ชั่นกับการแสดงออกของฟังก์ชั่นเป็นเหตุผลที่แท้จริงว่าทำไมมีความแตกต่างที่แสดงโดยเกร็ก

สนุกกับความเป็นจริง:

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

โดยส่วนตัวแล้วฉันชอบการประกาศ "ฟังก์ชั่นการแสดงออก" เพราะวิธีนี้ฉันสามารถควบคุมการมองเห็นได้ เมื่อฉันกำหนดฟังก์ชั่นเช่น

var abc = function(){};

ฉันรู้ว่าฉันกำหนดฟังก์ชั่นในเครื่อง เมื่อฉันกำหนดฟังก์ชั่นเช่น

abc = function(){};

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

function abc(){};

ขึ้นอยู่กับบริบทและอาจทำให้คุณเดาได้ว่ามันถูกกำหนดไว้ที่ไหนโดยเฉพาะในกรณีที่eval()- คำตอบคือ: มันขึ้นอยู่กับเบราว์เซอร์


69
ฉันอ้างถึง RoBorg แต่เขาไม่พบที่ไหนเลย ง่าย ๆ : RoBorg === Greg นั่นคือวิธีที่สามารถเขียนประวัติศาสตร์ในยุคอินเทอร์เน็ตได้ ;-)
Eugene Lazutkin

10
var xyz = function abc () {}; console.log (xyz === abc); เบราว์เซอร์ทั้งหมดที่ฉันทดสอบ (Safari 4, Firefox 3.5.5, Opera 10.10) ให้ "ตัวแปรไม่ได้กำหนด: abc"
NVI

7
โดยรวมแล้วฉันคิดว่าโพสต์นี้ทำงานได้ดีในการอธิบายความแตกต่างและข้อดีของการใช้การประกาศฟังก์ชัน ฉันเห็นด้วยที่จะไม่เห็นด้วยถึงประโยชน์ของการใช้ฟังก์ชันการกำหนดค่านิพจน์ให้กับตัวแปรโดยเฉพาะอย่างยิ่งเนื่องจาก "ผลประโยชน์" ดูเหมือนจะเป็นการแนะนำให้ประกาศเอนทิตีระดับโลก ... และทุกคนรู้ดีว่าคุณไม่ควรยุ่งกับ namespace ทั่วโลก ใช่มั้ย ;-)
natlee75

83
imo เหตุผลใหญ่ในการใช้ฟังก์ชั่นที่มีชื่อเป็นเพราะ debuggers สามารถใช้ชื่อเพื่อช่วยให้คุณเข้าใจการโทรสแต็คของคุณหรือกองติดตาม มัน sucks เมื่อคุณดู call stack และดู "ฟังก์ชั่นที่ไม่ระบุชื่อ" 10 ระดับลึก ...
goat

3
var abc = function(){}; console.log(abc.name);ไม่ได้ผลิต""ใด ๆ เพิ่มเติม แต่"abc"แทน
Qwerty

632

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

ข้อตกลง:

รายการด่วน:

  • ประกาศฟังก์ชั่น

  • การfunctionแสดงออก"ไม่ระบุชื่อ" (ซึ่งแม้จะมีคำว่าบางครั้งสร้างฟังก์ชั่นที่มีชื่อ)

  • functionนิพจน์ที่ระบุชื่อ

  • Accessor Function Initializer (ES5 +)

  • Arrow Function Expression (ES2015 +) (ซึ่งเช่นนิพจน์ฟังก์ชันนิรนามไม่เกี่ยวข้องกับชื่อที่ชัดเจนและยังสามารถสร้างฟังก์ชั่นที่มีชื่อ)

  • การประกาศวิธีการใน Object Initializer (ES2015 +)

  • ตัวสร้างและการประกาศวิธีในclass(ES2015 +)

ประกาศฟังก์ชั่น

แบบฟอร์มแรกคือการประกาศฟังก์ชั่นซึ่งมีลักษณะดังนี้:

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

การประกาศฟังก์ชั่นคือการประกาศ ; มันไม่ใช่คำสั่งหรือการแสดงออก ดังนั้นคุณจะไม่ปฏิบัติตามด้วย;(แม้ว่าการทำเช่นนั้นจะไม่เป็นอันตราย)

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

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

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

จนกระทั่ง ES2015 ข้อมูลจำเพาะไม่ครอบคลุมสิ่งที่เป็นเครื่องมือ JavaScript ที่ควรจะทำอย่างไรถ้าคุณใส่ฟังก์ชั่นการประกาศภายในโครงสร้างการควบคุมเช่นtry, if, switch, whileฯลฯ เช่นนี้

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

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

แม้ว่าการทำเช่นนี้จะไม่ได้ระบุไว้จนกระทั่ง ES2015 แต่เป็นส่วนขยายที่อนุญาตเพื่อรองรับการประกาศฟังก์ชันในบล็อก น่าเสียดาย (และหลีกเลี่ยงไม่ได้) เครื่องยนต์ต่าง ๆ ทำสิ่งที่แตกต่างกัน

ในฐานะของ ES2015 สเปคบอกว่าจะทำอย่างไร ในความเป็นจริงมันมีสามสิ่งที่ต้องทำ:

  1. หากอยู่ในโหมดหลวมไม่ได้อยู่ในเว็บเบราว์เซอร์เอ็นจิ้น JavaScript จะต้องทำสิ่งหนึ่ง
  2. หากอยู่ในโหมดหลวมบนเว็บเบราว์เซอร์เอ็นจิ้น JavaScript จะต้องทำอย่างอื่น
  3. หากอยู่ในโหมดเข้มงวด (เบราว์เซอร์หรือไม่) เอ็นจิ้น JavaScript จะต้องทำสิ่งอื่น

กฎสำหรับโหมดหลวมนั้นยุ่งยาก แต่ในโหมดเข้มงวดการประกาศฟังก์ชันในบล็อกนั้นง่าย: พวกมันอยู่ในบล็อก (มีขอบเขตบล็อกซึ่งยังใหม่ใน ES2015) และถูกยกขึ้นไปด้านบน ของบล็อก ดังนั้น:

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

functionนิพจน์"ไม่ระบุชื่อ"

รูปแบบทั่วไปที่สองเรียกว่านิพจน์ฟังก์ชันที่ไม่ระบุชื่อ :

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

เช่นเดียวกับนิพจน์ทั้งหมดการประเมินจะถูกประเมินเมื่อมีการเข้าถึงในการเรียกใช้โค้ดทีละขั้นตอน

ใน ES5 ฟังก์ชั่นนี้สร้างไม่มีชื่อ (มันไม่ระบุชื่อ) ใน ES2015 ฟังก์ชันจะถูกกำหนดชื่อถ้าเป็นไปได้โดยอนุมานจากบริบท yในตัวอย่างข้างต้นชื่อจะเป็น สิ่งที่คล้ายกันเกิดขึ้นเมื่อฟังก์ชันคือค่าของ initializer คุณสมบัติ (สำหรับรายละเอียดเกี่ยวกับสิ่งที่เกิดขึ้นและกฎค้นหาSetFunctionNameในสเปค  - มันจะปรากฏขึ้นทั่วสถานที่)

functionนิพจน์ที่ระบุชื่อ

รูปแบบที่สามคือนิพจน์ฟังก์ชันที่ระบุชื่อ ("NFE"):

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

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

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

โปรดทราบว่า NFE มักเป็นแหล่งของข้อบกพร่องสำหรับการใช้งาน JavaScript ยกตัวอย่างเช่น IE8 และรุ่นก่อนหน้าจัดการกับ NFE ไม่ถูกต้องอย่างสมบูรณ์สร้างฟังก์ชันที่แตกต่างกันสองรายการในเวลาที่ต่างกันสองครั้ง Safari เวอร์ชันก่อนหน้ามีปัญหาเช่นกัน ข่าวดีก็คือเบราว์เซอร์เวอร์ชันปัจจุบัน (IE9 ขึ้นไปซาฟารีปัจจุบัน) ไม่มีปัญหาเหล่านั้นอีกแล้ว (แต่จากการเขียนนี้น่าเศร้าที่ IE8 ยังคงใช้กันอย่างแพร่หลายและดังนั้นการใช้ NFEs ที่มีรหัสสำหรับเว็บโดยทั่วไปยังคงเป็นปัญหาอยู่)

Accessor Function Initializer (ES5 +)

บางครั้งฟังก์ชั่นสามารถแอบในส่วนใหญ่ไม่มีใครสังเกตเห็น; เป็นกรณีที่มีฟังก์ชั่นการเข้าถึง นี่คือตัวอย่าง:

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

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

นอกจากนี้คุณยังสามารถสร้างฟังก์ชั่นการเข้าถึงด้วยObject.defineProperty, และไม่ค่อยมีคนรู้จักอาร์กิวเมนต์ที่สองไปObject.definePropertiesObject.create

ลูกศรฟังก์ชั่นการแสดงออก (ES2015 +)

ES2015 นำเราฟังก์ชั่นลูกศร นี่คือตัวอย่างหนึ่ง:

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

เห็นn => n * 2สิ่งที่ซ่อนตัวอยู่ในmap()สาย? นั่นคือฟังก์ชั่น

สองสามสิ่งเกี่ยวกับฟังก์ชั่นลูกศร:

  1. thisพวกเขาไม่ได้เป็นของตัวเอง แต่พวกเขาใกล้กว่าthisของบริบทที่พวกเขากำลังที่กำหนดไว้ (พวกเขายังปิดargumentsและที่เกี่ยวข้อง, super.) ซึ่งหมายความว่าthisภายในพวกเขาเหมือนกันกับthisที่พวกเขากำลังสร้างและไม่สามารถเปลี่ยนแปลงได้

  2. ดังที่คุณได้สังเกตเห็นด้วยข้างต้นคุณไม่ได้ใช้คำหลักfunction; คุณใช้=>แทน

n => n * 2ตัวอย่างข้างต้นเป็นรูปแบบหนึ่งของพวกเขา หากคุณมีหลายอาร์กิวเมนต์ที่จะผ่านฟังก์ชั่นคุณใช้ parens:

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

(โปรดจำไว้ว่าArray#mapส่งผ่านรายการเป็นอาร์กิวเมนต์แรกและดัชนีเป็นรายการที่สอง)

ในทั้งสองกรณีร่างกายของฟังก์ชันเป็นเพียงการแสดงออก ค่าส่งคืนของฟังก์ชันจะเป็นผลลัพธ์ของนิพจน์นั้นโดยอัตโนมัติ (คุณไม่ได้ใช้อย่างชัดเจนreturn)

หากคุณทำมากกว่านิพจน์เดียวให้ใช้{}และชัดเจนreturn(ถ้าคุณต้องการส่งคืนค่า) ตามปกติ:

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

รุ่นโดยไม่ต้อง{ ... }เรียกว่าฟังก์ชั่นลูกศรที่มีร่างกายแสดงออกหรือร่างกายกระชับ (นอกจากนี้: การกระชับ . ฟังก์ชั่นลูกศร) เป็นผู้หนึ่งที่มี{ ... }การกำหนดร่างกายเป็นฟังก์ชั่นที่มีลูกศรร่างกายทำงาน (เช่น: ฟังก์ชั่นลูกศรverbose )

การประกาศวิธีการใน Object Initializer (ES2015 +)

ES2015 ช่วยให้รูปแบบที่สั้นลงประกาศทรัพย์สินที่อ้างอิงฟังก์ชั่นที่เรียกว่าเป็นคำนิยามวิธี ; ดูเหมือนว่านี้:

var o = {
    foo() {
    }
};

เกือบเทียบเท่าใน ES5 และรุ่นก่อนหน้าจะเป็น:

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

ความแตกต่าง (นอกเหนือจากคำฟุ่มเฟื่อย) คือวิธีการที่สามารถใช้superแต่ฟังก์ชั่นไม่สามารถ ตัวอย่างเช่นหากคุณมีวัตถุที่กำหนด (พูด) valueOfโดยใช้ไวยากรณ์วิธีการก็สามารถใช้super.valueOf()เพื่อรับค่าObject.prototype.valueOfจะได้กลับมา (ก่อนที่จะทำอย่างอื่นกับมัน) ในขณะที่รุ่น ES5 จะต้องทำObject.prototype.valueOf.call(this)แทน

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

ตัวสร้างและการประกาศวิธีในclass(ES2015 +)

ES2015 ทำให้เรามีclassไวยากรณ์รวมถึงตัวสร้างและวิธีการประกาศ:

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

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

มีฟังก์ชั่นที่สองการประกาศดังกล่าวเป็นหนึ่งสำหรับการสร้างซึ่งได้รับชื่อPersonและสำหรับซึ่งเป็นฟังก์ชั่นได้รับมอบหมายให้getFullNamePerson.prototype


3
ถ้าอย่างนั้นชื่อwก็จะถูกละเว้น?
BiAiB

8
@PellePenna: ชื่อฟังก์ชั่นมีประโยชน์สำหรับหลาย ๆ อย่าง biggies สองตัวในมุมมองของฉันคือการเรียกซ้ำและชื่อของฟังก์ชันที่แสดงในสแต็กการโทรการติดตามข้อยกเว้นและอื่น ๆ
TJ Crowder

4
@ChaimEliyah - "การยอมรับไม่ได้หมายความว่ามันเป็นคำตอบที่ดีที่สุด แต่ก็หมายความว่ามันใช้ได้ผลกับคนที่ถาม" แหล่งที่มา
ScrapCode

6
@AR: ค่อนข้างจริง แม้ว่าจะเป็นที่น่าขบขันเหนือคำกล่าวที่ว่า "คำตอบที่ดีที่สุดปรากฏก่อนเพื่อให้พวกเขาหาได้ง่ายเสมอ" เนื่องจากคำตอบที่ยอมรับปรากฏขึ้นเป็นครั้งแรกแม้คำตอบที่ได้รับการโหวตสูงกว่าทัวร์อาจจะค่อนข้างขัดแย้งกันเอง ;-) และยังไม่ถูกต้องถ้าเราตัดสินว่า "ดีที่สุด" ด้วยคะแนน (ซึ่งไม่น่าเชื่อถือมันเป็นสิ่งที่เรามี) คำตอบ "ดีที่สุด" จะปรากฏขึ้นก่อนเท่านั้นหากคุณใช้แท็บ "โหวต" - มิฉะนั้นคำตอบที่แรกคือคำตอบที่ใช้งานอยู่หรือคำตอบที่เก่าที่สุด
TJ Crowder

1
@TJCrowder: เห็นด้วย 'จัดเรียงตามวันที่' บางครั้งก็น่ารำคาญ
ScrapCode

143

การพูดเกี่ยวกับบริบทโลกทั้งvarคำสั่งและFunctionDeclarationท้ายที่สุดจะสร้างคุณสมบัติที่ไม่สามารถลบได้บนวัตถุส่วนกลาง แต่ค่าของทั้งคู่สามารถเขียนทับได้

ความแตกต่างที่ลึกซึ้งระหว่างสองวิธีคือเมื่อกระบวนการVariable instantiationทำงาน (ก่อนการเรียกใช้รหัสจริง) ตัวระบุทั้งหมดที่ประกาศด้วยvarจะเริ่มต้นด้วยundefinedและตัวที่ใช้โดยFunctionDeclaration's จะพร้อมใช้งานตั้งแต่วินาทีตัวอย่างเช่น:

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

การมอบหมายให้bar FunctionExpressionดำเนินการจนกระทั่งรันไทม์

คุณสมบัติโกลบอลที่สร้างโดย a FunctionDeclarationสามารถเขียนทับได้โดยไม่มีปัญหาเช่นเดียวกับค่าตัวแปรเช่น:

 function test () {}
 test = null;

ความแตกต่างที่ชัดเจนระหว่างสองตัวอย่างของคุณก็คือฟังก์ชั่นแรกไม่มีชื่อ แต่ที่สองมีชื่อซึ่งจะมีประโยชน์จริง ๆ เมื่อทำการดีบั๊ก (เช่นการตรวจสอบสแตกการโทร)

เกี่ยวกับตัวอย่างแรกที่แก้ไขของคุณ ( foo = function() { alert('hello!'); };) เป็นงานที่ไม่ได้ประกาศฉันขอแนะนำให้คุณใช้varคำหลักเสมอ

ด้วยการกำหนดโดยไม่มีvarคำสั่งถ้าระบุอ้างอิงไม่พบในห่วงโซ่ขอบเขตก็จะกลายเป็นdeleteableคุณสมบัติของวัตถุโลก

นอกจากนี้ยังได้รับมอบหมายไม่ได้ประกาศโยนReferenceErrorบน ECMAScript 5 ภายใต้โหมดเข้มงวด

ต้องอ่าน:

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


ฉันไม่ทราบว่าฟังก์ชั่นสามารถเขียนทับใน JavaScript! นอกจากนี้คำสั่งแยกวิเคราะห์เป็นจุดขายที่ยิ่งใหญ่สำหรับฉัน ฉันเดาว่าฉันต้องดูว่าฉันสร้างฟังก์ชั่นอย่างไร
Xeoncross

2
+0 ไปยังบทความ "ฟังก์ชั่นการตั้งชื่อนิพจน์ demystified" เนื่องจากเป็น 404 กระจกที่เป็นไปได้หรือไม่: kangax.github.com/nfe
Mr_Chimp

@CMS หนึ่งที่ดี โปรดจำไว้ว่าฉันไม่เคยเห็นต้นฉบับดังนั้นฉันไม่รู้ว่านั่นเป็นกระจกเงาหรือบทความอื่นที่มีชื่อเหมือนกัน!
Mr_Chimp

@Mr_Chimp ฉันค่อนข้างแน่ใจว่า thewaybackmachine กำลังบอกว่ามันมี 302 ครั้งในการรวบรวมข้อมูลและการเปลี่ยนเส้นทางไปยังลิงก์ที่คุณให้ไว้
John

123

ตัวอย่างโค้ดสองรายการที่คุณโพสต์ไว้จะมีพฤติกรรมเหมือนกันทุกประการ

อย่างไรก็ตามความแตกต่างของพฤติกรรมคือตัวแปรแรก ( var functionOne = function() {}) ฟังก์ชันนั้นสามารถถูกเรียกหลังจากจุดนั้นในรหัสเท่านั้น

ด้วยตัวแปรที่สอง ( function functionTwo()) ฟังก์ชันนี้สามารถใช้งานกับโค้ดที่ทำงานด้านบนซึ่งมีการประกาศฟังก์ชัน

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

ข้อมูลทางเทคนิคเพิ่มเติม

JavaScript มีวิธีกำหนดฟังก์ชันได้สามวิธี

  1. ตัวอย่างข้อมูลครั้งแรกของคุณแสดงให้เห็นถึงการแสดงออกของฟังก์ชั่น สิ่งนี้เกี่ยวข้องกับการใช้ตัวดำเนินการ"ฟังก์ชัน"เพื่อสร้างฟังก์ชัน - ผลลัพธ์ของตัวดำเนินการนั้นสามารถเก็บไว้ในตัวแปรหรือคุณสมบัติของวัตถุ การแสดงออกของฟังก์ชั่นนั้นมีประสิทธิภาพเช่นนั้น ฟังก์ชั่นการแสดงออกมักจะเรียกว่า "ฟังก์ชั่นที่ไม่ระบุชื่อ" เพราะมันไม่จำเป็นต้องมีชื่อ
  2. ตัวอย่างที่สองของคุณคือการประกาศฟังก์ชัน สิ่งนี้ใช้คำสั่ง "function"เพื่อสร้างฟังก์ชั่น ฟังก์ชั่นนี้มีให้ในเวลาที่แยกวิเคราะห์และสามารถเรียกได้ทุกที่ในขอบเขตนั้น คุณยังสามารถเก็บไว้ในตัวแปรหรือคุณสมบัติของวัตถุในภายหลัง
  3. วิธีที่สามของการกำหนดฟังก์ชั่นคือตัวสร้าง"Function ()"ซึ่งไม่ปรากฏในโพสต์ดั้งเดิมของคุณ ไม่แนะนำให้ใช้สิ่งนี้เพราะมันทำงานในลักษณะเดียวกับeval()ที่มีปัญหา

103

คำอธิบายที่ดีกว่าสำหรับคำตอบของ Greg

functionTwo();
function functionTwo() {
}

ทำไมไม่มีข้อผิดพลาด? เราได้รับการสอนเสมอว่านิพจน์จะถูกดำเนินการจากบนลงล่าง (??)

เพราะ:

การประกาศฟังก์ชั่นและการประกาศตัวแปรจะย้าย ( hoisted) สุดลูกหูลูกตาไปที่ด้านบนสุดของขอบเขตที่บรรจุโดยล่าม JavaScript พารามิเตอร์ฟังก์ชั่นและชื่อที่กำหนดโดยภาษานั้นมีอยู่แล้ว เชอร์รี่เบ็น

ซึ่งหมายความว่ารหัสเช่นนี้:

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

ขอให้สังเกตว่าส่วนที่ได้รับมอบหมายของการประกาศไม่ได้ยก เฉพาะชื่อที่ถูกยก

แต่ในกรณีที่มีการประกาศฟังก์ชั่นร่างกายของฟังก์ชั่นทั้งหมดจะถูกยกเช่นกัน :

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

สวัสดี suhail ขอบคุณสำหรับข้อมูลที่ชัดเจนเกี่ยวกับหัวข้อฟังก์ชั่น ตอนนี้คำถามของฉันคือข้อใดจะเป็นการประกาศครั้งแรกในลำดับชั้นการประกาศว่าการประกาศตัวแปร (functionOne) หรือการประกาศฟังก์ชัน (functionTwo)?
Sharathi RB

91

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

ฉันมักจะสร้างโมดูล JavaScript ด้วยรูปแบบเช่นนี้:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();

ด้วยรูปแบบนี้ฟังก์ชั่นสาธารณะของคุณจะใช้การมอบหมายทั้งหมดในขณะที่ฟังก์ชั่นส่วนตัวของคุณใช้การประกาศ

(โปรดทราบว่าการมอบหมายควรใช้อัฒภาคหลังจากคำสั่งในขณะที่การประกาศห้ามมัน)


4
yuiblog.com/blog/2007/06/12/module-patternเป็นการอ้างอิงดั้งเดิมสำหรับรูปแบบโมดูลเท่าที่ฉันสามารถบอกได้ (แม้ว่าบทความนั้นจะใช้var foo = function(){...}ไวยากรณ์แม้กระทั่งตัวแปรส่วนตัว
Sean McMillan

สิ่งนี้ไม่เป็นความจริงทั้งหมดใน IE เวอร์ชันเก่ากว่าจริงๆ ( function window.onload() {}เป็นเรื่อง)
Ry-

77

ภาพประกอบว่าเมื่อใดที่ต้องการวิธีแรกเป็นวิธีที่สองคือเมื่อคุณต้องการหลีกเลี่ยงการแทนที่คำจำกัดความก่อนหน้าของฟังก์ชัน

กับ

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

คำจำกัดความของคำว่านี้myfunctionจะแทนที่คำนิยามก่อนหน้าใด ๆ เนื่องจากจะทำในการแยกวิเคราะห์เวลา

ในขณะที่

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

งานที่ถูกต้องในการกำหนดmyfunctionเมื่อconditionพบเท่านั้น


1
ตัวอย่างนี้ดีและใกล้เคียงกับความสมบูรณ์แบบ แต่สามารถปรับปรุงได้ ตัวอย่างที่ดีกว่าคือการนิยามvar myFunc = null;นอกลูปหรือนอกบล็อก if / elseif / else จากนั้นคุณสามารถกำหนดฟังก์ชันที่แตกต่างให้กับตัวแปรเดียวกันแบบมีเงื่อนไขได้ ใน JS มันเป็นวิธีการที่ดีกว่าในการกำหนดค่าที่หายไปให้เป็นโมฆะจากนั้นจึงไม่ได้กำหนด ดังนั้นคุณควรประกาศฟังก์ชันของฉันเป็นโมฆะก่อนจากนั้นกำหนดในภายหลังตามเงื่อนไข
Alexander Mills

62

เหตุผลสำคัญคือการเพิ่มตัวแปรหนึ่งเดียวเท่านั้นที่เป็น "รูท" ของเนมสเปซของคุณ ...

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

}

หรือ

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

มีเทคนิคมากมายสำหรับการกำหนดชื่อ มันมีความสำคัญมากขึ้นเมื่อมีโมดูล JavaScript มากมาย

ยังดู ฉันจะประกาศ namespace ใน JavaScript ได้อย่างไร


3
มันจะปรากฏคำตอบนี้ถูกรวมเข้าไปในคำถามนี้จากคำถามอื่นและถ้อยคำอาจดูเหมือนจะเป็นบิตขนาดเล็กที่ไม่เกี่ยวข้องกับเรื่องนี้คำถาม คุณจะพิจารณาการแก้ไขคำตอบเพื่อให้ดูเหมือนตรงไปที่คำถามนี้มากขึ้นหรือไม่ (เพื่อย้ำว่านี่ไม่ใช่ความผิดของคุณเลย ... เพียงผลข้างเคียงของคำถามที่ถูกผสาน) คุณสามารถลบได้และฉันคิดว่าคุณจะรักษาชื่อเสียงของคุณ หรือคุณสามารถทิ้งมันไว้ เนื่องจากเป็นรุ่นเก่ามันอาจไม่สร้างความแตกต่างใหญ่
Andrew Barber

55

hoisting คือการกระทำของล่าม JavaScript ในการย้ายการประกาศตัวแปรและฟังก์ชันทั้งหมดไปยังด้านบนของขอบเขตปัจจุบัน

อย่างไรก็ตามจะมีเฉพาะการประกาศจริงเท่านั้น โดยออกจากที่ที่พวกเขาอยู่

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

ตัวแปร

Javascript เรียกว่าภาษาที่พิมพ์อย่างหลวม ๆ ซึ่งหมายความว่าตัวแปร Javascript สามารถเก็บค่าของข้อมูลประเภท Javascript จะดูแลการเปลี่ยนแปลงชนิดตัวแปรโดยอัตโนมัติตามค่า / ตัวอักษรที่จัดเตรียมไว้ในระหว่างการใช้งานจริง

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

ฟังก์ชัน

function Identifier_opt ( FormalParameterList_opt ) { 
      FunctionBody | sequence of statements

      « return;  Default undefined
      « return 'some data';
}
  • ฟังก์ชั่นประกาศภายในหน้าจะยกไปด้านบนของหน้ามีการเข้าถึงทั่วโลก
  • ฟังก์ชั่นที่ประกาศไว้ภายในฟังก์ชั่นบล็อกจะถูกยกขึ้นไปด้านบนของบล็อก
  • ค่าส่งคืนเริ่มต้นของฟังก์ชั่นคือ ' ไม่ได้กำหนด ', ค่าเริ่มต้นการประกาศตัวแปรยังเป็น 'ไม่ได้กำหนด'

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

ประกาศฟังก์ชั่น

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

ฟังก์ชั่นการแสดงออก

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

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

ฟังก์ชั่นที่กำหนดให้กับตัวแปรตัวอย่าง:

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

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

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

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

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

namedExpression();
globalExpression();

จาวาสคริปต์ตีความว่า

var anonymous;
var namedExpression;
var globalExpression;

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

namedExpression = function for_InternalUSE(fact){
    var localExpression;

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

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

namedExpression(10);
globalExpression();

คุณสามารถตรวจสอบการประกาศฟังก์ชั่นการทดสอบการแสดงออกผ่านการใช้เบราว์เซอร์ที่แตกต่างกัน jsperf Test Runner


คลาสฟังก์ชันของตัวสร้างES5 : วัตถุฟังก์ชันที่สร้างขึ้นโดยใช้ Function.prototype.bind

JavaScript ทำหน้าที่เป็นวัตถุชั้นหนึ่งดังนั้นในการเป็นวัตถุคุณสามารถกำหนดคุณสมบัติให้กับฟังก์ชันได้

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

var expFn = Shape; // Function Expression

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

ES6 แนะนำฟังก์ชั่นลูกศร : การแสดงออกของฟังก์ชั่นลูกศรมีไวยากรณ์ที่สั้นกว่าพวกเขาเหมาะที่สุดสำหรับฟังก์ชั่นที่ไม่ใช่วิธีการและพวกเขาไม่สามารถใช้เป็นตัวสร้าง

ArrowFunction : ArrowParameters => ConciseBody.

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

3
เอ่อคำตอบของคุณ ... มันไม่ชัดเจนหรือ? เขียนได้ดีแม้ว่า +1 สำหรับการใช้จ่ายและการเขียนข้อมูลมากเกินไป
ภาษาเดนมาร์ก

40

ฉันกำลังเพิ่มคำตอบของตัวเองเพียงเพราะคนอื่น ๆ ได้ครอบคลุมส่วนการชักรอกอย่างละเอียด

ฉันสงสัยเกี่ยวกับวิธีที่ดีกว่าในขณะนี้และขอบคุณhttp://jsperf.comตอนนี้ฉันรู้ :)

ป้อนคำอธิบายรูปภาพที่นี่

การประกาศฟังก์ชั่นเร็วขึ้นและนั่นคือสิ่งที่สำคัญในการพัฒนาเว็บใช่มั้ย ;)


8
ฉันจะบอกว่าการบำรุงรักษาเป็นสิ่งสำคัญที่สุดของรหัสส่วนใหญ่ ประสิทธิภาพเป็นสิ่งสำคัญ แต่ในกรณีส่วนใหญ่ IO น่าจะเป็นคอขวดที่ใหญ่กว่าซึ่งเป็นวิธีที่คุณกำหนดหน้าที่ของคุณ อย่างไรก็ตามมีปัญหาบางอย่างที่คุณต้องการประสิทธิภาพการทำงานทุกอย่างที่คุณได้รับและสิ่งนี้มีประโยชน์ในกรณีเหล่านั้น นอกจากนี้ยังมีคำตอบที่ดีที่คำตอบที่ชัดเจนเป็นส่วนหนึ่งของคำถาม
Richard Garside

3
ฉันพบว่ามันเป็นวิธีอื่นใน Firefox jsperf.com/sandytest
Sandeep Nayak

เพิ่งได้รับการอัปเดตเนื่องจากตอนนี้ฉันได้เขียนโปรแกรมฟังก์ชั่นเต็มรูปแบบในจาวาสคริปต์แล้วฉันไม่เคยใช้การประกาศเพียงแค่การแสดงออกของฟังก์ชั่นเท่านั้นดังนั้นฉันจึงสามารถเชื่อมโยงและเรียกใช้ฟังก์ชันของฉันตามชื่อตัวแปร ตรวจสอบ RamdaJS ...
Leon Gaban

1
@SandeepNayak ฉันเพิ่งทำการทดสอบของคุณเองใน Firefox 50.0.0 / Windows 7 0.0.0 และจริง ๆ แล้วมันก็เป็นวิธีเดียวกับของ Leon ดังนั้นหากการทดสอบของคุณถูกต้องฉันจะสรุปได้ว่าการทดสอบของ jsperf นั้นไม่ใช่ตัวบ่งชี้และทั้งหมดนั้นขึ้นอยู่กับเบราว์เซอร์และ / หรือเวอร์ชั่นของระบบปฏิบัติการของคุณหรือในสถานะเฉพาะของเครื่องปัจจุบันในช่วงเวลานั้น
ocramot

33

การประกาศฟังก์ชั่นและการแสดงออกของฟังก์ชั่นที่กำหนดให้กับตัวแปรที่ทำงานเหมือนกันเมื่อมีการจัดตั้งผูกพัน

มีความแตกต่างอย่างไรก็ตามวิธีและเวลาที่วัตถุฟังก์ชันเชื่อมโยงกับตัวแปรจริง ความแตกต่างนี้เกิดจากกลไกที่เรียกว่าการยกตัวแปรใน JavaScript

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

  • เมื่อการประกาศฟังก์ชั่นถูกยกขึ้นร่างกายฟังก์ชั่น "ดังนี้" ดังนั้นเมื่อมีการประเมินร่างกายฟังก์ชั่นตัวแปรจะถูกผูกไว้กับวัตถุฟังก์ชั่นทันที

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

ลำดับของการชักรอกก็มีความสำคัญเช่นกัน: การประกาศฟังก์ชั่นมีความสำคัญมากกว่าการประกาศตัวแปรที่มีชื่อเดียวกันและการประกาศฟังก์ชั่นสุดท้ายจะมีความสำคัญเหนือกว่าการประกาศฟังก์ชั่นก่อนหน้านี้ด้วยชื่อเดียวกัน

ตัวอย่างบางส่วน ...

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

ตัวแปรที่fooถูกยกขึ้นไปด้านบนของฟังก์ชั่นเริ่มต้นไปundefinedเพื่อให้!fooเป็นtrueเพื่อให้ได้รับมอบหมายfoo 10ขอบเขตfooด้านนอกbarไม่มีบทบาทและไม่ถูกแตะต้อง

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

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

การประกาศฟังก์ชั่นมีความสำคัญมากกว่าการประกาศตัวแปรและการประกาศฟังก์ชั่นสุดท้าย "sticks"

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

ในตัวอย่างนี้จะเริ่มต้นกับวัตถุฟังก์ชั่นที่เกิดจากการประเมินผลการประกาศฟังก์ชั่นที่สองแล้วที่ได้รับมอบหมายa4

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

aนี่คือการประกาศฟังก์ชันจะยกแรกประกาศการเริ่มต้นและตัวแปร 10ถัดไปตัวแปรนี้จะได้รับมอบหมาย ในคำอื่น ๆ : aได้รับมอบหมายไม่ได้กำหนดให้กับตัวแปรด้านนอก


3
คุณมีวิธีแปลก ๆ ในการวางเครื่องมือจัดฟันแบบปิด คุณเป็น Coder Python หรือไม่? ดูเหมือนว่าคุณพยายามทำให้ Javascript ดูเหมือน Python ฉันกลัวว่าคนอื่นจะสับสน ถ้าฉันต้องบำรุงรักษารหัส JavaScript ของคุณฉันจะให้รหัสของคุณผ่านพริตตี้สวยอัตโนมัติก่อน
nalply

1
โพสต์ที่ยอดเยี่ยม 'ฟังก์ชั่นที่เรียกใช้งานเอง' หรือ 'นิพจน์ฟังก์ชันที่เรียกใช้ทันที' ควรจะง่ายพอที่จะเห็นและการตั้งค่าสไตล์ของเขาไม่ควรเบี่ยงเบนความสนใจจากโพสต์ของเขา - ซึ่งมีความถูกต้องและสรุป +1
Ricalsin

32

ตัวอย่างแรกคือการประกาศฟังก์ชั่น:

function abc(){}

ตัวอย่างที่สองคือนิพจน์ฟังก์ชัน:

var abc = function() {};

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

หากต้องการใส่เพียง:

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

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

เพื่อศึกษาเพิ่มเติมเกี่ยวกับหัวข้อนี้ฉันขอแนะนำให้คุณลิงค์นี้


1
ตัวอย่างของคุณเหมือนกับคำตอบที่ดีที่สุด
GôTô

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

1
มันยอดเยี่ยมมากที่คุณต้องการแชร์ลิงก์ แต่การเชื่อมโยงไปยังข้อมูลเพิ่มเติมใน SO ควรเป็นเพียงความเห็นเกี่ยวกับคำถามหรือคำตอบที่คุณโปรดปราน เป็นส่วนย่อยที่ดีที่สุดในการถ่วงหน้ายาวและซับซ้อนเช่นนี้พร้อมข้อมูลซ้ำ ๆ เพื่อเพิ่มลิงค์ที่มีประโยชน์เพียงจุดเดียวในตอนท้าย ไม่คุณจะไม่ได้รับคะแนนตัวแทนจากการให้ลิงก์ แต่คุณจะช่วยเหลือชุมชน
XML

30

ในแง่ของค่าบำรุงรักษารหัสฟังก์ชั่นที่ตั้งชื่อจะดีกว่า:

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

ฉันสงสัยว่ามี PROS มากกว่าสำหรับฟังก์ชั่นที่ตั้งชื่อไว้แล้ว และสิ่งที่มีการระบุไว้ว่าเป็นข้อได้เปรียบของฟังก์ชั่นที่มีชื่อเป็นข้อเสียสำหรับคนที่ไม่ระบุชื่อ

ในอดีตฟังก์ชั่นที่ไม่ระบุชื่อปรากฏขึ้นจากความไม่สามารถของ JavaScript เป็นภาษาเพื่อแสดงรายการสมาชิกที่มีฟังก์ชั่นที่มีชื่อ:

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

2
มีการทดสอบเพื่อยืนยันคือ: blog.firsov.net/2010/01/…การทดสอบประสิทธิภาพของ JS - ขอบเขตและฟังก์ชั่นการตั้งชื่อ - Analytics
Sasha Firsov

25

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

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับฟังก์ชั่นนิรนามและแคลคูลัสแลมบ์ดาวิกิพีเดียเป็นการเริ่มต้นที่ดี ( http://en.wikipedia.org/wiki/Anonymous_function )


ในขณะที่ ES2015 (หกปีครึ่งหลังจากคำตอบของคุณถูกโพสต์) ฟังก์ชั่นทั้งสองในคำถามจะถูกตั้งชื่อ
TJ Crowder

25

ฉันใช้วิธีตัวแปรในรหัสของฉันด้วยเหตุผลที่เฉพาะเจาะจงทฤษฎีที่ได้รับการกล่าวถึงในวิธีที่เป็นนามธรรมข้างต้น แต่ตัวอย่างอาจช่วยให้บางคนเช่นฉันด้วยความเชี่ยวชาญ JavaScript ที่ จำกัด

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

บางแบรนด์ต้องการฟังก์ชั่นเฉพาะและบางยี่ห้อไม่ต้องการ บางครั้งฉันต้องเพิ่มฟังก์ชั่นใหม่เพื่อทำสิ่งใหม่เฉพาะแบรนด์ ฉันยินดีที่จะเปลี่ยนรหัสที่แชร์ แต่ฉันไม่ต้องการเปลี่ยนไฟล์แบรนด์ทั้งหมด 160 ชุด

โดยใช้ไวยากรณ์ตัวแปรฉันสามารถประกาศตัวแปร (ตัวชี้ฟังก์ชั่นเป็นหลัก) ในรหัสที่ใช้ร่วมกันและอาจกำหนดฟังก์ชั่นต้นขั้วเล็กน้อยหรือตั้งค่าเป็นโมฆะ

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

จากความคิดเห็นของผู้คนข้างต้นฉันรวบรวมมันอาจเป็นไปได้ที่จะกำหนดฟังก์ชันแบบคงที่ใหม่อีกครั้ง แต่ฉันคิดว่าวิธีแก้ไขปัญหาตัวแปรนั้นดีและชัดเจน


25

คำตอบของ Gregดีพอ แต่ฉันยังต้องการเพิ่มสิ่งที่ฉันเรียนรู้ตอนนี้ดูวิดีโอของ Douglas Crockford

ฟังก์ชั่นการแสดงออก:

var foo = function foo() {};

คำสั่งฟังก์ชั่น:

function foo() {};

คำสั่งฟังก์ชั่นเป็นเพียงชวเลขสำหรับvarคำสั่งที่มีfunctionค่า

ดังนั้น

function foo() {};

ขยายเป็น

var foo = function foo() {};

ซึ่งขยายเพิ่มเติมไปที่:

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

และพวกเขาทั้งสองยกขึ้นไปด้านบนของรหัส

ภาพหน้าจอจากวิดีโอ


7
ขออภัย แต่ไม่ถูกต้อง - ฉันไม่รู้ว่า Crockford พยายามพูดอะไรในสไลด์นั้น การประกาศฟังก์ชันและตัวแปรทั้งสองจะถูกยกขึ้นด้านบนของขอบเขตเสมอ ความแตกต่างคือการกำหนดตัวแปร (ไม่ว่าคุณจะกำหนดด้วยสตริงบูลีนหรือฟังก์ชั่น) จะไม่ถูกยกไปด้านบนในขณะที่ฟังก์ชั่นร่างกาย (ใช้การประกาศฟังก์ชั่น) เป็น
Thomas Heymann

ดูตัวอย่างโค้ดเหล่านี้: gist.github.com/cyberthom/36603fbc20de8e04fd09
Thomas Heymann

23

มีการเปรียบเทียบที่สำคัญสี่ประการระหว่างการประกาศฟังก์ชันที่แตกต่างกันสองรายการตามรายการด้านล่าง

  1. ความพร้อมใช้งาน (ขอบเขต) ของฟังก์ชัน

ผลงานต่อไปนี้เนื่องจากfunction add()ถูกกำหนดขอบเขตไปยังบล็อกที่ใกล้ที่สุด:

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

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

addต่อไปนี้จะไม่ทำงานเพราะตัวแปรที่เรียกว่าก่อนที่จะมีค่าฟังก์ชั่นได้รับมอบหมายให้ตัวแปร

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

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

โค้ดด้านบนนั้นเหมือนกันในการทำงานกับรหัสด้านล่าง โปรดทราบว่าการกำหนดอย่างชัดเจนadd = undefinedนั้นฟุ่มเฟือยเพราะการทำเช่นvar add;นั้นเหมือนกันvar add=undefinedทุกประการ

var add = undefined;

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

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

ข้อมูลต่อไปนี้ใช้งานไม่ได้เนื่องจากมีvar add=ความfunction add()ต้องการสูงกว่า

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

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

  1. (ฟังก์ชั่น) . ชื่อ

ชื่อของฟังก์ชั่นfunction thefuncname(){}คือthefuncnameเมื่อมีการประกาศด้วยวิธีนี้

function foobar(a, b){}

console.log(foobar.name);

var a = function foobar(){};

console.log(a.name);

มิฉะนั้นถ้าทำงานถูกประกาศเป็นfunction(){}ที่ฟังก์ชั่น .name เป็นตัวแปรแรกที่ใช้ในการจัดเก็บฟังก์ชั่น

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

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

หากไม่มีการตั้งค่าตัวแปรให้กับฟังก์ชันดังนั้นชื่อฟังก์ชันคือสตริงว่าง ( "")

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

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

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

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

  1. ประสิทธิภาพ

ใน Spidermonkey ของ V8 และ Firefox ของ Google อาจมีความแตกต่างในการรวบรวม JIST ไม่กี่ microsecond แต่ท้ายที่สุดผลลัพธ์ก็เหมือนกันทุกประการ เพื่อพิสูจน์สิ่งนี้เรามาตรวจสอบประสิทธิภาพของ JSPerf ที่ microbenchmarks โดยการเปรียบเทียบความเร็วของโค้ดขนาดสั้นสองอัน ทดสอบ JSPerf จะพบได้ที่นี่ และที่testsare jsben.ch พบได้ที่นี่ อย่างที่คุณเห็นมีความแตกต่างที่น่าสังเกตเมื่อไม่ควรมี หากคุณเป็นคนที่คลั่งไคล้การแสดงอย่างฉันจริง ๆ มันอาจจะคุ้มค่ากับการที่คุณพยายามลดจำนวนของตัวแปรและฟังก์ชั่นในขอบเขตโดยเฉพาะการกำจัด polymorphism (เช่นการใช้ตัวแปรเดียวกันเพื่อเก็บสองประเภทที่แตกต่างกัน)

  1. ความแปรปรวนแปรผัน

เมื่อคุณใช้varคำหลักเพื่อประกาศตัวแปรคุณสามารถกำหนดค่าที่แตกต่างให้กับตัวแปรเช่นนั้นได้อีกครั้ง

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

อย่างไรก็ตามเมื่อเราใช้คำสั่ง const การอ้างอิงตัวแปรจะไม่เปลี่ยนรูป ซึ่งหมายความว่าเราไม่สามารถกำหนดค่าใหม่ให้กับตัวแปรได้ อย่างไรก็ตามโปรดทราบว่าเรื่องนี้ไม่ได้ทำให้เนื้อหาของตัวแปรเปลี่ยนรูป: ถ้าคุณทำแล้วคุณยังสามารถทำconst arr = [] arr[10] = "example"ทำสิ่งที่ชอบarr = "new value"หรือarr = []ทำผิดพลาดดังที่เห็นด้านล่าง

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

ที่น่าสนใจถ้าเราประกาศตัวแปรแล้วไม่เปลี่ยนรูปของตัวแปรเป็นเช่นเดียวกับการประกาศด้วยfunction funcName(){}var

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

"บล็อกที่ใกล้ที่สุด" คืออะไร

"บล็อกที่ใกล้ที่สุด" คือ "ฟังก์ชั่นที่ใกล้ที่สุด" (รวมถึงฟังก์ชั่นอะซิงโครนัสฟังก์ชั่นเครื่องกำเนิดและฟังก์ชั่นเครื่องกำเนิดไฟฟ้าแบบอะซิงโครนัส) อย่างไรก็ตามสิ่งที่น่าสนใจคือfunction functionName() {}พฤติกรรมvar functionName = function() {}เมื่ออยู่ในบล็อกแบบไม่ปิดรายการที่อยู่นอกการปิดบัญชีดังกล่าว สังเกต.

  • ปกติ var add=function(){}

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

  • ปกติ function add(){}

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

  • ฟังก์ชัน

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

  • คำสั่ง (เช่น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;
    }
}

  • ฟังก์ชั่น Arrow ด้วย var add=function()

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

  • ฟังก์ชั่นลูกศรด้วย function add()

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


สิ่งนี้สมควรที่จะเป็นคำตอบที่ได้รับการยอมรับและมีชื่อเสียงมากที่สุด
แอรอนจอห์นซาบู

18

@EugeneLazutkinให้ตัวอย่างที่เขาตั้งชื่อฟังก์ชั่นที่กำหนดเพื่อให้สามารถใช้shortcut()เป็นการอ้างอิงภายในกับตัวเอง John Resigให้อีกตัวอย่างหนึ่ง - การคัดลอกฟังก์ชันเรียกซ้ำที่กำหนดให้กับวัตถุอื่นในJavascript Advanced Learningของเขากวดวิชา ในขณะที่การกำหนดฟังก์ชั่นให้กับคุณสมบัติไม่ใช่คำถามที่นี่ฉันขอแนะนำให้ลองทำตามขั้นตอน - รันโค้ดโดยคลิกที่ปุ่มที่มุมขวาบนแล้วดับเบิลคลิกที่รหัสเพื่อแก้ไขความชอบของคุณ

ตัวอย่างจากบทช่วยสอน: การโทรซ้ำ yell() :

การทดสอบล้มเหลวเมื่อนำวัตถุนินจาดั้งเดิมออก (หน้า 13)

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

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

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

หากคุณตั้งชื่อฟังก์ชันที่จะเรียกซ้ำการทดสอบจะผ่าน (หน้า 14)

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

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

17

ความแตกต่างอีกอย่างที่ไม่ได้กล่าวถึงในคำตอบอื่นคือถ้าคุณใช้ฟังก์ชั่นที่ไม่ระบุชื่อ

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

และใช้มันเป็นตัวสร้างเช่นเดียวกับใน

var one = new functionOne();

จากนั้นone.constructor.nameจะไม่ถูกกำหนด Function.nameไม่ได้มาตรฐาน แต่รองรับโดย Firefox, Chrome, เบราว์เซอร์ที่มาจาก Webkit และ IE 9+

กับ

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

two.constructor.nameมันเป็นไปได้ที่จะดึงชื่อของตัวสร้างเป็นสตริงกับ


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

ในตัวอย่างนี้ทั้งสองใหม่กลายเป็นฟังก์ชั่นทั่วโลกเพราะไม่มี var
Waqas Tahir

16

อันแรก (function doSomething (x)) ควรเป็นส่วนหนึ่งของสัญกรณ์วัตถุ

คนที่สอง ( var doSomething = function(x){ alert(x);}) doSomethingเป็นเพียงการสร้างฟังก์ชั่นที่ไม่ระบุชื่อและกำหนดให้กับตัวแปร, ดังนั้น doSomething () จะเรียกใช้ฟังก์ชัน

คุณอาจต้องการทราบว่าการประกาศฟังก์ชั่นและการแสดงออกของฟังก์ชั่นคืออะไร

การประกาศฟังก์ชั่นกำหนดตัวแปรฟังก์ชั่นที่มีชื่อโดยไม่ต้องมีการกำหนดตัวแปร การประกาศฟังก์ชันเกิดขึ้นในรูปแบบสแตนด์อโลนและไม่สามารถซ้อนภายในบล็อกที่ไม่ใช่ฟังก์ชันได้

function foo() {
    return 3;
}

ECMA 5 (13.0) กำหนดไวยากรณ์เป็น
function Identifier (การเลือก FormalParameterList ) {FunctionBody}

ในเงื่อนไขข้างต้นชื่อฟังก์ชันสามารถมองเห็นได้ภายในขอบเขตและขอบเขตของพาเรนต์ (มิฉะนั้นจะไม่สามารถเข้าถึงได้)

และในฟังก์ชั่นการแสดงออก

การแสดงออกของฟังก์ชั่นกำหนดฟังก์ชั่นเป็นส่วนหนึ่งของไวยากรณ์การแสดงออกที่มีขนาดใหญ่กว่า (โดยทั่วไปการกำหนดตัวแปร) ฟังก์ชั่นที่กำหนดผ่านการแสดงออกฟังก์ชั่นสามารถตั้งชื่อหรือไม่ระบุชื่อ การแสดงออกของฟังก์ชั่นไม่ควรเริ่มต้นด้วย "ฟังก์ชั่น"

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

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

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

ECMA 5 (13.0) กำหนดไวยากรณ์เป็น
ฟังก์ชัน Identifier opt (การเลือก FormalParameterList ) {FunctionBody}


16

ฉันแสดงความแตกต่างด้านล่าง:

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

    ดูฟังก์ชั่นด้านล่าง:

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

    นี่เป็นเพราะในระหว่างการดำเนินการดูเหมือนว่า: -

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

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

    ฟังก์ชั่นเดียวกันโดยใช้ฟังก์ชั่นการแสดงออก:

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

    นี่เป็นเพราะในระหว่างการดำเนินการดูเหมือนว่า:

    function outerFunction() {
       var foo = undefined;
       var foo = undefined;
    
       foo = function() {
          return 1;
       };
       return foo ();
       foo = function() {   // This function expression is not reachable
          return 2;
       };
    }
    alert(outerFunction()); // Displays 1
  2. ไม่ปลอดภัยที่จะเขียนการประกาศฟังก์ชันในบล็อกที่ไม่ใช่ฟังก์ชั่นราวกับว่าเพราะจะไม่สามารถเข้าถึงได้

    if (test) {
        function x() { doSomething(); }
    }
  3. การแสดงออกของฟังก์ชั่นที่มีชื่อเหมือนด้านล่างอาจไม่สามารถใช้งานได้ในเบราว์เซอร์ Internet Explorer ก่อนรุ่น 9

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

1
@Arjun ปัญหาคืออะไรหากคำถามถูกถามเมื่อหลายปีก่อน? คำตอบไม่เพียง แต่จะเป็นประโยชน์ต่อ OP แต่ผู้ใช้ SO ทั้งหมดอาจเกิดขึ้นไม่ว่าจะถามคำถามเมื่อใด และมีอะไรผิดปกติกับการตอบคำถามที่มีคำตอบที่ยอมรับแล้ว?
SantiBailors

1
@Arjun คุณต้องเข้าใจการตอบคำถามเก่าไม่เลว ถ้ามันจะเป็นเช่นนั้นดังนั้นจะมีอุปสรรคดังกล่าว ลองนึกภาพว่ามีการเปลี่ยนแปลงใน API (แม้ว่าจะไม่ใช่ในบริบทของคำถามนี้) และมีคนเห็นและให้คำตอบกับ API ใหม่ไม่ควรได้รับอนุญาตหรือไม่? จนกว่าและหากคำตอบไม่สมเหตุสมผลและไม่ได้อยู่ที่นี่คำตอบนั้นจะถูกลดระดับและลบออกโดยอัตโนมัติ คุณไม่จำเป็นต้องไปยุ่งกับมัน !!!!
Sudhansu Choudhary

15

หากคุณจะใช้ฟังก์ชั่นเหล่านั้นเพื่อสร้างวัตถุคุณจะได้รับ:

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

ฉันดูเหมือนจะทำซ้ำไม่ได้ console.log(objectOne.__proto__);พิมพ์ "functionOne {}" ในคอนโซลของฉัน ความคิดใด ๆ ว่าทำไมถึงเป็นเช่นนี้?
Mike

ฉันไม่สามารถทำซ้ำได้เช่นกัน
daremkd

1
นี่คือความสามารถในการดีบักเกอร์ของคุณ (เพื่อแสดง "คลาส" ของวัตถุที่บันทึกไว้) และส่วนใหญ่สามารถรับชื่อแม้กระทั่งสำหรับนิพจน์ฟังก์ชันนิรนามในทุกวันนี้ Btw คุณควรทำให้ชัดเจนว่าไม่มีความแตกต่างในการทำงานระหว่างสองอินสแตนซ์
Bergi

12

ในแง่ของการโต้แย้ง "ฟังก์ชั่นที่มีชื่อปรากฏขึ้นในการติดตามสแต็ก" เครื่องมือ JavaScript ที่ทันสมัยมีความสามารถในการแสดงฟังก์ชั่นที่ไม่ระบุชื่อ

ในการเขียนนี้ V8, SpiderMonkey, Chakra และ Nitro มักจะอ้างถึงฟังก์ชั่นที่มีชื่อตามชื่อของพวกเขา พวกเขามักจะอ้างถึงฟังก์ชั่นที่ไม่ระบุชื่อโดยตัวระบุถ้ามันมี

SpiderMonkey สามารถคิดชื่อของฟังก์ชั่นที่ไม่ระบุชื่อที่ส่งคืนจากฟังก์ชั่นอื่น ที่เหลือไม่ได้

หากคุณต้องการให้ตัววนซ้ำและการเรียกกลับที่สำเร็จปรากฏขึ้นในการติดตามคุณสามารถตั้งชื่อสิ่งเหล่านั้นด้วย ...

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

แต่ส่วนใหญ่มันไม่คุ้มค่าที่จะเครียด

บังเหียน ( ซอ )

'use strict';

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

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

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

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

V8

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

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

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

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

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

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

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

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

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

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

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

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

ลิงแมงมุม

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


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


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


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


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


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


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


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


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


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


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


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

Chakra

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

ใน JavaScript มีวิธีสร้างฟังก์ชันสองวิธี:

  1. ประกาศฟังก์ชั่น:

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

    นี่เป็นพื้นฐานที่อธิบายได้ด้วยตนเองใช้ในหลายภาษาและเป็นมาตรฐานในตระกูลภาษา C เราประกาศฟังก์ชั่นที่กำหนดและดำเนินการโดยเรียกมันว่า

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

  2. ฟังก์ชั่นการแสดงออก:

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

    JavaScript มีฟังก์ชันชั้นหนึ่งนั่นคือสร้างฟังก์ชั่นและกำหนดให้กับตัวแปรเช่นเดียวกับที่คุณสร้างสตริงหรือตัวเลขและกำหนดให้กับตัวแปร นี่คือตัวแปร fn ถูกกำหนดให้กับฟังก์ชัน เหตุผลสำหรับแนวคิดนี้คือฟังก์ชั่นเป็นวัตถุใน JavaScript; fn กำลังชี้ไปที่อินสแตนซ์วัตถุของฟังก์ชันด้านบน เราได้เริ่มต้นฟังก์ชั่นและกำหนดให้กับตัวแปร มันไม่ได้ดำเนินการฟังก์ชันและกำหนดผลลัพธ์

การอ้างอิง: ไวยากรณ์ประกาศฟังก์ชัน JavaScript: var fn = function () {} vs function fn () {}


1
แล้วตัวเลือกที่สามvar fn = function fn() {...}ล่ะ?
chharvey

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

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

1
ใช่ฟังก์ชั่นการตั้งชื่อจะคล้ายกับตัวเลือกของฉัน # 2 การมีตัวระบุไม่ใช่ข้อบังคับเนื่องจากไม่ได้ใช้ เมื่อใดก็ตามที่คุณจะดำเนินการแสดงออกฟังก์ชั่นที่คุณจะใช้ตัวแปรถือวัตถุฟังก์ชั่น ตัวระบุไม่มีจุดประสงค์
Anoop Rai

11

ทั้งสองวิธีแตกต่างกันของการกำหนดฟังก์ชั่น ความแตกต่างคือวิธีที่เบราว์เซอร์ตีความและโหลดลงในบริบทการดำเนินการ

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

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

เหตุผลก็คือในบรรทัดแรกไม่มีการกำหนดค่าให้กับ functionOne และด้วยเหตุนี้จึงไม่ได้กำหนด เราพยายามเรียกมันว่าเป็นฟังก์ชั่นและด้วยเหตุนี้เราจึงได้รับข้อผิดพลาด

ในบรรทัดที่สองเราจะกำหนดการอ้างอิงของฟังก์ชั่นที่ไม่ระบุชื่อให้ functionOne

กรณีที่สองเป็นการประกาศฟังก์ชันที่โหลดก่อนที่จะมีการเรียกใช้รหัสใด ๆ ดังนั้นหากคุณชอบสิ่งต่อไปนี้คุณจะไม่ได้รับข้อผิดพลาดใด ๆ เนื่องจากการโหลดจะประกาศก่อนการเรียกใช้โค้ด

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

11

เกี่ยวกับประสิทธิภาพ:

เวอร์ชั่นใหม่ของV8การเพิ่มประสิทธิภาพการแนะนำภายใต้ประทุนหลายอย่างก็SpiderMonkeyเช่นกัน

ตอนนี้แทบไม่มีความแตกต่างระหว่างการแสดงออกและการประกาศ
การแสดงออกของฟังก์ชั่นดูเหมือนจะเร็วขึ้นในขณะนี้

Chrome 62.0.3202 การทดสอบ Chrome

FireFox 55 ทดสอบ Firefox

Chrome Canary 63.0.3225 การทดสอบ Chrome Canary


Anonymousการแสดงออกของฟังก์ชั่นดูเหมือนจะมีประสิทธิภาพที่ดีกว่า กับNamedการแสดงออกของฟังก์ชั่น


Firefox Chrome Canary ChromeFirefox ชื่อ _ ชื่อ Chrome canary ชื่อ _anonymous Chrome ชื่อ _ ชื่อ


1
ใช่ความแตกต่างนี้จึงไม่มีนัยสำคัญที่หวัง devs จะเกี่ยวข้องกับตัวเองด้วยซึ่งวิธีการคือการบำรุงรักษามากขึ้นสำหรับความต้องการเฉพาะของพวกเขามากกว่าที่หนึ่งอาจจะเร็วขึ้น (คุณจะได้รับผล jsperf ที่แตกต่างกันในแต่ละลองขึ้นอยู่กับสิ่งที่เบราว์เซอร์จะทำ - งานจาวาสคริปต์ส่วนใหญ่ไม่จำเป็นต้องเกี่ยวข้องกับการปรับให้เหมาะกับระดับนี้มากไป)
squidbe

@squidbe ไม่มีความแตกต่าง ดูที่นี่: jsperf.com/empty-tests-performance
Jack Giffin

10

พวกมันค่อนข้างคล้ายกับความแตกต่างเล็ก ๆ น้อย ๆ อันแรกคือตัวแปรที่กำหนดให้กับฟังก์ชั่นที่ไม่ระบุตัวตน (Function Declaration) และอันที่สองเป็นวิธีปกติในการสร้างฟังก์ชั่นใน JavaScript (Declaration ฟังก์ชั่นนิรนาม) :

1. ฟังก์ชั่นการแสดงออก

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

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

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

2. ประกาศฟังก์ชั่น

function functionTwo() {
    // Some code
}

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

นี่เป็นวิธีปกติในการเรียกใช้ฟังก์ชั่นใน JavaScript ฟังก์ชั่นนี้สามารถเรียกใช้ก่อนที่คุณจะประกาศได้เช่นเดียวกับใน JavaScript ทุกฟังก์ชั่นจะได้รับ Hoisted แต่ถ้าคุณใช้ 'เข้มงวด' สิ่งนี้จะไม่เกิดขึ้นตามที่คาดไว้ เพื่อเรียกฟังก์ชั่นปกติทั้งหมดซึ่งไม่ใหญ่ในบรรทัดและไม่มีฟังก์ชั่นคอนสตรัค

นอกจากนี้หากคุณต้องการข้อมูลเพิ่มเติมเกี่ยวกับการทำงานของ hoisting ใน JavaScript โปรดไปที่ลิงก์ด้านล่าง:

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting


1
...also this way of declaring is a better way to create Constructor functions in JavaScriptขอให้คุณช่วยอธิบายเพิ่มเติมหน่อยได้ไหมฉันสงสัย!
Karl Morrison

เหตุผลหนึ่งก็เพราะฟังก์ชั่นการสร้างในตัวใน JavaScript ที่สร้างขึ้นเช่นฟังก์ชั่นนี้ Number () {[native code]} และคุณไม่ควรสับสนกับฟังก์ชั่นในตัวการอ้างอิงในภายหลังในกรณีนี้จะปลอดภัยกว่า ตั้งค่า neater code แต่ไม่ได้ใช้ hoisting ...
Alireza

8

นี่เป็นเพียงสองวิธีที่เป็นไปได้ของการประกาศฟังก์ชั่นและในวิธีที่สองคุณสามารถใช้ฟังก์ชั่นก่อนการประกาศ


7

new Function()สามารถใช้ในการส่งผ่านร่างกายของฟังก์ชั่นในสตริง ด้วยเหตุนี้สิ่งนี้สามารถใช้ในการสร้างฟังก์ชั่นแบบไดนามิก ส่งผ่านสคริปต์โดยไม่เรียกใช้สคริปต์

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

secondFunction()

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