วัตถุประสงค์ของคำหลัก var คืออะไรและเมื่อใดที่ฉันควรใช้ (หรือละเว้น)


1543

หมายเหตุ : คำถามนี้ถูกถามจากมุมมองของ ECMAScript เวอร์ชัน 3 หรือ 5 คำตอบอาจล้าสมัยเมื่อมีการเปิดตัวฟีเจอร์ใหม่ในการเปิดตัว ECMAScript 6

ฟังก์ชันของvarคำหลักใน JavaScript คืออะไรและอะไรคือความแตกต่างระหว่าง

var someNumber = 2;
var someFunction = function() { doSomething; }
var someObject = { }
var someObject.someProperty = 5;

และ

someNumber = 2;
someFunction = function() { doSomething; }
someObject = { }
someObject.someProperty = 5;

?

เมื่อไหร่ที่คุณจะใช้อันใดอันหนึ่งและเพราะอะไร / ทำอะไร?


3
เมื่อประกาศการผูกมัด var การขึ้นบรรทัดใหม่หลังจากเครื่องหมายจุลภาคส่งผลกระทบต่อพฤติกรรมหรือไม่ var x = 1, y = 2, [return] z = 3;
Alfabravo

4
การไม่ใช้ "var" จะทำให้คุณเปิดเผยในกรณีที่ชื่อตัวแปรที่คุณเลือกเกิดขึ้นเป็นตัวแปรโกลบอลที่กำหนดไว้ก่อนหน้านี้ ดูการเดินทางของฉันด้วยความเศร้าโศกที่นี่: stackoverflow.com/questions/16704014/…
Scott C Wilson

5
โพสต์ meloncard บล็อก @Ray กโตอัลของ (มั่นเหมาะมูลค่าการอ่าน) ได้ย้ายไปblog.safeshepherd.com/23/how-one-missing-var-ruined-our-launch
เทพเจ้า

ฉันไม่เคยคิดเลยว่าบทกวีจะเป็นแรงบันดาลใจให้ฉันพิจารณาปัญหาที่เกิดจากการเขียนโปรแกรม
Félix Gagnon-Grenier

1
@Gibolt แต่ดูที่วันที่คำถามมันเป็นธรรมที่ไม่เป็นธรรมเรียกคำถาม 2009 ที่จะบอกว่า แม้ว่ามันจะยังคงใช้ได้เหมือนในวันที่ปัจจุบันสำหรับการบำรุงรักษา แต่ก็มีรหัส "JS สมัยใหม่" จำนวนมาก
Andre Figueiredo

คำตอบ:


1356

หากคุณอยู่ในขอบเขตทั่วโลกก็มีความแตกต่างไม่มาก อ่านคำตอบของ Kangaxเพื่อขอคำอธิบาย

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

// These are both globals
var foo = 1;
bar = 2;

function()
{
    var foo = 1; // Local
    bar = 2;     // Global

    // Execute an anonymous function
    (function()
    {
        var wibble = 1; // Local
        foo = 2; // Inherits from scope above (creating a closure)
        moo = 3; // Global
    }())
}

หากคุณไม่ได้ทำการบ้านคุณต้องใช้var:

var x; // Declare x

31
"ไม่แตกต่างกันมากจริง ๆ " == "ไม่มีความแตกต่าง"?
Alex

65
จริง ๆ แล้วใช่มีความแตกต่าง :) ความแตกต่างนั้นสำคัญหรือไม่เป็นคำถามอื่น ดูคำตอบของฉันเพิ่มเติมได้ที่: stackoverflow.com/questions/1470488/…
kangax

4
ฉันคิดว่านั่นอาจเป็นประเด็นของอเล็กซ์ซึ่งเป็นสาเหตุที่เขาเขียนโดยใช้โอเปอเรเตอร์ "เท่ากับ"!
James Bedford

18
มันเหมือนกับการถ่ายภาพตัวเองด้วย Railgun ... ลืมใส่ 'var' ไว้ข้างหน้าตัวแปรและจบลงด้วยการปรับเปลี่ยนตัวแปรที่อยู่ในห่วงโซ่ขอบเขต ... ลองโน้มน้าวใจ Java / C / Python / etc นักพัฒนาที่ JavaScript มีคุณค่า ฮา! ข้อผิดพลาด C / C ++ ดูดีโดยคมชัด ลองนึกภาพว่าต้องทำการดีบัก JavaScript ... และบางคนก็ทำเช่นนั้น และมีรหัสมาก (และไม่ได้รหัสที่ง่ายใจคุณ) เขียนใน JavaScript ...
อัลบัสดัมเบิลด

