ขอบเขตของตัวแปรใน JavaScript คืออะไร?


2013

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


4
นี่คือลิงค์ที่น่าสนใจอีกข้อหนึ่งที่ควรคำนึงถึงปัญหานี้: "การอธิบายขอบเขต JavaScript และการปิด "
วิศวกรซอฟต์แวร์แบบเต็มส

9
นี่คือบทความที่อธิบายได้เป็นอย่างดี ทุกสิ่งที่คุณจำเป็นต้องรู้เกี่ยวกับขอบเขตตัวแปร Javascript
Saurab Parakh

2
กล่าวถึงก่อนหน้า e-book ของไคล์ซิมป์สันสามารถใช้ได้ที่จะอ่านบน Github และจะบอกคุณทั้งหมดที่คุณต้องรู้เกี่ยวกับ JavaScript ขอบเขตและการปิด คุณสามารถค้นหาได้ที่นี่: github.com/getify/You-Dont-Know-JS/blob/master/ ......มันเป็นส่วนหนึ่งของชุดหนังสือ "คุณไม่รู้จัก JS"ซึ่งเหมาะสำหรับทุกคนที่อยากรู้ เพิ่มเติมเกี่ยวกับ JavaScript
3rik82

คำตอบ:


2535

TLDR

JavaScript มีการกำหนดขอบเขตและคำศัพท์ (หรือที่เรียกว่าแบบคงที่) ซึ่งหมายความว่าคุณสามารถบอกขอบเขตของตัวระบุได้โดยดูที่ซอร์สโค้ด

สี่ขอบเขตคือ:

  1. ทั่วโลก - ทุกอย่างสามารถมองเห็นได้
  2. ฟังก์ชั่น - มองเห็นได้ภายในฟังก์ชั่น (และฟังก์ชั่นย่อยและบล็อก)
  3. บล็อก - มองเห็นได้ภายในบล็อก (และบล็อกย่อย)
  4. โมดูล - มองเห็นได้ภายในโมดูล

นอกเหนือจากกรณีพิเศษของขอบเขตโกลบอลและโมดูลตัวแปรจะถูกประกาศโดยใช้var(ขอบเขตฟังก์ชั่น), let(ขอบเขตบล็อก) และconst(ขอบเขตบล็อก) การประกาศตัวระบุรูปแบบอื่น ๆ ส่วนใหญ่มีขอบเขตบล็อกในโหมดเข้มงวด

ภาพรวม

ขอบเขตคือขอบเขตของ codebase ซึ่งตัวระบุนั้นถูกต้อง

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

ขอบเขตถูกสร้างขึ้นจากการซ้อนการลิงก์ของสภาวะแวดล้อมคำศัพท์โดยแต่ละระดับในการซ้อนจะสอดคล้องกับสภาวะแวดล้อมคำศัพท์ของบริบทการดำเนินการบรรพบุรุษ

สภาพแวดล้อมศัพท์ที่เชื่อมโยงเหล่านี้สร้างขอบเขต "โซ่" ความละเอียดของตัวระบุเป็นกระบวนการค้นหาตามสายโซ่นี้เพื่อหาตัวระบุที่ตรงกัน

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

มีปัจจัยที่เกี่ยวข้องสามประการในการตัดสินใจขอบเขตของตัวระบุใน JavaScript:

  1. วิธีประกาศตัวระบุ
  2. ตำแหน่งที่มีการประกาศตัวระบุ
  3. ไม่ว่าคุณจะอยู่ในโหมดเข้มงวดหรือโหมดไม่เข้มงวด

สามารถประกาศตัวระบุวิธีได้บางวิธี:

  1. var, letและconst
  2. พารามิเตอร์ฟังก์ชั่น
  3. จับพารามิเตอร์บล็อก
  4. ประกาศฟังก์ชั่น
  5. นิพจน์ฟังก์ชันที่ระบุชื่อ
  6. คุณสมบัติที่กำหนดโดยปริยายบนออบเจ็กต์โกลบอล (เช่น. หายไปvarในโหมดที่ไม่เข้มงวด)
  7. import งบ
  8. eval

ตัวระบุตำแหน่งบางตัวสามารถประกาศได้:

  1. บริบทระดับโลก
  2. ฟังก์ชั่นร่างกาย
  3. บล็อกสามัญ
  4. ด้านบนของโครงสร้างการควบคุม (เช่นลูปถ้าในขณะที่ ฯลฯ )
  5. โครงสร้างร่างกายควบคุม
  6. โมดูล

รูปแบบการประกาศ

var

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

ให้และ const

ตัวระบุที่ประกาศใช้letและconst มีขอบเขตบล็อกนอกเหนือจากเมื่อมีการประกาศโดยตรงในบริบทส่วนกลางซึ่งในกรณีนี้จะมีขอบเขตทั่วโลก

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

function f() {
    function g() {
        console.log(x)
    }
    let x = 1
    g()
}
f() // 1 because x is hoisted even though declared with `let`!

ชื่อพารามิเตอร์ฟังก์ชั่น

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

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

การประกาศฟังก์ชั่นมีขอบเขตบล็อกในโหมดเข้มงวดและขอบเขตฟังก์ชันในโหมดไม่เข้มงวด หมายเหตุ: โหมดที่ไม่เข้มงวดเป็นชุดที่ซับซ้อนของกฎฉุกเฉินตามการใช้งานทางประวัติศาสตร์ที่แปลกประหลาดของเบราว์เซอร์ที่แตกต่างกัน

นิพจน์ฟังก์ชันที่ระบุชื่อ

นิพจน์ฟังก์ชันที่ระบุชื่อจะถูกกำหนดขอบเขตให้กับตัวเอง (เช่นเพื่อการเรียกซ้ำ)

คุณสมบัติที่กำหนดโดยปริยายบนอ็อบเจ็กต์โกลบอล

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

EVAL

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

ตัวอย่าง

ต่อไปนี้จะโยน ReferenceError เนื่องจากชื่อที่x, yและไม่มีนอกหมายของฟังก์ชั่นzf

function f() {
    var x = 1
    let y = 1
    const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)

ต่อไปนี้จะโยน ReferenceError สำหรับyและz, แต่ไม่ใช่xเพราะ, การมองเห็นของxไม่ถูก จำกัด โดยบล็อก บล็อกที่กำหนดร่างของโครงสร้างการควบคุมเช่นif, forและwhileประพฤติในทำนองเดียวกัน

