ตัวแปร JavaScript ประกาศนอกหรือในวง?


213

ใน AS3 ฉันเชื่อว่าคุณควรเริ่มต้นตัวแปรทั้งหมดนอกลูปเพื่อประสิทธิภาพที่เพิ่มขึ้น เป็นกรณีนี้กับ JavaScript เช่นกัน? แบบไหนดีกว่า / เร็วกว่า / แนวปฏิบัติที่ดีที่สุด

var value = 0;

for (var i = 0; i < 100; i++)
{
    value = somearray[i];
}

หรือ

for (var i = 0 ; i < 100; i++)
{
    var value = somearray[i];
}

7
นอก! ข้างนอกเสมอ
BGerrissen

37
อืมไม่ต้องประกาศตัวแปรได้รับการผลักดันให้ขึ้นอยู่กับขอบเขตการทำงานทั้งใน Javascript และ AS3 ใช่ไหม ถ้าฉันถูกต้องแล้วมันไม่สำคัญ
อะไรต่อมิอะไร

3
@Andy - คุณลองมอบหมายก่อนที่จะประกาศในส่วนของฟังก์ชั่นหรือไม่? บางทีความคิดของคุณอาจทำให้คุณหลงทาง ประสิทธิภาพของ WRT พร้อมการกำหนดขอบเขตแบบพุชหาก JS ถูกตีความแล้วมันจะเคี้ยววงรอบพิเศษภายในบล็อกลูป ถ้าเรียบเรียง (ซึ่งเครื่องยนต์ส่วนใหญ่ทำทุกวันนี้) มันจะไม่สำคัญ
อะไรต่อมิอะไร

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

3
ฉันประหลาดใจที่ไม่มีใครพยายามวัดประสิทธิภาพ ฉันสร้างjsperf ดูเหมือนว่าจะเร็วขึ้นเล็กน้อยเมื่อมีการประกาศภายในลูปสำหรับ Safari และ Firefox ซึ่งตรงกันข้ามกับ Chrome …
Buzut

คำตอบ:


281

มีอย่างไม่แตกต่างกันในความหมายหรือประสิทธิภาพใน JavaScript หรือ ActionScript

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

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

โดยเฉพาะอย่างยิ่ง:

for (var i; i<100; i++)
    do something;

for (var i; i<100; i++)
    do something else;

Crockford จะแนะนำให้คุณลบอันที่สองvar(หรือลบทั้งสองอย่างvarและทำvar i;ข้างบน) และ jslint จะเข้ามาหาคุณในเรื่องนี้ แต่ IMO จะรักษาได้ทั้งสองvars เก็บรหัสที่เกี่ยวข้องทั้งหมดไว้ด้วยกันแทนที่จะมีบิตพิเศษที่ถูกลืมได้ง่ายที่ด้านบนของฟังก์ชัน

โดยส่วนตัวแล้วฉันมักจะประกาศว่าvarเป็นการกำหนดครั้งแรกของตัวแปรในส่วนที่เป็นอิสระของรหัสไม่ว่าจะมีการใช้งานที่แยกต่างหากของชื่อตัวแปรเดียวกันในส่วนอื่น ๆ ของฟังก์ชั่นเดียวกันหรือไม่ สำหรับฉันการประกาศvarทั้งหมดเป็นหูดที่ไม่พึงประสงค์ JS (มันจะดีกว่าที่จะมีตัวแปรเริ่มต้นเป็นท้องถิ่น); ฉันไม่เห็นว่าเป็นหน้าที่ของฉันที่จะทำซ้ำข้อ จำกัด ของ [การแก้ไขแบบเก่า] ANSI C ใน JavaScript เช่นกัน

(*: นอกเหนือจากในฟังก์ชั่นที่ซ้อนกัน)