6
หากคุณอยู่ในขอบเขตทั่วโลกก็ไม่มีความแตกต่าง >> มีความแตกต่างซึ่งอธิบายไว้ในคำตอบด้านล่าง
Max Koretskyi

746

มีความแตกต่างคือ

var x = 1 ประกาศตัวแปร xในขอบเขตปัจจุบัน (รู้จักในบริบทการดำเนินการ) หากการประกาศปรากฏขึ้นในฟังก์ชั่น - มีการประกาศตัวแปรท้องถิ่น ถ้าอยู่ในขอบเขตทั่วโลกตัวแปรทั่วโลกจะถูกประกาศ

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

ตอนนี้สังเกตว่ามันไม่ได้ประกาศตัวแปรทั่วโลกมันสร้างทรัพย์สินส่วนกลาง

ความแตกต่างระหว่างทั้งสองนั้นบอบบางและอาจสร้างความสับสนเว้นแต่ว่าคุณเข้าใจว่าการประกาศตัวแปรยังสร้างคุณสมบัติ (เฉพาะในตัวแปรที่เป็นวัตถุ) และทุกคุณสมบัติใน Javascript (ดี ECMAScript) มีแฟล็กที่แน่นอนที่อธิบายคุณสมบัติของพวกเขา - ReadOnly, DontEnum และ DontDelete

เนื่องจากการประกาศตัวแปรสร้างคุณสมบัติด้วยแฟล็ก DontDelete ความแตกต่างระหว่างvar x = 1และx = 1(เมื่อดำเนินการในขอบเขตทั่วโลก) คือการประกาศในอดีต - การประกาศตัวแปร - สร้างคุณสมบัติ DontDelete'able และอันหลังไม่ได้ ด้วยเหตุนี้คุณสมบัติที่สร้างขึ้นผ่านการกำหนดโดยนัยนี้จะสามารถลบออกจากออบเจ็กต์ทั่วโลกและไม่สามารถลบอันแรกที่สร้างผ่านการประกาศตัวแปร -

แต่นี่เป็นเพียงทฤษฎีแน่นอนและในทางปฏิบัติมีความแตกต่างระหว่างทั้งสองมากขึ้นเนื่องจากข้อบกพร่องต่าง ๆ ในการใช้งาน (เช่นจาก IE)

หวังว่ามันจะสมเหตุสมผล:)


[อัพเดท 2010/12/16]

ใน ES5 (ECMAScript 5; ภาษามาตรฐานฉบับที่ 5 เมื่อเร็ว ๆ นี้) มีสิ่งที่เรียกว่า "โหมดเข้มงวด" - โหมดการเลือกใช้ภาษาซึ่งเปลี่ยนพฤติกรรมของการมอบหมายที่ไม่ได้ประกาศเล็กน้อย ในโหมดเข้มงวดมอบหมายให้ตัวระบุไม่ได้ประกาศเป็นReferenceError เหตุผลสำหรับเรื่องนี้คือการจับการมอบหมายโดยไม่ตั้งใจป้องกันการสร้างคุณสมบัติทั่วโลกที่ไม่พึงประสงค์ เบราว์เซอร์ที่ใหม่กว่าบางตัวเริ่มการสนับสนุนในโหมดเข้มงวดแล้ว ดูตัวอย่างตารางที่เข้ากันได้ของฉัน


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

3
@Mageek เขาอาจจะเกี่ยวกับตัวแปรที่ประกาศโดย eval ซึ่งจะถูกลบออก ฉันเขียนบทความเกี่ยวกับบล็อกครั้งนี้
kangax

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

@kangax จะเกิดอะไรขึ้นถ้าตัวอย่างสองตัวอย่างสุดท้ายของ Alex ผสมกัน: var someObject = {}และsomeObject.someProperty = 5? จะsomePropertyกลายเป็นโลกในขณะที่วัตถุมันเป็นทรัพย์สินของซากท้องถิ่น?
snapfractalpop

1
ชื่อสเปคสำหรับสิ่งที่ @kangax เรียกDontDeleteธงที่กำหนด (= false)คุณสามารถอ่านเกี่ยวกับเรื่องนี้ในการไปถึงObject.definePropertyและObject.getOwnPropertyDescriptor
พอลเอส

137