{
    var x = 1
    let y = 1
    const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope

ในต่อไปxนี้สามารถมองเห็นได้นอกวงเนื่องจากvarมีขอบเขตฟังก์ชั่น:

for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)

... เนื่องจากพฤติกรรมนี้คุณต้องระวังเกี่ยวกับการปิดตัวแปรที่ประกาศใช้varในลูป มีเพียงหนึ่งอินสแตนซ์ของตัวแปรที่xประกาศที่นี่และมันตั้งอยู่นอกตรรกะของลูป

พิมพ์ต่อไปนี้5ห้าครั้งแล้วพิมพ์5เป็นครั้งที่หกสำหรับconsole.logวงนอก:

for(var x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop

งานพิมพ์ต่อไปนี้undefinedเนื่องจากxถูกบล็อกขอบเขต การเรียกกลับถูกเรียกใช้ทีละอะซิงโครนัส พฤติกรรมใหม่สำหรับletตัวแปรหมายความว่าแต่ละฟังก์ชั่นที่ไม่ระบุชื่อปิดมากกว่าตัวแปรที่แตกต่างกันชื่อx(เหมือนมันจะได้ทำกับvar) และจำนวนเต็ม0ผ่าน4จะถูกพิมพ์ .:

for(let x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined

ต่อไปนี้จะไม่ส่งต่อReferenceErrorเนื่องจากการแสดงผลของxบล็อกไม่ถูก จำกัด มันจะอย่างไรก็ตามพิมพ์undefinedเพราะตัวแปรยังไม่ได้เริ่มต้น (เพราะifคำสั่ง)

if(false) {
    var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised

ตัวแปรที่ประกาศที่ด้านบนของforลูปที่ใช้letจะถูกกำหนดขอบเขตไว้ที่เนื้อความของลูป:

for(let x = 0; x < 10; ++x) {} 
console.log(typeof x) // undefined, because `x` is block-scoped

ต่อไปนี้จะโยนReferenceErrorเนื่องจากการเปิดเผยxถูก จำกัด โดยบล็อก:

if(false) {
    let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped

ตัวแปรที่ประกาศใช้var, letหรือconstทุกขอบเขตโมดูล:

// module1.js

var x = 0
export function f() {}

//module2.js

import f from 'module1.js'

console.log(x) // throws ReferenceError

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

var x = 1
console.log(window.hasOwnProperty('x')) // true

letและconstในบริบทโลกไม่ได้เพิ่มคุณสมบัติให้กับวัตถุทั่วโลก แต่ยังคงมีขอบเขตทั่วโลก:

let x = 1
console.log(window.hasOwnProperty('x')) // false

สามารถพิจารณาพารามิเตอร์ของฟังก์ชั่นที่จะประกาศในฟังก์ชั่นร่างกาย:

function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function

พารามิเตอร์ catch block ถูกกำหนดขอบเขตไว้ที่เนื้อหา catch-block:

try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block

นิพจน์ฟังก์ชันที่มีชื่อถูกกำหนดขอบเขตเฉพาะนิพจน์เท่านั้น:

(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression

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

x = 1 // implicitly defined property on the global object (no "var"!)

console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true

ในโหมดที่ไม่เข้มงวดการประกาศฟังก์ชันมีขอบเขตฟังก์ชัน ในโหมดเข้มงวดพวกเขามีขอบเขตบล็อก

'use strict'
{
    function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped

มันทำงานอย่างไรภายใต้ประทุน

ขอบเขตถูกกำหนดให้เป็นขอบเขตศัพท์ของรหัสซึ่งตัวระบุนั้นถูกต้อง

ใน JavaScript ทุกฟังก์ชั่นวัตถุมีการ[[Environment]]อ้างอิงที่ซ่อนอยู่ซึ่งเป็นการอ้างอิงถึงสภาพแวดล้อมศัพท์ของบริบทการดำเนินการ (กรอบสแต็ก) ที่มันถูกสร้างขึ้น

เมื่อคุณเรียกใช้ฟังก์ชั่น[[Call]]วิธีการที่ซ่อนอยู่จะถูกเรียก วิธีนี้จะสร้างบริบทการดำเนินการใหม่และสร้างการเชื่อมโยงระหว่างบริบทการดำเนินการใหม่และสภาพแวดล้อมคำศัพท์ของฟังก์ชั่น - วัตถุ มันทำได้โดยการคัดลอก[[Environment]]ค่าในฟังก์ชั่นวัตถุไปยังเขตข้อมูลอ้างอิงด้านนอกบนสภาพแวดล้อมศัพท์ของบริบทการดำเนินการใหม่

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

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

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


280
ไม่ได้ใกล้เคียงกับการครอบคลุม แต่นี่อาจเป็นชุดของขอบเขต Javascript ที่ต้องรู้หนึ่งต้องได้อย่างมีประสิทธิภาพแม้อ่านจาวาสคริปต์ที่ทันสมัย
Triptych

148
คำตอบที่ได้คะแนนสูงไม่แน่ใจว่าทำไม มันเป็นเพียงตัวอย่างจำนวนมากที่ไม่มีคำอธิบายที่เหมาะสมดูเหมือนว่าจะสร้างความสับสนให้กับการสืบทอดต้นแบบ (เช่นการแก้ไขคุณสมบัติ) ด้วยขอบเขตของขอบเขต (เช่นการแก้ไขตัวแปร) ที่ครอบคลุม (และถูกต้อง) คำอธิบายของขอบเขตและคุณสมบัติความละเอียดอยู่ใน comp.lang.javascript บันทึกคำถามที่พบบ่อย
RobG

109
@RobG ได้รับการจัดอันดับสูงเพราะมีประโยชน์และเข้าใจได้ง่ายสำหรับโปรแกรมเมอร์ที่หลากหลาย catachresis เล็ก ๆ น้อย ๆ ลิงค์ที่คุณโพสต์ในขณะที่มีประโยชน์สำหรับมืออาชีพบางคนไม่สามารถเข้าใจได้สำหรับคนส่วนใหญ่ที่เขียน Javascript ในวันนี้ รู้สึกอิสระที่จะแก้ไขปัญหาการตั้งชื่อใด ๆ โดยการแก้ไขคำตอบ
Triptych

7
@ triptych— ฉันเพียงแก้ไขคำตอบเพื่อแก้ไขสิ่งเล็ก ๆ น้อย ๆ ไม่ใช่เรื่องสำคัญ การเปลี่ยน "ขอบเขต" เป็น "คุณสมบัติ" จะแก้ไขข้อผิดพลาด แต่ไม่ใช่ปัญหาของการผสมการสืบทอดและขอบเขตโดยไม่มีความแตกต่างที่ชัดเจนมาก
RobG

24
หากคุณกำหนดตัวแปรในขอบเขตด้านนอกจากนั้นให้คำสั่ง if กำหนดตัวแปรภายในฟังก์ชันที่มีชื่อเดียวกันแม้ว่าจะไม่ได้สาขาก็ตามก็ตามมันจะถูกนิยามใหม่ ตัวอย่าง - jsfiddle.net/3CxVm
Chris S

233

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

องค์ประกอบในห่วงโซ่ขอบเขตนั้นโดยทั่วไปแล้วแผนที่ที่มีตัวชี้ไปยังขอบเขตหลัก

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


1
กลุ่มขอบเขตเป็นอีกคำศัพท์หนึ่งสำหรับ [หน่วยความจำ] การปิด ... สำหรับผู้ที่อ่านที่นี่เพื่อเรียนรู้ / เข้าสู่จาวาสคริปต์
ใหม่ Alexandria

108

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

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


7
ฉันกลัวที่จะเริ่มตอบคำถามนี้ ในฐานะที่เป็นโปรแกรมเมอร์ Javascript จริงฉันรู้ว่าคำตอบจะออกไปได้เร็วแค่ไหน บทความที่ดี
Triptych

10
@Triptych: ฉันรู้ว่าคุณหมายถึงอะไรเกี่ยวกับสิ่งที่ออกไปจากมือ แต่โปรดเพิ่มคำตอบต่อไป ผมได้กล่าวข้างต้นเป็นเพียงแค่จากการทำคู่ของการค้นหาแล้ว ... คำตอบที่เขียนโดยคนที่มีประสบการณ์จริงจะถูกผูกไว้จะดีกว่า โปรดแก้ไขคำตอบของฉันซึ่งผิดแน่นอน!
Jon Skeet

4
อย่างใด Jon Skeet เป็นผู้รับผิดชอบคำตอบที่ได้รับความนิยมสูงสุดใน Stack Overflow
Triptych

75

จาวาสคริปต์ของโรงเรียนเก่า

ตามเนื้อผ้าจาวาสคริปต์มีขอบเขตสองประเภทเท่านั้น:

  1. ขอบเขตทั่วโลก : ตัวแปรเป็นที่รู้จักกันทั่วทั้งแอปพลิเคชันตั้งแต่เริ่มต้นแอปพลิเคชัน(*)
  2. ขอบเขตหน้าที่ : ตัวแปรเป็นที่รู้จักกันในฟังก์ชั่นที่พวกเขาประกาศในจากจุดเริ่มต้นของฟังก์ชั่น(*)

ฉันจะไม่ทำอย่างละเอียดในเรื่องนี้เนื่องจากมีคำตอบอื่น ๆ อีกมากมายที่อธิบายถึงความแตกต่าง


JavaScript ที่ทันสมัย

รายละเอียด JavaScript ล่าสุดตอนนี้ยังอนุญาตให้มีขอบเขตที่สาม:

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

ฉันจะสร้างตัวแปรขอบเขตบล็อกได้อย่างไร

ตามเนื้อผ้าคุณสร้างตัวแปรของคุณเช่นนี้:

var myVariable = "Some text";

ตัวแปรขอบเขตบล็อกถูกสร้างขึ้นเช่นนี้:

let myVariable = "Some text";

ดังนั้นขอบเขตการทำงานและขอบเขตบล็อกแตกต่างกันอย่างไร

เพื่อให้เข้าใจถึงความแตกต่างระหว่างขอบเขตการทำงานและขอบเขตบล็อกให้พิจารณารหัสต่อไปนี้:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

ที่นี่เราจะเห็นได้ว่าตัวแปรของเราjเป็นที่รู้จักเฉพาะในลูปแรก แต่ไม่ใช่ก่อนและหลัง แต่ตัวแปรของเราiเป็นที่รู้จักในทุกฟังก์ชั่น

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


วันนี้จะปลอดภัยหรือไม่ที่จะใช้ตัวแปรขอบเขตบล็อก

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

  • หากคุณกำลังเขียนโค้ด JavaScript ฝั่งเซิร์ฟเวอร์ ( Node.js ) คุณสามารถใช้letคำสั่งนี้ได้อย่างปลอดภัย

  • หากคุณกำลังเขียนฝั่งไคลเอ็นต์รหัส JavaScript และใช้ transpiler เบราว์เซอร์ (เช่นTraceurหรือBabel-แบบสแตนด์อโลน ), คุณสามารถใช้letคำสั่ง แต่รหัสของคุณมีแนวโน้มที่จะเป็นอะไรก็ได้ แต่ที่ดีที่สุดด้วยความเคารพต่อผลการดำเนินงาน

  • หากคุณกำลังเขียนโค้ด JavaScript ฝั่งไคลเอ็นต์และใช้ transpiler ที่ใช้ Node (เช่นสคริปต์เชลล์ traceurหรือBabel ) คุณสามารถใช้letคำสั่งนี้ได้อย่างปลอดภัย และเนื่องจากเบราว์เซอร์ของคุณจะรู้เกี่ยวกับรหัส transpiled เท่านั้นข้อเสียของประสิทธิภาพจึงควร จำกัด

  • หากคุณกำลังเขียนโค้ด JavaScript ฝั่งไคลเอ็นต์และไม่ใช้ transpiler คุณต้องพิจารณาการสนับสนุนเบราว์เซอร์

    นี่คือเบราว์เซอร์บางตัวที่ไม่รองรับletเลย:

    • Internet explorer 10และต่ำกว่า
    • Firefox 43และต่ำกว่า
    • Safari 9และต่ำกว่า
    • เบราว์เซอร์ Android 4 ขึ้นไป
    • Opera 27และต่ำกว่า
    • Chome 40และต่ำกว่า
    • เบราว์เซอร์Opera Mini & Blackberryรุ่นใดก็ได้

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


วิธีการติดตามการสนับสนุนเบราว์เซอร์

สำหรับภาพรวมที่เป็นปัจจุบันซึ่งเบราว์เซอร์รองรับletคำสั่งในขณะที่คุณอ่านคำตอบนี้Can I Useดูหน้านี้


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


2
"ไม่ทราบ" ทำให้เข้าใจผิดเนื่องจากมีการประกาศตัวแปรเนื่องจากมีการยก
Oriol

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

1
สิ่งนี้มีประโยชน์ขอขอบคุณ! ฉันคิดว่ามันจะมีประโยชน์มากยิ่งขึ้นในการระบุสิ่งที่คุณหมายถึงโดย "Modern JavaScript" และ "Old school JavaScript" ฉันคิดว่าสิ่งเหล่านี้สอดคล้องกับ ECMAScript 6 / ES6 / ECMAScript 2015 และรุ่นก่อนหน้าตามลำดับหรือไม่
Jon Schneider

1
@ JonSchneider: ถูกต้อง! ที่ฉันพูดว่า "old school JavaScript" ฉันกำลังพูดถึง ECMAScript 5 และที่ที่ฉันอ้างถึง "JavaScript ทันสมัย" ฉันกำลังใช้ ECMAScript 6 (aka ECMAScript 2015) ฉันไม่คิดว่ามันสำคัญจริงๆที่จะต้องลงรายละเอียดที่นี่เนื่องจากคนส่วนใหญ่ต้องการทราบ (1) ความแตกต่างระหว่างขอบเขตบล็อกและขอบเขตการทำงาน (2) สิ่งที่เบราว์เซอร์สนับสนุนขอบเขตบล็อกและ (3) ไม่ว่าจะปลอดภัยหรือไม่ที่จะใช้ขอบเขตการบล็อกในวันนี้สำหรับโครงการที่กำลังทำงานอยู่ ดังนั้นฉันจึงมุ่งเน้นคำตอบของฉันในการแก้ไขปัญหาเหล่านั้น
John Slegers

1
@ JonSchneider: (ต่อ) อย่างไรก็ตามฉันเพิ่งเพิ่มลิงก์ไปยังบทความ Smashing Magazine ใน ES6 / ES2015 สำหรับผู้ที่ต้องการเรียนรู้เพิ่มเติมเกี่ยวกับฟีเจอร์ที่เพิ่มลงใน JavaScript ในช่วงสองสามปีที่ผ่านมา ... ของคนอื่น ๆ ที่ อาจสงสัยว่าฉันหมายถึงอะไรกับ "JavaScript ที่ทันสมัย"
John Slegers

39

นี่คือตัวอย่าง:

<script>

var globalVariable = 7; //==window.globalVariable

function aGlobal( param ) { //==window.aGlobal(); 
                            //param is only accessible in this function
  var scopedToFunction = {
    //can't be accessed outside of this function

    nested : 3 //accessible by: scopedToFunction.nested
  };

  anotherGlobal = {
    //global because there's no `var`
  }; 

}

</script>

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



26

ใน "Javascript 1.7" (ส่วนขยายของ Mozilla to Javascript) หนึ่งสามารถประกาศตัวแปรขอบเขตบล็อกด้วยletคำสั่ง :

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4

2
ใช่ แต่มันปลอดภัยที่จะใช้? ฉันหมายถึงฉันจะเลือกใช้งานจริงหรือไม่หากรหัสของฉันจะทำงานใน WebKit
IgorGanapolsky

10
@Python: ไม่มี WebKit letไม่สนับสนุน
kennytm

ฉันเดาว่าการใช้งานที่ถูกต้องเพียงอย่างเดียวคือถ้าคุณรู้ว่าลูกค้าทุกคนจะใช้เบราว์เซอร์ Mozilla เช่นสำหรับระบบภายใน บริษัท
GazB

หรือถ้าคุณกำลังเขียนโปรแกรมโดยใช้เฟรมเวิร์ก XUL ซึ่งเป็นเฟรมเวิร์กอินเตอร์เฟสของ Mozilla ที่คุณสร้างโดยใช้ css, xml และ javascript
เจอราร์ด ONeill

1
@GazB แม้จะเป็นความคิดที่น่ากลัว! ดังนั้นวันนี้คุณรู้ว่าลูกค้าของคุณใช้ Mozilla แล้วออกมาบันทึกใหม่ระบุว่าตอนนี้พวกเขากำลังใช้อย่างอื่น IE เหตุผลที่ระบบการจ่ายเงินของเราแย่ ... คุณต้องใช้ IE8 และไม่เคยใช้ IE9 หรือ IE10 หรือ Firefox หรือ Chrome เพราะมันจะไม่ทำงาน ...
buzzsawddog

25

ความคิดในการกำหนดขอบเขตใน JavaScript เมื่อได้รับการออกแบบมาโดยเบรนแดนไอซ์มาจากHyperCardภาษาสคริปต์HyperTalk

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

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

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

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

นั่นคือคำอธิบายทางเทคนิค ในทางปฏิบัติเป็นสิ่งสำคัญที่ต้องจำไว้ใน JavaScript

  • ขอบเขตทางเทคนิคเป็น "Execution Contexts"
  • บริบทสร้างสแต็กของสภาพแวดล้อมที่เก็บตัวแปรไว้
  • ด้านบนของสแต็คจะมีความสำคัญกว่า (ด้านล่างเป็นบริบททั่วโลก)
  • แต่ละฟังก์ชั่นสร้างบริบทการดำเนินการ (แต่ไม่ผูกพันใหม่เสมอ)

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

i ) บริบทด้านนอก มันมีสภาพแวดล้อมที่แปรผันของ a = 1
ii ) บริบทของ IIFE มันมีสภาพแวดล้อมที่เป็นศัพท์ของ a = 1 แต่สภาพแวดล้อมของตัวแปรของ a = 6 ซึ่งมีความสำคัญใน stack
iii ) บริบทของฟังก์ชันที่ส่งคืนมันมีคำศัพท์ สภาพแวดล้อมของ = 6 และนั่นคือค่าอ้างอิงในการแจ้งเตือนเมื่อมีการเรียก

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


17

1) มีขอบเขตโกลบอลขอบเขตฟังก์ชันและขอบเขต with และ catch ไม่มีขอบเขตระดับ 'บล็อก' โดยทั่วไปสำหรับตัวแปร - คำสั่ง with และ catch จะเพิ่มชื่อให้กับบล็อก

2) ขอบเขตซ้อนกันโดยฟังก์ชั่นไปจนถึงขอบเขตทั่วโลก

3) คุณสมบัติได้รับการแก้ไขโดยการผ่านห่วงโซ่ต้นแบบ คำสั่ง with นำชื่อคุณสมบัติของวัตถุไว้ในขอบเขตคำศัพท์ที่กำหนดโดยบล็อกด้วย