4
ฉันยังไม่สามารถตัดสินใจได้ว่าฉันอยู่กับ Crockford หรือไม่กับอันนี้ การประกาศตัวแปรในบล็อกให้ความรู้สึกเหมือนกับการใช้คำสั่งฟังก์ชั่นตามเงื่อนไข (ซึ่งซุกซน) ... อย่างไรก็ตามฉันเห็นด้วยกับประเด็นของคุณเช่นกัน :) #confused
Daniel Vassallo

20
+1 ฉันไม่เห็นด้วยกับเหตุผลของ Crockford (อ้างถึงในคำตอบของ Daniel) เนื่องจากนักพัฒนา JavaScript เราไม่ควรเขียนโค้ดเพื่อให้โปรแกรมเมอร์ "ตระกูล C" คนอื่นเข้าใจได้ ฉันมักจะใช้varข้างในถ้าบล็อกและลูปเพราะมันเหมาะสมกับฉัน
Andy E

4
-1 OP กำลังถามว่า vars ในเนื้อความของ loop ควรถูกประกาศก่อนที่ loop จะเริ่มต้นหรือไม่ ค่าดัชนีของลูปเป็นกรณีพิเศษอย่างชัดเจน (และถูกยกขึ้น) และไม่ช่วย OP เลย
mkoistinen

21
ดัชนีลูปไม่ใช่กรณีพิเศษพวกเขาจัดการและยกแบบเดียวกับที่ได้รับมอบหมายตามปกติ
bobince

31
+1 Crockford ผิดเกี่ยวกับสิ่งนี้ (และอื่น ๆ แต่ฉันพูดนอกเรื่อง) การขอvarใช้งานที่ด้านบนสุดของฟังก์ชั่นเป็นเพียงการขอให้สร้างตัวแปรส่วนกลางโดยไม่ได้ตั้งใจ และการมีมวลของตัวแปรที่ไม่เกี่ยวข้องทั้งหมดที่ประกาศไว้ในจุดเดียวนั้นไม่มีความหมายทางความหมายโดยเฉพาะอย่างยิ่งเมื่อตัวแปรเหล่านั้นบางตัวอาจไม่ได้ถูกนำมาใช้
MooGoo

64

ในทางทฤษฎีแล้วมันไม่ควรสร้างความแตกต่างใด ๆ กับ JavaScript เนื่องจากภาษาไม่มีขอบเขตบล็อก แต่เป็นเพียงขอบเขตหน้าที่เท่านั้น

ฉันไม่แน่ใจเกี่ยวกับข้อโต้แย้งด้านประสิทธิภาพ แต่Douglas Crockfordยังคงแนะนำว่าvarข้อความควรเป็นข้อความแรกในเนื้อหาของฟังก์ชัน การอ้างอิงจากCode Conventions สำหรับภาษาการเขียนโปรแกรม JavaScript :

JavaScript ไม่มีขอบเขตบล็อกดังนั้นการกำหนดตัวแปรในบล็อกอาจสร้างความสับสนให้โปรแกรมเมอร์ที่มีประสบการณ์กับภาษาตระกูล C อื่น ๆ กำหนดตัวแปรทั้งหมดที่ด้านบนของฟังก์ชั่น

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

function myFunction() {
  var i;    // the scope of the variables is very clear

  for (i = 0; i < 10; i++) {
    // ...
  }
}

8
+1 สำหรับการบอกความจริง OP เกี่ยวกับขอบเขต JS ฉันสงสัยว่าจะลงคะแนนคำตอบที่พูดอย่างอื่น!
spender

1
@ Kieranmaine: ฉันไม่ได้บอกว่ามันไม่ส่งผลกระทบต่อประสิทธิภาพ ฉันเพิ่งโต้แย้งเพื่อวางมันไว้นอกลูป, ไม่เกี่ยวข้องกับการแสดง .... ฉันไม่มีการอ้างอิงใด ๆ สำหรับการโต้แย้งผลงาน แต่คุณไม่ได้อ้างคำตอบใด ๆ ในคำตอบของคุณ :)
Daniel Vassallo