การพูดว่าเป็นความแตกต่างระหว่าง " ระดับท้องถิ่นและระดับโลก " นั้นไม่ถูกต้องทั้งหมด

มันอาจจะเป็นการดีกว่าถ้าคิดว่าเป็นความแตกต่างระหว่าง " ท้องถิ่นและใกล้ที่สุด " ที่ใกล้ที่สุดสามารถเป็นโลกได้ แต่ไม่ได้เป็นเช่นนั้นเสมอไป

/* global scope */
var local = true;
var global = true;

function outer() {
    /* local scope */
    var local = true;
    var global = false;

    /* nearest scope = outer */
    local = !global;

    function inner() {
        /* nearest scope = outer */
        local = false;
        global = false;

        /* nearest scope = undefined */
        /* defaults to defining a global */
        public = global;
    }
}

3
ไม่ใช่ขอบเขตouterที่ใกล้ที่สุดที่คุณกำหนดvar global = false;ใช่ไหม
Snekse

@Snekse: 'ใกล้ที่สุด' จะไม่ใช้งานเมื่อ <code> var global = false; </code> ถูกประกาศ ในการประกาศนั้น 'global' จะอยู่ในขอบเขตของ outer () เนื่องจากมีการใช้ 'var' ในการประกาศ เนื่องจาก 'var' ไม่ได้ใช้ใน inner () มันจะเปลี่ยนค่าในระดับถัดไปขึ้นไปซึ่งอยู่ด้านนอก ()
มิทช์

ฉันสงสัยว่าความคิดเห็นของคุณจะเปลี่ยนไปหรือไม่ถ้าคุณเปลี่ยนบรรทัดvar global = local;นั้นเป็นกรณีนี้ขอบเขตอันใกล้ของ Local จะเป็นขอบเขตด้านนอก "local" ที่กำหนดไว้อย่างแข็งขัน แม้ว่ามันจะแปลกถ้าคุณจะเปลี่ยนบรรทัดเดียวกันเป็นvar global = globalในกรณีที่ขอบเขตที่ใกล้ที่สุดเมื่อค้นหาค่าของglobalจะเป็นระดับที่ขอบเขตหน้าต่างทั่วโลก
Snekse

80

เมื่อ Javascript ทำงานในเบราว์เซอร์รหัสทั้งหมดของคุณจะถูกล้อมรอบด้วยคำสั่ง with เช่น:

with (window) {
    //Your code
}

ข้อมูลเพิ่มเติมเกี่ยวกับwith- MDN

เนื่องจากvarประกาศตัวแปรในขอบเขตปัจจุบันจึงไม่มีความแตกต่างระหว่างการประกาศvar ภายในหน้าต่างและไม่ประกาศตัวแปรเลย

ความแตกต่างเกิดขึ้นเมื่อคุณไม่ได้อยู่ในหน้าต่างโดยตรงเช่นภายในฟังก์ชั่นหรือภายในบล็อก

การใช้varให้คุณซ่อนตัวแปรภายนอกที่มีชื่อเหมือนกัน ด้วยวิธีนี้คุณสามารถจำลองตัวแปร "ส่วนตัว" แต่นั่นเป็นอีกหัวข้อ

กฎของหัวแม่มือคือการใช้เสมอvarเพราะมิฉะนั้นคุณจะเสี่ยงต่อการแนะนำข้อบกพร่องที่ลึกซึ้ง

แก้ไข: หลังจากวิพากษ์วิจารณ์ที่ฉันได้รับฉันต้องการเน้นย้ำต่อไปนี้:

  • varประกาศตัวแปรในขอบเขตปัจจุบัน
  • ขอบเขตทั่วโลกคือ window
  • ไม่ได้ใช้varประกาศโดยปริยายvarในขอบเขตส่วนกลาง (หน้าต่าง)
  • การประกาศตัวแปรในขอบเขตโกลบอล (หน้าต่าง) ที่ใช้varนั้นเหมือนกับการละเว้นตัวแปร
  • การประกาศตัวแปรในขอบเขตที่แตกต่างจากการใช้หน้าต่างvar ไม่ใช่สิ่งเดียวกับการประกาศตัวแปรที่ไม่มีvar
  • ประกาศvarอย่างชัดเจนเสมอเพราะเป็นแนวปฏิบัติที่ดี

1
ฉันไม่ได้ลงคะแนนให้คุณ แต่ขอบเขตน่าจะเป็นคำที่ดีกว่าหน้าต่าง คุณกำลังอธิบายทั้งหมดเป็นป้านนิดหน่อย
Robert Harvey