แก้ไข: ECMAAScript 6 (Harmony) เป็น spec'ed เพื่อสนับสนุนการให้และฉันรู้ว่าโครเมี่ยมช่วยให้ธง 'สามัคคี' ดังนั้นบางทีมันอาจจะสนับสนุน ..

ให้เป็นการสนับสนุนสำหรับการกำหนดขอบเขตระดับบล็อก แต่คุณต้องใช้คำหลักเพื่อทำให้เกิดขึ้น

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

 //chrome (v8)

 var a = { 'test1':'test1val' }
 test1   // error not defined
 with (a) { var test1 = 'replaced' }
 test1   // undefined
 a       // a.test1 = 'replaced'

แก้ไข: ชี้แจงตัวอย่าง:

test1 ถูกกำหนดขอบเขตไว้ที่ with block แต่ถูกกำหนดเป็น a.test1 'Var test1' สร้างตัวแปรใหม่ test1 ในบริบทศัพท์ด้านบน (ฟังก์ชั่นหรือทั่วโลก) เว้นแต่จะเป็นคุณสมบัติของ a - ซึ่งมันเป็น

อ๊ะ! ระวังการใช้ 'กับ' - เช่นเดียวกับ var คือ noop หากตัวแปรถูกกำหนดไว้แล้วในฟังก์ชั่นมันยังเป็น noop ที่เกี่ยวข้องกับชื่อที่นำเข้าจากวัตถุ! ชื่อเล็ก ๆ น้อย ๆ ที่ขึ้นชื่อแล้วถูกกำหนดไว้จะทำให้ปลอดภัยกว่านี้มาก โดยส่วนตัวแล้วฉันจะไม่ใช้เพราะสิ่งนี้