1
@ Kieranmaine: คุณมีแหล่งที่มาสำหรับสิ่งนั้นหรือไม่?
Andy E

5
@ Kieranmaine: AFAIK แม้ว่าคุณจะประกาศตัวแปรที่อยู่ในลูป แต่ecma- / javascriptก็จะชนพวกนั้นในรันไทม์ ที่เรียกว่า "Hoisting" ดังนั้นไม่ควรมีความแตกต่างใด ๆ
jAndy

1
ES6 letมีผลต่อคำตอบนี้อย่างไร
jbyrd

58

ECMA-/Javascriptภาษาhoistsตัวแปรใด ๆ ที่มีการประกาศใดก็ได้ไปด้านบนของฟังก์ชั่น นั่นเป็นเพราะภาษานี้ไม่ได้function scopeและไม่ได้มีblock scopeอื่น ๆ อีกหลาย C-เช่นภาษา ที่ยังเป็นที่รู้จักกัน
lexical scope

ถ้าคุณประกาศสิ่งที่ชอบ

var foo = function(){
    for(var i = 0; i < 10; i++){
    }
};

นี่hoistedคือ:

var foo = function(){
    var i;
    for(i = 0; i < 10; i++){
    }
}

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


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

5
ES6 letมีผลต่อคำตอบนี้อย่างไร
jbyrd

13

ในปีหน้าเบราว์เซอร์ทั้งหมดจะมีเอ็นจิน JS ที่รวบรวมรหัสไว้ล่วงหน้าดังนั้นความแตกต่างของประสิทธิภาพ (ซึ่งมาจากการแยกวิเคราะห์รหัสบล็อกเดียวกันซ้ำแล้วซ้ำอีกรวมถึงการดำเนินการมอบหมาย) ควรจะไม่มีความสำคัญ

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


6

ข้อควรพิจารณาอีกประการหนึ่งตอนนี้เรามีletและconstใน ES2015 คือตอนนี้คุณสามารถกำหนดขอบเขตตัวแปรเฉพาะให้กับบล็อกลูปได้ ดังนั้นหากคุณไม่ต้องการตัวแปรเดียวกันนอกลูป (หรือถ้าการวนซ้ำแต่ละครั้งขึ้นอยู่กับการดำเนินการที่ทำกับตัวแปรนั้นในการทำซ้ำก่อนหน้า) ก็อาจจะดีกว่าที่จะทำสิ่งนี้:

for (let i = 0; i < 100; i++) {
    let value = somearray[i];
    //do something with `value`
}

4

ฉันเพิ่งจะทดสอบง่ายๆใน Chrome ลองเล่นซอในเบราว์เซอร์ของคุณและดูผลลัพธ์

  var count = 100000000;
    var a = 0;
    console.log(new Date());

    for (var i=0; i<count; i++) {
      a = a + 1
    }

    console.log(new Date());

    var j;
    for (j=0; j<count; j++) {
      a = a + 1;
    }

    console.log(new Date());

    var j;
    for (j=0; j<count; j++) {
        var x;
        x = x + 1;
    }

    console.log(new Date());

ผลลัพธ์คือการทดสอบครั้งสุดท้ายใช้เวลา ~ 8 วินาทีและ 2 ก่อนหน้านี้เพียง ~ 2 วินาที ทำซ้ำมากและไม่คำนึงถึงการสั่งซื้อ

ดังนั้นนี่เป็นข้อพิสูจน์สำหรับฉันว่าคนหนึ่งควรประกาศขวดด้านนอกของลูป กรณีที่อยากรู้อยากเห็นสำหรับฉันเป็นคนแรกที่ฉันประกาศiในคำสั่งสำหรับ () สิ่งนี้ดูเหมือนจะเร็วเท่ากับการทดสอบครั้งที่ 2 ที่ฉันประกาศดัชนีล่วงหน้า