4
ฉันเรียกสิ่งต่าง ๆ โดยใช้ชื่อคุณต้องการเรียกมันว่า "global scope" มันก็โอเค แต่ฝั่งไคลเอ็นต์โดยการประชุมคือวัตถุหน้าต่างซึ่งเป็นองค์ประกอบสุดท้ายของขอบเขตขอบเขตนั่นคือสาเหตุที่คุณสามารถโทรหาทุก ฟังก์ชั่นและวัตถุทุกชิ้นในหน้าต่างโดยไม่ต้องเขียน "หน้าต่าง"
kentaromiura

2
+1 นี่เป็นคำอธิบายที่ดีจริงๆ - ฉันไม่เคยได้ยินปัญหา var / no var มาจากกรอบ (ไม่มีปุนตั้งใจ) เช่นนี้
ดั๊ก

คำตอบส่วนใหญ่นี้เลิกใช้แล้วletใน ES6
Evan Carroll

3
@EvanCarroll คำตอบนี้ยังไม่ถูกต้องทางเทคนิคเนื่องจากการละเว้น var ไม่ได้ประกาศตัวแปรใด ๆ แต่จะสร้างคุณสมบัติที่ลบได้ในวัตถุทั่วโลกนอกเหนือจาก ES5 "ใช้เข้มงวด" โหมดส่วนใหญ่คำตอบที่เห็นได้ชัดว่าไม่ถูกต้อง ไม่ได้พิจารณาในคำตอบนี้ตั้งแต่เวลาที่คำถามไม่มีการอ้างอิงถึงจาวาสคริปต์ (เพิ่มเมื่อวานนี้) ซึ่งหมายความว่ามาตรฐานการอ้างอิง (ในเวลานั้น) คือ ECMA 262 ฉบับที่ 3
kentaromiura

43

ใช้varคำสำคัญเพื่อประกาศตัวแปรเสมอ ทำไม? การเขียนโค้ดที่ดีควรมีเหตุผลเพียงพอในตัวมันเอง แต่ไม่ได้หมายความว่ามันถูกประกาศในขอบเขตทั่วโลก (ตัวแปรเช่นนี้เรียกว่า "โดยนัย" ทั่วโลก) Douglas Crockford ไม่แนะนำให้ใช้ globals โดยนัยและเป็นไปตามแนวทางการเข้ารหัสของApple JavaScript :

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


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

@cHao ฉันคิดว่าgood coding practiceมีเหตุผลเพียงพอเสมอหากเป็นแนวทางปฏิบัติที่ดีที่สุดซึ่งเป็นสิ่งที่ผู้เขียน Javascript หลายคนแนะนำ
Chris S

8
@ChrisS: ไม่ "การเขียนโปรแกรมที่ดี" ไม่ใช่เหตุผลในตัวเอง เหตุผลก็ถือว่าการปฏิบัติที่ดีเป็นสิ่งที่สำคัญ หากผู้เขียนเหล่านั้นไม่บอกเหตุผลที่พวกเขาแนะนำข้อเสนอแนะของพวกเขาไม่ควรมีน้ำหนักใด ๆ หากคุณไม่เห็นด้วยกับเหตุผลคุณมีอิสระที่จะพิจารณาคำแนะนำที่ไม่ดี และถ้าคุณทำตามโดยไม่เคยถามว่าทำไมนั่นคือลัทธิการขนส่งสินค้าเริ่มต้นขึ้น
cHao

30

นี่เป็นตัวอย่างที่ดีของวิธีที่คุณสามารถหลีกเลี่ยงการประกาศตัวแปรท้องถิ่นด้วยvar:

<script>
one();

function one()
{
    for (i = 0;i < 10;i++)
    {
        two();
        alert(i);
    }
}

function two()
{
    i = 1;
}
</script>

( iถูกรีเซ็ตทุกการวนซ้ำของลูปเนื่องจากไม่ได้ประกาศเฉพาะในforลูป แต่เป็นแบบโกลบอล) ทำให้เกิดลูปไม่สิ้นสุด


อ๊ะ! ฉันสามารถจินตนาการข้อบกพร่องทั้งหมดที่อาจเกิดจากการพิมพ์ผิด
BonsaiOak