คุณมีข้อผิดพลาดที่นี่สำหรับ JavaScript หนึ่งรูปแบบมีการกำหนดขอบเขตของบล็อก
Benjamin Gruenbaum

หูของฉัน (ตา) เปิดเบ็นจามิน - ข้อความของฉันด้านบนเป็นวิธีที่ฉันใช้ในการกำหนดขอบเขต Javascript แต่ไม่ได้อ่านสเป็ค และฉันหวังว่าคุณจะไม่อ้างถึงคำสั่ง with (ซึ่งเป็นรูปแบบของการกำหนดขอบเขตวัตถุ) หรือไวยากรณ์ 'let' พิเศษของ Mozilla
เจอราร์ดโอเนลล์

ดีwithคำสั่งเป็นรูปแบบของการกำหนดขอบเขตบล็อก แต่catchคำสั่งเป็นรูปแบบที่พบมากขึ้น (ความเป็นจริงสนุก, การดำเนินการ v8 catchมีwith) - ที่สวยมากในรูปแบบเฉพาะของการกำหนดขอบเขตบล็อกใน JavaScript เอง (นั่นคือฟังก์ชั่นทั่วโลกลอง / จับ ด้วยและอนุพันธ์ของพวกเขา) แต่สภาพแวดล้อมโฮสต์มีความคิดที่แตกต่างกันของการกำหนดขอบเขต - ตัวอย่างเช่นเหตุการณ์แบบอินไลน์ในเบราว์เซอร์และโมดูล vm ของ NodeJS
Benjamin Gruenbaum

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