14
@KP: ผลลัพธ์จะได้รับการพิสูจน์ก็ต่อเมื่อคุณทดสอบด้วยตนเองหรือถ้ามีคนจำนวนมากยืนยันพวกเขา @mkoistinen: ฉันสร้างการทดสอบธรรมjsfiddle.net/GM8nk หลังจากเรียกใช้สคริปต์หลายครั้งใน Chrome 5 ฉันจะเห็นว่าไม่มีผู้ชนะที่สอดคล้องกัน ทั้งสามรูปแบบมีประสิทธิภาพดีกว่ารูปแบบอื่น ๆ หลังจากรีเฟรชเล็กน้อย -1 จากฉันฉันกลัว หมายเหตุคุณอาจต้องการเรียกใช้สิ่งนี้ในเบราว์เซอร์อื่น IE และ Fx ไม่ชอบซ้ำ 100 ล้านครั้ง
Andy E

1
@AndyE ว้าวดังนั้นจากการทดสอบเพียงนี้ IE ก็ดูดอีก 100 เท่า =)
mkoistinen

2
ผลลัพธ์มีอยู่ทั่วสถานที่สำหรับฉันไม่มีผู้ชนะข้ามเบราว์เซอร์ที่ชัดเจนแม้ว่าบางครั้งจะมีความแตกต่างของความเร็วที่สำคัญ แปลก. ฉันคิดว่าซอของแอนดี้เป็นการทดสอบที่ดีกว่าการวางผู้สมัครแต่ละคนในหน้าที่ของตัวเอง ... แน่นอนว่าถ้าสคริปต์ต้นฉบับทำงานนอกฟังก์ชั่นมันไม่ควรจะทดสอบอะไรเลยเพราะvarมันประกาศว่าเป็นตัวแปรระดับโลก เป็นโลกต่อไป
bobince

4
มากกว่าหนึ่งปีหลังจากความจริงแล้ว แต่SHAPOW
sdleihssirhc

2
นี่ไม่ใช่ของฉัน แต่ฉันคิดว่าบางส่วนของคุณจะสนใจ: jsperf.com/var-in-for-loop
m1

1

JavaScript เป็นภาษาที่เขียนที่ด้านล่างโดย C หรือ C ++ ฉันไม่แน่ใจว่าเป็นภาษาใด และหนึ่งในวัตถุประสงค์ของมันคือการประหยัดการจัดการหน่วยความจำภายใน แม้แต่ใน C หรือ C ++ คุณไม่ต้องกังวลว่าจะใช้ทรัพยากรจำนวนมากหรือไม่เมื่อมีการประกาศตัวแปรภายในลูป ทำไมคุณควรกังวลใน JavaScript?


1
C หรือ C ++ อาจอยู่ที่ด้านล่างของ JavaScript แต่เราไม่ควรลืมว่าเบราว์เซอร์แปลง JavaScript เป็นภาษาพื้นฐาน (C, C ++) ดังนั้นประสิทธิภาพจึงขึ้นอยู่กับเบราว์เซอร์
Kira

3
@Kira ไม่ได้แปลงเป็น C / C ++ จริงๆ Javascript ได้รับการรวบรวมเป็นชุดคำสั่งซึ่งดำเนินการโดยรันไทม์ JS (เช่นเครื่องเสมือนที่ทำงานในเบราว์เซอร์) หลักการเดียวกันนี้ใช้กับภาษาไดนามิกอื่น ๆ เช่น Python และ Ruby
Anthony E

@ AnthonyE ขอบคุณสำหรับข้อมูล ฉันไม่แน่ใจเกี่ยวกับการแปลง JS เป็น C หรือ C ++ ดังนั้นฉันอาจใช้ในความคิดเห็นของฉัน
Kira

0

นั่นขึ้นอยู่กับสิ่งที่คุณพยายามที่จะบรรลุ ... หากvalueสมมติว่าเป็นเพียงตัวแปรชั่วคราวในการบล็อกแบบลูปคุณจะเห็นได้อย่างชัดเจนว่าใช้รูปแบบที่สอง นอกจากนี้ยังมีเหตุผลและ verbose มากขึ้น