2
ฉันอยากรู้อยากเห็นทำไมคุณผ่านฉันเป็นข้อโต้แย้งที่สอง ()? (ในวง for) นั้นซ้ำซ้อนไหม?
กาลิน

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

ข้อผิดพลาดหรือคุณสมบัติ?
TheMaster

13

ฉันจะบอกว่ามันจะดีกว่าที่จะใช้varในสถานการณ์ส่วนใหญ่

ตัวแปรโลคัลจะเร็วกว่าตัวแปรในขอบเขตโกลบอลเสมอ

หากคุณไม่ได้ใช้varเพื่อประกาศตัวแปรตัวแปรนั้นจะอยู่ในขอบเขตส่วนกลาง

สำหรับข้อมูลเพิ่มเติมคุณสามารถค้นหา "JavaScript chain scope" ใน Google


หากคุณประกาศตัวแปรโดยใช้คำหลัก var มันจะถูกสร้างขึ้นที่รันไทม์ดังนั้นมันควรจะช้าลงไหม? เพราะส่วนอื่นถูกสร้างขึ้นในเวลาที่แยกวิเคราะห์
BarışVelioğlu

@RyuKaplan - เฮ้มันเป็นเรื่องจริงเหรอ? ฉันลองใช้ Google และไม่สามารถรับข้อมูลใด ๆ ในเรื่องนี้ได้! คุณมีอำนาจที่มาสำหรับการยืนยันว่า? ขอบคุณ
ไมค์หนู

@RyuKaplan การแยกวิเคราะห์ / การคอมไพล์แตกต่างจากการรันโค้ดจริง ๆ
gcampbell

11

อย่าใช้var!

varเป็นวิธีก่อน ES6 ในการประกาศตัวแปร เรากำลังอยู่ในอนาคตและคุณควรเขียนโค้ดเช่นนี้

ใช้constและlet

constควรใช้ 95% ของกรณี มันทำให้ดังนั้นการอ้างอิงตัวแปรไม่สามารถเปลี่ยนอาร์เรย์จึงวัตถุและ DOM constคุณสมบัติโหนดสามารถเปลี่ยนและมีแนวโน้มที่ควรจะเป็น

letควรใช้สำหรับตัวแปรใด ๆ ที่คาดว่าจะกำหนดใหม่ ซึ่งรวมถึงภายในวง ถ้าคุณเคยเขียนเกินกว่าการเริ่มต้นใช้งานvarName =let

ทั้งสองมีการกำหนดขอบเขตระดับบล็อกตามที่คาดไว้ในภาษาอื่น ๆ ส่วนใหญ่


2
แทนที่ทั้งหมดของคุณ 'var' โดย 'const' (แทนที่ทั้งหมด) คุณจะสังเกตได้อย่างรวดเร็วว่าตัวแปรที่คุณกำหนดใหม่อยู่ที่ใด หากคุณมีจำนวนมากเกินไปคุณอาจใช้รหัส anti-patternly: ตัวแปรที่กำหนดใหม่ได้ส่วนใหญ่สามารถฝังในการปิดหรือเป็นคุณสมบัติของวัตถุ หากคุณมีไม่กี่: ใช้ 'ให้' สำหรับพวกเขา ในที่สุดหากตัวแปรบางตัวที่ไม่ได้ทำการลบด้วย 'var' ตัวแปรเหล่านั้นจะยังคงไม่ถูกประกาศและยังคงปรากฏอยู่ในพื้นที่ส่วนกลางระวัง เกี่ยวกับความคิดเห็น @Gibolt 'ภายในสำหรับลูป' ขอแนะนำให้หลีกเลี่ยงการวนซ้ำเช่นนี้ใน "95% ของกรณี" ;-): วิธีการแบบอาร์เรย์นั้นยอดเยี่ยม
allez l'OM

ด้วยการบอกว่าควรใช้ const ใน 95% ของคดีดูเหมือนว่าเราจะออกไปจากแนวปฏิบัติที่ดีและเข้าสู่ความเชื่อ
Agamemnus

9

ความแตกต่างอื่น ๆ เช่น

var a = a || [] ; // works 

ในขณะที่

a = a || [] ; // a is undefined error.

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