2
ซึ่งเป็นสิ่งที่บล็อกการกำหนดขอบเขตวิธี :)
เบนจามิน Gruenbaum

9

ฉันพบว่าหลายคนที่เพิ่งเริ่มใช้ JavaScript มีปัญหาในการทำความเข้าใจว่าการสืบทอดนั้นพร้อมใช้งานในภาษาและขอบเขตการทำงานนั้นเป็นขอบเขตเดียวเท่านั้น ฉันให้ส่วนขยายแก่ beautifier ที่ฉันเขียนเมื่อปลายปีที่แล้วชื่อ JSPretty ขอบเขตคุณสมบัติของสีฟังก์ชันในรหัสและเชื่อมโยงสีกับตัวแปรทั้งหมดที่ประกาศในขอบเขตนั้นเสมอ การปิดจะแสดงให้เห็นด้วยสายตาเมื่อใช้ตัวแปรที่มีสีจากขอบเขตหนึ่งในขอบเขตที่แตกต่างกัน

ลองใช้คุณสมบัติได้ที่:

ดูตัวอย่างได้ที่:

ดูรหัสได้ที่:

ขณะนี้คุณลักษณะดังกล่าวรองรับการทำงานที่มีความลึกถึง 16 ฟังก์ชันซ้อนกัน แต่ในปัจจุบันไม่ได้เปลี่ยนสีของตัวแปรทั่วโลก


1
ใช้งานไม่ได้กับฉันด้วย Firefox 26 ฉันวางโค้ดหรือโหลดไฟล์คลิกเรียกใช้และไม่มีอะไรเกิดขึ้น
mplwork

ขอบเขตและการสืบทอดเป็นสองสิ่งที่แตกต่าง
Ben Aston

9

JavaScript มีขอบเขตสองประเภทเท่านั้น:

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

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

        a = "global";
         function outer(){ 
              b = "local";
              console.log(a+b); //"globallocal"
         }
outer();

ห่วงโซ่ขอบเขต ->

  1. ระดับหน้าต่าง - aและouterฟังก์ชั่นอยู่ที่ระดับบนสุดในห่วงโซ่ขอบเขต
  2. เมื่อฟังก์ชั่นด้านนอกเรียกว่าใหม่variable scope object(และรวมอยู่ในห่วงโซ่ขอบเขต) เพิ่มด้วยตัวแปรbภายใน

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


1
ไม่แน่ใจว่าทำไมนี่ไม่ใช่คำตอบที่ยอมรับ มีขอบเขตการใช้งานจริง (ก่อน ECMA6 ไม่มี "ขอบเขตในท้องถิ่น") และการเชื่อมโยงทั่วโลก
texasbruce

9

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

ดังนั้นจากมุมมองนี้ฉันคิดว่ารูปภาพจะช่วยให้ฉันพบใน Scopes and Closures ebook ของ Kyle Simpson:

ภาพ

อ้างจาก ebook ของเขา:

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

สิ่งหนึ่งที่ควรทราบเมื่อพูดถึง "ขอบเขตการค้นหาหยุดเมื่อพบการแข่งขันครั้งแรก"

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


8

เรียกใช้รหัส หวังว่านี่จะให้ความคิดเกี่ยวกับการกำหนดขอบเขต

Name = 'global data';
document.Name = 'current document data';
(function(window,document){
var Name = 'local data';
var myObj = {
    Name: 'object data',
    f: function(){
        alert(this.Name);
    }
};

myObj.newFun = function(){
    alert(this.Name);
}

function testFun(){
    alert("Window Scope : " + window.Name + 
          "\nLocal Scope : " + Name + 
          "\nObject Scope : " + this.Name + 
          "\nCurrent document Scope : " + document.Name
         );
}


testFun.call(myObj);
})(window,document);