ฉันพบว่าการผลักดันการประกาศตัวแปรทั้งหมดไปยังด้านบน - รวมถึงตัวแปรชั่วคราว - จริง ๆ แล้วสามารถนำไปสู่ความสับสนตามที่เพิ่งได้รับ 'เสียงดัง'
Daniel Sokolowski

0

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

function a() {
   console.log('Function a() starts');
   console.log(new Date());
    var j;
    for (j=0; j<100000000; j++) {
        var x;
        x = x + 1;
    }
    console.log(new Date());
    console.log('Function a() Ends');
}
a()
function b() {
console.log('Function B() starts');
   console.log(new Date());
    var a;
    var j;
    for (j=0; j<100000000; j++) {
      a = a + 1;
    }
    console.log(new Date());
    console.log('Function B() Ends');
}
b()

ผลลัพธ์แสดงให้เห็นในกรณีของฉัน

Function a() starts
VM121:3 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:9 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:10 Function a() Ends
VM121:14 Function B() starts
VM121:15 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:21 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:22 Function B() Ends

ขอบคุณ - MyFavs.in


ในทั้งสองกรณีคุณประกาศ j นอกวง! X_x
john ktejik

ฉันลองใช้ใน Chromium 81 letแทนที่จะใช้varและa()มีแนวโน้มที่จะช้าลงเล็กน้อย (เช่น 120 vs 115 ms = ~ 6% = IMO เล็กน้อย)
mikiqex

-1

คำถามที่นี่เป็นพื้นประกาศวาภายในวง แค่คิดว่าจะเกิดอะไรขึ้นถ้าคุณทำสิ่งนี้:

var a = 30;
var a = 50;
var a = 60;

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

ดังนั้นจึงเป็นการดีที่จะประกาศตัวแปรด้านบน - 1 เพื่อให้อ่านได้ง่าย 2. สร้างนิสัยที่ดี


1
"เมื่อคุณประกาศตัวแปรภายในลูปมันจะไม่ประกาศกี่ครั้งที่ลูปทำงาน" <- ไม่นั่นไม่ถูกต้อง การประกาศตัวแปรได้รับการยกดังนั้นทั้งหมดที่คุณเหลืออยู่คือการมอบหมาย
Matthias

-2

เกี่ยวกับประสิทธิภาพหลังจากรันการทดสอบบน Chrome, Firefox และ jsperf บนระบบปฏิบัติการ Linux ดูเหมือนว่าจะมีความแตกต่างระหว่างการประกาศตัวแปรในลูปและลูป มันเป็นความแตกต่างเล็ก ๆ น้อย ๆ แต่สิ่งนี้ยังรวมกับจำนวนการทำซ้ำและปริมาณของการประกาศตัวแปร

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

// inline
for (var ai = 0, al = 100000000, av; ai < al; ai++) {
    av = av + 1;
}

// outside
var bv;
var bl = 100000000;
for (var bi = 0; bi < bl; bi++) {
    bv = bv + 1;
}

สังเกตว่าตัวแปร 'al' และ 'av' อยู่ในบรรทัดสำหรับการประกาศลูป การประกาศแบบอินไลน์นี้ทำให้ฉันมีประสิทธิภาพที่ดีขึ้นอย่างต่อเนื่อง แม้จะมีการประกาศตัวแปรนอกวง ความแตกต่างด้านประสิทธิภาพอีกครั้งมีขนาดเล็กมาก

https://jsperf.com/outside-inline-for-loop-ase/1


สำหรับฉันการทดสอบของคุณให้ไว้ในลูป และอย่างไรก็ตามมันก็ไม่ได้ความแตกต่างนั้นเล็กเกินกว่าที่จะสรุปได้และคำตอบที่ยอมรับได้อธิบายอย่างชัดเจนว่าไม่มีความแตกต่าง
Ulysse BN

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