6
@Lucek เพราะvar aถูกยกขึ้นไปด้านบนของขอบเขตและชุดโมฆะซึ่งประกาศ แต่ไม่ได้เริ่มต้นตัวแปรแล้วในการกำหนดคุณมีการอ้างอิงถึงตัวแปร null []ไม่ได้กำหนดซึ่งประเมินเป็นเท็จและการตั้งค่าที่ได้รับมอบหมายไป ในระยะหลังคุณมีการมอบหมายให้ทรัพย์สินของทรัพย์สินa aคุณสามารถกำหนดให้กับคุณสมบัติที่ไม่มีอยู่ - สร้างขึ้นบนการมอบหมาย แต่คุณไม่สามารถอ่านจากคุณสมบัติที่ไม่มีอยู่หากไม่ได้รับการReferenceErrorโยนให้คุณ
Evan Carroll

1
@EvanCarroll: ได้รับการยกไปด้านบนของขอบเขตและได้รับการตั้งค่าเป็นไม่ได้กำหนดแทนโมฆะ
mithunsatheesh

8

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


8

ไม่มีvar- ตัวแปรทั่วโลก

แนะนำอย่างยิ่งให้ใช้คำสั่งเสมอvarเพราะตัวแปรทั่วโลก init ในบริบทท้องถิ่น - เป็นความชั่วร้าย แต่ถ้าคุณต้องการเคล็ดลับสกปรกคุณควรเขียนความคิดเห็นเมื่อเริ่มต้นหน้า:

/* global: varname1, varname2... */

3

นี่คือโค้ดตัวอย่างที่ฉันเขียนให้คุณเข้าใจแนวคิดนี้:

var foo = 5; 
bar = 2;     
fooba = 3;

// Execute an anonymous function
(function() {    
    bar = 100;             //overwrites global scope bar
    var foo = 4;           //a new foo variable is created in this' function's scope
    var fooba = 900;       //same as above
    document.write(foo);   //prints 4
    document.write(bar);   //prints 100
    document.write(fooba); //prints 900
})();

document.write('<br/>');
document.write('<br/>');
document.write(foo);       //prints 5
document.write(bar);       //prints 100
document.write(fooba);     //prints 3

2
ฟังก์ชั่นนี้ไม่ได้หมายความว่า "ไม่ระบุชื่อ" ในความเป็นจริงมันเป็นเรื่องเกี่ยวกับชื่ออย่างเห็นได้ชัดเท่าที่จะเป็นไปได้
Ingo Bürk

ขอบคุณสำหรับการแก้ไขคำตอบของคุณตามคำติชมของ Ingo Bürkเพื่อให้ "ฟังก์ชั่นที่ไม่ระบุชื่อ" ระบุชื่อจริง
เดฟเบอร์ตัน

3

@ Chris S ให้เป็นตัวอย่างที่ดีการจัดแสดงความแตกต่างของการปฏิบัติ (และอันตราย) ระหว่างและไม่มีการvar varนี่คืออีกอันหนึ่งฉันพบว่าสิ่งนี้อันตรายเป็นพิเศษเพราะความแตกต่างสามารถมองเห็นได้ในสภาพแวดล้อมแบบอะซิงโครนัสเท่านั้นดังนั้นจึงสามารถลื่นหลุดได้ง่ายในระหว่างการทดสอบ

ตามที่คุณคาดหวังผลลัพธ์ตัวอย่างต่อไปนี้["text"]:

function var_fun() {
  let array = []
  array.push('text')
  return array
}

console.log(var_fun())

ดังนั้นข้อมูลโค้ดต่อไปนี้ (โปรดสังเกตสิ่งที่ขาดไปletก่อนหน้านี้array):

function var_fun() {
  array = []
  array.push('text')
  return array
}

console.log(var_fun())

การดำเนินการจัดการข้อมูลแบบอะซิงโครนัสยังคงสร้างผลลัพธ์เดียวกันโดยใช้ตัวจัดการเดียว:

function var_fun() {
  array = [];
  return new Promise(resolve => resolve()).then(() => {
    array.push('text')
    return array
  })
}

var_fun().then(result => {console.log(result)})

แต่มีพฤติกรรมที่แตกต่างกับหลาย ๆ คน:

function var_fun() {
  array = [];
  return new Promise(resolve => resolve()).then(() => {
    array.push('text')
    return array
  })
}

[1,2,3].forEach(i => {
  var_fun().then(result => {console.log(result)})
})

ใช้ให้อย่างไรก็ตาม:

function var_fun() {
  let array = [];
  return new Promise(resolve => resolve()).then(() => {
    array.push('text')
    return array
  })
}

[1,2,3].forEach(i => {
  var_fun().then(result => {console.log(result)})
})


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

2