8

ขอบเขตทั่วโลก:

ตัวแปรทั่วโลกเป็นเหมือนดาวทั่วโลก (Jackie Chan, Nelson Mandela) คุณสามารถเข้าถึงได้ (รับหรือตั้งค่า) จากส่วนใด ๆ ของแอปพลิเคชันของคุณ ฟังก์ชั่นทั่วโลกเป็นเหมือนเหตุการณ์ทั่วโลก (ปีใหม่คริสต์มาส) คุณสามารถดำเนินการ (โทร) พวกเขาจากส่วนใด ๆ ของใบสมัครของคุณ

//global variable
var a = 2;

//global function
function b(){
   console.log(a);  //access global variable
}

ขอบเขตท้องถิ่น:

หากคุณอยู่ในสหรัฐอเมริกาคุณอาจรู้จัก Kim Kardashian ผู้มีชื่อเสียงที่น่าอับอาย แต่ผู้คนที่อยู่นอกสหรัฐอเมริกาจะไม่รู้จักเธอ เธอเป็นดาราท้องถิ่นที่ผูกพันกับดินแดนของเธอ

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

function b(){
   var d = 21; //local variable
   console.log(d);

   function dog(){  console.log(a); }
     dog(); //execute local function
}

 console.log(d); //ReferenceError: dddddd is not defined    

ตรวจสอบบทความนี้เพื่อทำความเข้าใจเกี่ยวกับขอบเขตในเชิงลึก


6

มีขอบเขต JavaScript เกือบสองประเภทเท่านั้น:

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

ดังนั้นบล็อกใด ๆ นอกเหนือจากฟังก์ชั่นจะไม่สร้างขอบเขตใหม่ นั่นอธิบายว่าทำไม for-loops เขียนทับตัวแปรที่อยู่นอกขอบเขต:

var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5

ใช้ฟังก์ชั่นแทน:

var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10

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

เกือบทั้งหมดที่คุณต้องรู้ในแง่ของการกำหนดขอบเขต JavaScript ยกเว้น:

  • ลอง / จับแนะนำขอบเขตใหม่เท่านั้นสำหรับตัวแปรยกเว้นตัวเองตัวแปรอื่น ๆ ไม่มีขอบเขตใหม่
  • เห็นได้ชัดว่าข้อ with-clause เป็นข้อยกเว้นอื่น แต่การใช้ with-clause จะทำให้หมดกำลังใจอย่างมาก ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with )

ดังนั้นคุณจะเห็นว่าการกำหนดขอบเขต JavaScript นั้นง่ายมากจริงๆแม้ว่าจะไม่ได้หยั่งรู้เสมอไป สิ่งเล็ก ๆ น้อย ๆ ที่ควรระวัง:

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

ดังนั้นรหัสนี้:

var i = 1;
function abc() {
  i = 2;
  var i = 3;
}
console.log(i);     // outputs 1

เทียบเท่ากับ:

var i = 1;
function abc() {
  var i;     // var declaration moved to the top of the scope
  i = 2;
  i = 3;     // the assignment stays where it is
}
console.log(i);

สิ่งนี้อาจดูขัดกับสัญชาตญาณ แต่ก็สมเหตุสมผลจากมุมมองของนักออกแบบภาษาที่จำเป็น


5

Modern Js, ES6 +, ' const' และ ' let'

คุณควรใช้การกำหนดขอบเขตบล็อกสำหรับตัวแปรทุกตัวที่คุณสร้างเช่นเดียวกับภาษาหลักอื่น ๆ ส่วนใหญ่ varเป็นล้าสมัย ทำให้รหัสของคุณปลอดภัยและสามารถดูแลรักษาได้มากขึ้น

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

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

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


3

ลองตัวอย่างที่อยากรู้อยากเห็นนี้ ในตัวอย่างด้านล่างหาก a มีตัวเลขเริ่มต้นที่ 0 คุณจะเห็น 0 แล้ว 1 ยกเว้น a คือวัตถุและ javascript จะส่ง f1 ตัวชี้ของแทนที่จะเป็นสำเนา ผลลัพธ์ก็คือคุณจะได้รับการแจ้งเตือนเดียวกันทั้งสองครั้ง

var a = new Date();
function f1(b)
{
    b.setDate(b.getDate()+1);
    alert(b.getDate());
}
f1(a);
alert(a.getDate());

3

มีขอบเขตฟังก์ชันใน JS เท่านั้น ไม่ปิดกั้นขอบเขต! คุณสามารถเห็นสิ่งที่ยกเกินไป

var global_variable = "global_variable";
var hoisting_variable = "global_hoist";

// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);

if (true) {
    // The variable block will be global, on true condition.
    var block = "block";
}
console.log("global_scope: - block: " + block);