ในขณะที่มีคนพยายามที่จะเรียนรู้สิ่งนี้เป็นวิธีที่ฉันเห็น ตัวอย่างข้างต้นอาจจะซับซ้อนเกินไปสำหรับผู้เริ่มต้น

หากคุณเรียกใช้รหัสนี้:

var local = true;
var global = true;


function test(){
  var local = false;
  var global = false;
  console.log(local)
  console.log(global)
}

test();

console.log(local);
console.log(global);

เอาต์พุตจะอ่านว่า: false, false, true, true

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

var local = true;
var global = true;


function test(){
  local = false;
  global = false;
  console.log(local)
  console.log(global)
}

test();

console.log(local);
console.log(global);

ผลลัพธ์คือ false, false, false, false

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


2

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

ดูสคริปต์ด้านล่างในการดำเนินการที่นี่ที่ jsfiddle

a = 1;// Defined outside the function without var
var b = 1;// Defined outside the function with var
alert("Starting outside of all functions... \n \n a, b defined but c, d not defined yet: \n a:" + a + "\n b:" + b + "\n \n (If I try to show the value of the undefined c or d, console.log would throw 'Uncaught ReferenceError: c is not defined' error and script would stop running!)");

function testVar1(){
    c = 1;// Defined inside the function without var
    var d = 1;// Defined inside the function with var
    alert("Now inside the 1. function: \n a:" + a + "\n b:" + b + "\n c:" + c + "\n d:" + d);

    a = a + 5;
    b = b + 5;
    c = c + 5;
    d = d + 5;

    alert("After added values inside the 1. function: \n a:" + a + "\n b:" + b + "\n c:" + c + "\n d:" + d);
};


testVar1();
alert("Run the 1. function again...");
testVar1();

function testVar2(){
    var d = 1;// Defined inside the function with var
    alert("Now inside the 2. function: \n a:" + a + "\n b:" + b + "\n c:" + c + "\n d:" + d);

    a = a + 5;
    b = b + 5;
    c = c + 5;
    d = d + 5;

    alert("After added values inside the 2. function: \n a:" + a + "\n b:" + b + "\n c:" + c + "\n d:" + d);
};

testVar2();

alert("Now outside of all functions... \n \n Final Values: \n a:" + a + "\n b:" + b + "\n c:" + c + "\n You will not be able to see d here because then the value is requested, console.log would throw error 'Uncaught ReferenceError: d is not defined' and script would stop. \n ");
alert("**************\n Conclusion \n ************** \n \n 1. No matter declared with or without var (like a, b) if they get their value outside the function, they will preserve their value and also any other values that are added inside various functions through the script are preserved.\n 2. If the variable is declared without var inside a function (like c), it will act like the previous rule, it will preserve its value across all functions from now on. Either it got its first value in function testVar1() it still preserves the value and get additional value in function testVar2() \n 3. If the variable is declared with var inside a function only (like d in testVar1 or testVar2) it will will be undefined whenever the function ends. So it will be temporary variable in a function.");
alert("Now check console.log for the error when value d is requested next:");
alert(d);

ข้อสรุป

  1. ไม่ว่ามีการประกาศโดยมีหรือไม่มี var (เช่น a, b) หากพวกเขาได้รับค่าของพวกเขานอกฟังก์ชั่นพวกเขาจะรักษาคุณค่าของพวกเขาและค่าอื่น ๆ ที่เพิ่มเข้ามาในฟังก์ชั่นต่างๆผ่านสคริปต์จะถูกเก็บไว้
  2. หากตัวแปรถูกประกาศโดยไม่มี var ภายในฟังก์ชั่น (เช่น c) มันจะทำหน้าที่เหมือนกฎก่อนหน้านี้มันจะเก็บค่าไว้ในทุกฟังก์ชั่นนับจากนี้เป็นต้นไป ไม่ว่าจะได้รับค่าแรกในฟังก์ชั่น testVar1 () ก็ยังคงรักษาค่าและรับค่าเพิ่มเติมในฟังก์ชั่น testVar2 ()
  3. หากประกาศตัวแปรด้วย var ภายในฟังก์ชั่นเท่านั้น (เช่น d ใน testVar1 หรือ testVar2) มันจะไม่ถูกกำหนดเมื่อใดก็ตามที่ฟังก์ชั่นสิ้นสุดลง ดังนั้นมันจะเป็นตัวแปรชั่วคราวในฟังก์ชั่น

ขอบคุณที่สละเวลาสร้างตัวอย่างเพื่อสาธิตหัวข้อนี้ รหัสด้านบนหายไปส่วนด้านล่างดังนั้นคุณอาจต้องการแก้ไขคำตอบของคุณ: a = 1; // กำหนดนอกฟังก์ชันโดยไม่ต้อง var var b = 1; // กำหนดนอกฟังก์ชันด้วยการแจ้งเตือน var ("เริ่มต้นจากฟังก์ชั่นทั้งหมด ... \ n \ na, b ถูกกำหนด แต่ c, d ยังไม่ได้กำหนด: \ na: "+ a +" \ nb: "+ b +" \ n \ n (ถ้าฉันพยายามแสดงค่าของ c ที่ไม่ได้กำหนด หรือ d, console.log จะโยนข้อผิดพลาด 'Uncaught ReferenceError: c ไม่ได้กำหนดไว้' และสคริปต์จะหยุดทำงาน!) ");
Sankofa

1

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

someFunction() {
    var a = some_value; /*a has local scope and it cannot be accessed when this
    function is not active*/
    b = a; /*here it places "var b" at top of script i.e. gives b global scope or
    uses already defined global variable b */
}

1

นอกจากปัญหาขอบเขตแล้วบางคนยังพูดถึงการชักรอกแต่ก็ไม่มีใครยกตัวอย่าง นี่คือขอบเขตหนึ่งของโลก:

console.log(noErrorCase);
var noErrorCase = "you will reach that point";

console.log(runTimeError);
runTimeError = "you won't reach that point";


0

โดยไม่ใช้ตัวแปร "var" สามารถกำหนดได้เมื่อตั้งค่าเท่านั้น ในตัวอย่าง:

my_var;

ไม่สามารถทำงานได้ในขอบเขตทั่วโลกหรือขอบเขตอื่นควรมีค่าเช่น:

my_var = "value";

ในอีกทางหนึ่งคุณสามารถกำหนด vaiable like;

var my_var;

ค่าของมันคือundefined(ค่าของมันไม่ได้nullและไม่เท่ากับที่nullน่าสนใจ)


my_var;จริงๆแล้วเป็นคำสั่งที่ถูกต้อง
lexicore

มันเป็นคำสั่งที่ถูกต้องหากมีการกำหนดตัวแปรไว้ก่อน มิฉะนั้นจะเกิดข้อผิดพลาด "... ไม่ได้กำหนด"
umut

3
มันเป็นคำสั่งที่ถูกต้องโดยไม่คำนึงว่าตัวแปรถูกกำหนดมาก่อนหรือไม่ :) คำสั่งที่ถูกต้องสามารถโยนข้อผิดพลาดมันไม่ได้ทำให้คำสั่งที่ไม่ถูกต้อง
lexicore

ฉันสับสนเกี่ยวกับเรื่องนี้ คำสั่งที่ถูกต้องคืออะไร? และคุณสามารถให้ตัวอย่างคำสั่งที่ไม่ถูกต้องกับฉันได้ไหม
umut

ฉันจะต้องขอโทษ - ECMAScript ไวยากรณ์มากเกินไปเมื่อเร็ว ๆ นี้ my_var;เป็นที่ถูกต้องคำสั่งการแสดงออก /my_var;จะเป็นคำสั่งที่ไม่ถูกต้อง แต่อย่างที่ฉันบอกว่านี่เป็นไวยากรณ์คาถาฉันขอโทษจริง ๆ แล้วความคิดเห็นของฉันไม่เหมาะสม
lexicore

0

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

เมื่อตัวแปรถูกกำหนดโดยไม่ต้องใช้คำหลัก var สิ่งที่ดูเหมือนว่าเป็นการดำเนินการ "การมอบหมาย" อย่างง่าย

เมื่อค่าถูกกำหนดให้กับตัวแปรใน javascript ล่ามจะพยายามค้นหา“ การประกาศตัวแปร” ในบริบท / ขอบเขตเดียวกันกับที่ได้รับมอบหมาย เมื่อล่ามดำเนินการdummyVariable = 20มันจะค้นหาการประกาศของ dummyVariable ที่จุดเริ่มต้นของฟังก์ชัน (เนื่องจากการประกาศตัวแปรทั้งหมดจะถูกย้ายไปที่จุดเริ่มต้นของบริบทโดยล่าม javascript และสิ่งนี้เรียกว่า hoisting)

คุณอาจต้องการดูhoisting ใน javascript

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