function local_function() {
    var local_variable = "local_variable";
    console.log("local_scope: - local_variable: " + local_variable);
    console.log("local_scope: - global_variable: " + global_variable);
    console.log("local_scope: - block: " + block);
    // The hoisting_variable is undefined at the moment.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);

    var hoisting_variable = "local_hoist";
    // The hoisting_variable is now set as a local one.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}

local_function();

// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);

(เป็นเวลานานตั้งแต่คำตอบที่โพสต์) บล็อกขอบเขต; developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Bob

2

ความเข้าใจของฉันคือว่ามี 3 ขอบเขต: ขอบเขตทั่วโลกพร้อมใช้งานทั่วโลก; ขอบเขตท้องถิ่นพร้อมใช้งานสำหรับฟังก์ชันทั้งหมดโดยไม่คำนึงถึงบล็อก และขอบเขตบล็อกมีให้เฉพาะกับบล็อกคำสั่งหรือนิพจน์ที่ใช้งานอยู่ ขอบเขตโกลบอลและโลคัลถูกระบุด้วยคีย์เวิร์ด 'var', ภายในฟังก์ชันหรือภายนอก, และขอบเขตบล็อกจะถูกระบุด้วยคีย์เวิร์ด 'let'

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

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let


2

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

<button onclick="foo()"></button>

ขอบเขตของตัวแปรที่on*แอตทริบิวต์สามารถอ้างอิงได้ต้องเป็น:

  • โกลบอล (เครื่องมือจัดการอินไลน์ที่ใช้งานได้อ้างอิงตัวแปรโกลบอลเกือบทั้งหมด)
  • คุณสมบัติของเอกสาร (เช่นquerySelectorเป็นตัวแปรแบบสแตนด์อโลนจะชี้ไปที่document.querySelectorหายาก)
  • คุณสมบัติขององค์ประกอบที่ตัวจัดการติดอยู่ (เช่นด้านบน; หายาก)

มิฉะนั้นคุณจะได้รับ ReferenceError เมื่อมีการเรียกใช้ตัวจัดการ ตัวอย่างเช่นหากตัวจัดการแบบอินไลน์อ้างอิงฟังก์ชันที่กำหนดไว้ภายใน window.onloadหรือ$(function() {การอ้างอิงจะล้มเหลวเนื่องจากตัวจัดการแบบอินไลน์อาจอ้างอิงตัวแปรในขอบเขตส่วนกลางเท่านั้นและฟังก์ชันไม่ใช่แบบโกลบอล:

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

เนื่องจากขอบเขตของโซ่ภายในตัวจัดการแบบอินไลน์นั้นแปลกมากและเนื่องจากตัวจัดการแบบอินไลน์ต้องการมลพิษทั่วโลกในการทำงานและเนื่องจากบางครั้งตัวจัดการแบบอินไลน์ต้องใช้สตริงที่น่าเกลียดเมื่อหนีผ่านการโต้แย้งจึงอาจหลีกเลี่ยงได้ ให้แนบตัวจัดการเหตุการณ์โดยใช้ Javascript (เช่นเดียวกับaddEventListener) แทนที่จะใช้มาร์กอัป HTML


ในบันทึกย่อที่แตกต่างจาก<script>แท็กปกติซึ่งทำงานที่ระดับบนสุดโค้ดภายในโมดูล ES6 ทำงานในขอบเขตส่วนตัวของตนเอง ตัวแปรที่กำหนดที่ด้านบนของ<script>แท็กปกติคือโกลบอลดังนั้นคุณสามารถอ้างอิงได้ใน<script>แท็กอื่นเช่นนี้

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

ระดับบนสุดของโมดูล ES6 จะคล้ายกับที่ภายในของ IIFE <script>ในระดับบนในปกติ โมดูลสามารถอ้างอิงตัวแปรใด ๆ ที่เป็นโกลบอลและไม่มีสิ่งใดที่สามารถอ้างอิงอะไรได้ภายในโมดูลยกเว้นว่าโมดูลนั้นได้รับการออกแบบอย่างชัดเจน


1

ใน JavaScript มีขอบเขตสองประเภท:

  • ขอบเขตท้องถิ่น
  • ขอบเขตทั่วโลก

carNameฟังก์ชั่นด้านล่างมีตัวแปรขอบเขตท้องถิ่น และตัวแปรนี้ไม่สามารถเข้าถึงได้จากนอกฟังก์ชั่น

function myFunction() {
    var carName = "Volvo";
    alert(carName);
    // code here can use carName
}

carNameด้านล่างชั้นมีตัวแปรขอบเขตทั่วโลก และตัวแปรนี้สามารถเข้าถึงได้จากทุกที่ในชั้นเรียน

class {

    var carName = " Volvo";

    // code here can use carName

    function myFunction() {
        alert(carName);
        // code here can use carName 
    }
}

1

ES5 และก่อนหน้านี้:

ตัวแปรใน Javascript นั้นเริ่มแรก (ก่อนES6) กำหนดขอบเขตของฟังก์ชัน คำที่กำหนดขอบเขตศัพท์หมายถึงคุณสามารถดูขอบเขตของตัวแปรโดย 'ดู' ที่โค้ด

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

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

ตัวอย่าง:

// global scope
var foo = 'global';
var bar = 'global';
var foobar = 'global';

function outerFunc () {
 // outerFunc scope
 var foo = 'outerFunc';
 var foobar = 'outerFunc';
 innerFunc();
 
 function innerFunc(){
 // innerFunc scope
  var foo = 'innerFunc';
  console.log(foo);
  console.log(bar);
  console.log(foobar);
  }
}

outerFunc();

จะเกิดอะไรขึ้นเมื่อเรากำลังพยายามที่จะเข้าสู่ระบบตัวแปรfoo, barและfoobarไปยังคอนโซลคือต่อไปนี้:

  1. เราพยายามที่จะบันทึก foo ไปยังคอนโซล, foo สามารถพบได้ในฟังก์ชั่นของinnerFuncตัวเอง ดังนั้นมูลค่าของ foo innerFuncได้รับการแก้ไขเพื่อสตริง
  2. เราพยายามล็อกบาร์ไปที่คอนโซลไม่พบแถบภายในฟังก์ชันinnerFuncเอง ดังนั้นเราจึงจำเป็นที่จะต้องปีนโซ่ขอบเขต ก่อนอื่นเราดูในฟังก์ชั่นด้านนอกซึ่งฟังก์ชั่นที่innerFuncถูกกำหนด outerFuncนี้เป็นฟังก์ชั่น ในขอบเขตของouterFuncเราสามารถหาแถบตัวแปรซึ่งเก็บสตริง 'outerFunc'
  3. ไม่พบ foobar ใน innerFunc . ดังนั้นเราจำเป็นต้องปีนโซ่ขอบเขตไปยังขอบเขต innerFunc ไม่พบที่นี่เราไต่ระดับอีกระดับไปยังขอบเขตทั่วโลก (เช่นขอบเขตด้านนอกสุด) เราพบตัวแปร foobar ที่นี่ซึ่งเก็บสตริง 'global' ถ้ามันจะไม่พบตัวแปรหลังจากปีนขอบเขตโซ่เครื่องยนต์ JS จะโยนการอ้างอิงข้อผิดพลาด

ES6 (ES 2015) และมากกว่า:

แนวคิดเดียวกันของขอบเขต lexically และ scopechain ES6ยังคงใช้ใน อย่างไรก็ตามมีวิธีการใหม่ในการประกาศตัวแปร มีดังต่อไปนี้:

  • let: สร้างตัวแปรที่กำหนดขอบเขตของบล็อก
  • const: สร้างตัวแปรที่กำหนดขอบเขตบล็อกซึ่งจะต้องเริ่มต้นและไม่สามารถกำหนดใหม่ได้

ความแตกต่างที่ใหญ่ที่สุดระหว่างvarและlet/ constคือว่าvarเป็นหน้าที่กำหนดขอบเขตในขณะที่let/ constมีบล็อกขอบเขต นี่คือตัวอย่างที่แสดงให้เห็นถึงสิ่งนี้:

let letVar = 'global';
var varVar = 'global';

function foo () {
  
  if (true) {
    // this variable declared with let is scoped to the if block, block scoped
    let letVar = 5;
    // this variable declared with let is scoped to the function block, function scoped
    var varVar = 10;
  }
  
  console.log(letVar);
  console.log(varVar);
}


foo();

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


0

ใน EcmaScript5 มีสองส่วนใหญ่ขอบเขตขอบเขตท้องถิ่นและขอบเขตทั่วโลกแต่ใน EcmaScript6 เรามีหลักสามขอบเขตขอบเขตท้องถิ่นขอบเขตทั่วโลกและขอบเขตที่เรียกว่าใหม่ขอบเขตบล็อก

ตัวอย่างขอบเขตของบล็อกคือ: -

for ( let i = 0; i < 10; i++)
{
 statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}

0

ECMAScript 6 แนะนำคำหลัก let และ const คำหลักเหล่านี้สามารถใช้แทนคำหลัก var ตรงกันข้ามกับคำหลัก var คีย์เวิร์ด let และ const สนับสนุนการประกาศขอบเขตโลคัลภายในข้อความสั่ง block

var x = 10
let y = 10
const z = 10
{
  x = 20
  let y = 20
  const z = 20
  {
    x = 30
    // x is in the global scope because of the 'var' keyword
    let y = 30
    // y is in the local scope because of the 'let' keyword
    const z = 30
    // z is in the local scope because of the 'const' keyword
    console.log(x) // 30
    console.log(y) // 30
    console.log(z) // 30
  }
  console.log(x) // 30
  console.log(y) // 20
  console.log(z) // 20
}

console.log(x) // 30
console.log(y) // 10
console.log(z) // 10

0

ฉันชอบคำตอบที่ได้รับการยอมรับ แต่ต้องการเพิ่มสิ่งนี้:

ขอบเขตรวบรวมและเก็บรักษารายการค้นหาของตัวระบุที่ประกาศไว้ทั้งหมด (ตัวแปร) และบังคับใช้ชุดของกฎที่เข้มงวดเกี่ยวกับวิธีที่สิ่งเหล่านี้สามารถเข้าถึงได้ในการเรียกใช้โค้ดในปัจจุบัน

ขอบเขตคือชุดของกฎสำหรับค้นหาตัวแปรโดยใช้ชื่อตัวระบุ

  • หากไม่พบตัวแปรในขอบเขตทันทีเอ็นจิ้นจะพิจารณาขอบเขตด้านนอกถัดไปที่มีอยู่ดำเนินการต่อไปจนกระทั่งพบหรือจนกว่าจะถึงขอบเขตนอกสุด (aka ทั่วโลก)
  • เป็นชุดของกฎที่กำหนดตำแหน่งและลักษณะของตัวแปร (ตัวระบุ) ที่สามารถค้นหาได้ การค้นหานี้อาจมีวัตถุประสงค์เพื่อกำหนดให้กับตัวแปรซึ่งเป็นข้อมูลอ้างอิง LHS (ด้านซ้ายมือ) หรืออาจมีวัตถุประสงค์เพื่อดึงค่าของมันซึ่งเป็นการอ้างอิง RHS (ด้านขวามือ) .
  • การอ้างอิง LHS เป็นผลมาจากการดำเนินการที่ได้รับมอบหมาย การกำหนดขอบเขตที่เกี่ยวข้องสามารถเกิดขึ้นได้ด้วยตัวดำเนินการ = หรือส่งผ่านอาร์กิวเมนต์ไปยัง (มอบหมายให้) พารามิเตอร์ฟังก์ชัน
  • เอ็นจิ้น JavaScript จะคอมไพล์โค้ดก่อนที่จะดำเนินการและในการทำเช่นนั้นจะแยกข้อความสั่งเช่น var a = 2; เป็นสองขั้นตอนแยก: 1 ก่อนอื่นให้ var a ประกาศในขอบเขตนั้น สิ่งนี้จะถูกดำเนินการในตอนต้นก่อนการเรียกใช้โค้ด ครั้งที่ 2 ต่อมา a = 2 เพื่อค้นหาตัวแปร (การอ้างอิง LHS) และกำหนดให้ถ้าพบ
  • ทั้งการค้นหาการอ้างอิง LHS และ RHS เริ่มต้นที่ขอบเขตการดำเนินการปัจจุบันและหากจำเป็นต้องใช้ (นั่นคือพวกเขาไม่พบสิ่งที่พวกเขากำลังมองหาอยู่ที่นั่น) พวกเขาทำงานหาขอบเขตซ้อนกันขอบเขตหนึ่งชั้น ) ในแต่ละครั้งมองหาตัวระบุจนกว่าพวกเขาจะได้รับไปทั่วโลก (ชั้นบนสุด) และหยุดและหามันได้หรือไม่ การอ้างอิง RHS ที่ไม่สมบูรณ์ทำให้เกิดการอ้างอิง ReferenceError การอ้างอิง LHS ที่ไม่ได้รับผลจะส่งผลให้เกิดการสร้างอัตโนมัติแบบทั่วโลกของชื่อนั้น (ถ้าไม่ใช่ในโหมด Strict) หรือ ReferenceError (ถ้าอยู่ใน Strict Mode)
  • ขอบเขตประกอบด้วยชุดของ "ฟองสบู่" ที่แต่ละการกระทำเป็นภาชนะบรรจุหรือที่เก็บข้อมูลซึ่งมีการประกาศตัวระบุ (ตัวแปรฟังก์ชัน) ฟองเหล่านี้ซ้อนกันอย่างเป็นระเบียบภายในและการทำรังนี้ถูกกำหนดไว้ในเวลาของผู้เขียน

-3

JavaScript มีขอบเขตสองประเภท

  1. ขอบเขตทั่วโลก : ตัวแปรที่ประกาศในขอบเขตทั่วโลกสามารถใช้ได้ทุกที่ในโปรแกรมอย่างราบรื่น ตัวอย่างเช่น:

    var carName = " BMW";
    
    // code here can use carName
    
    function myFunction() {
         // code here can use carName 
    }
  2. ขอบเขตการทำงานหรือขอบเขตภายใน : ตัวแปรที่ประกาศในขอบเขตนี้สามารถใช้ในฟังก์ชั่นของตนเองเท่านั้น ตัวอย่างเช่น:

    // code here can not use carName
    function myFunction() {
       var carName = "BMW";
       // code here can use carName
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.