การกำหนดขอบเขตและการยกฟังก์ชั่น Javascript


90

ฉันเพิ่งอ่านบทความดีๆเกี่ยวกับJavaScript Scoping and Hoisting โดย Ben Cherryซึ่งเขายกตัวอย่างต่อไปนี้:

var a = 1;

function b() {
    a = 10;
    return;

    function a() {}
}
b();
alert(a);

เมื่อใช้โค้ดด้านบนเบราว์เซอร์จะแจ้งเตือน "1"

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

คำตอบ:


120

การยกฟังก์ชันหมายถึงฟังก์ชันจะถูกย้ายไปที่ด้านบนสุดของขอบเขต นั่นคือ,

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

จะเขียนใหม่โดย interpeter เป็นสิ่งนี้

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

แปลกใช่มั้ย?

นอกจากนี้ในกรณีนี้

function a() {}

ประพฤติเช่นเดียวกับ

var a = function () {};

โดยพื้นฐานแล้วนี่คือสิ่งที่โค้ดกำลังทำอยู่:

var a = 1;                 //defines "a" in global scope
function b() {  
   var a = function () {}; //defines "a" in local scope 
   a = 10;                 //overwrites local variable "a"
   return;      
}       
b();       
alert(a);                 //alerts global variable "a"

2
ดังนั้นในที่สุดการประกาศฟังก์ชันทั้งหมดจึงถูกกำหนดให้กับตัวแปร?
dev.e.loper

15
@ dev.e.loper ใช่ใน Javascript ฟังก์ชันเป็นออบเจ็กต์ชั้นหนึ่งเช่นเดียวกับสตริงและตัวเลข นั่นหมายความว่าพวกมันถูกกำหนดให้เป็นตัวแปรและสามารถส่งผ่านไปยังฟังก์ชันอื่น ๆ ถูกเก็บไว้ในอาร์เรย์และอื่น ๆ
Peter Olson

4
เนื้อหาฟังก์ชัน "เขียนใหม่" ไม่ว่าในทางใด มาตรฐาน ECMAScript ต่างๆระบุไว้อย่างชัดเจนว่าการประกาศตัวแปรและฟังก์ชันได้รับการประมวลผลก่อนที่การเรียกใช้โค้ดจะเริ่มขึ้น นั่นคือไม่มีสิ่งใดขยับได้ แต่เกี่ยวกับคำสั่งประหารชีวิต (ด้วยเหตุนี้ฉันจึงไม่ชอบคำว่า "ชักรอก" ซึ่งอนุมานถึงการเคลื่อนที่หรือการจัดเรียงใหม่) ในโค้ดที่เขียนขึ้นใหม่การประกาศvar aควรอยู่ก่อนการประกาศฟังก์ชันและการกำหนดa = 1ควรอยู่หลัง แต่โปรดทราบว่าสิ่งนี้ไม่ได้ระบุให้เกิดขึ้นจริงโดย parser, tokeniser, interpreter, compiler อะไรก็ตามมันเป็นเพียงสิ่งที่เทียบเท่ากัน
RobG

3
@RobG แน่นอนฉันเดาว่าคุณสามารถเรียกคำอธิบายว่า"โกหกเด็ก"ได้ แต่ในที่สุดพฤติกรรมก็เหมือนกันไม่ว่าโค้ดจะถูกจัดเรียงใหม่ตามตัวอักษรหรือเพียงแค่เรียงลำดับการดำเนินการใหม่ สิ่งที่เกิดขึ้นจริงเบื้องหลังเป็นปัญหาทางวิชาการมากกว่าและอาจขึ้นอยู่กับการนำไปใช้งาน
Peter Olson

7
“ นอกจากนี้ในกรณีนี้มีfunction a() {}พฤติกรรมเช่นเดียวกับvar a = function () {};  ซึ่งไม่ถูกต้องในสองวิธี: ประการแรกถ้ามีอะไรจะเป็นvar a = function a() {};(ฟังก์ชันไม่ระบุชื่อจริง) ประการที่สองรูปแบบทั้งสองไม่สามารถใช้แทนกันได้เนื่องจากจากvar a = function a() {};เฉพาะvar a;ส่วนที่จะถูกยกขึ้น a = function a() {};ส่วนหนึ่งจะยังคงได้รับที่อยู่เบื้องหลังคำสั่งที่ส่งกลับมา เนื่องจากรูปแบบดั้งเดิมเป็นการประกาศฟังก์ชันไม่ใช่นิพจน์ฟังก์ชันจึงถูกยกขึ้นโดยรวม
Sebastian Simon

6

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

function a() {} 

กลายเป็นจริงๆ

var a = function () {}

var a บังคับให้เป็นขอบเขตโลคัลและขอบเขตตัวแปรจะผ่านฟังก์ชันทั้งหมดดังนั้นตัวแปร global ยังคงเป็น 1 เนื่องจากคุณได้ประกาศเป็นขอบเขตโลคัลโดยทำให้เป็นฟังก์ชัน


5

ฟังก์ชั่นaยกภายในฟังก์ชั่นb:

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

ซึ่งเกือบจะเหมือนกับการใช้var:

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

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


1
บรรทัดนี้ "var a = function () {};" ทำให้ทุกสิ่งชัดเจน .. โดยพื้นฐานแล้ว JavaScript เป็นภาษาแบบไดนามิกและ "ฟังก์ชัน" ก็เป็นวัตถุใน JavaScript เช่นกัน
refactor

3
  1. การประกาศฟังก์ชันfunction a(){}จะถูกยกขึ้นก่อนและมีลักษณะการทำงานvar a = function () {};ดังนั้นในขอบเขตท้องถิ่นaจึงถูกสร้างขึ้น
  2. หากคุณมีตัวแปรสองตัวที่มีชื่อเดียวกัน (อีกตัวแปรหนึ่งในระบบโลคัล) ตัวแปรโลคัลจะมีความสำคัญเหนือตัวแปรส่วนกลางเสมอ
  3. เมื่อคุณตั้งค่าa=10คุณกำลังตั้งค่าตัวแปรโลคัลaไม่ใช่ตัวแปรส่วนกลาง

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


1

function a() { }เป็นคำสั่งฟังก์ชันซึ่งสร้างaตัวแปรภายในให้กับbฟังก์ชัน
ตัวแปรจะถูกสร้างขึ้นเมื่อมีการแยกวิเคราะห์ฟังก์ชันไม่ว่าจะเรียกvarใช้คำสั่งหรือฟังก์ชันหรือไม่ก็ตาม

a = 10 ตั้งค่าตัวแปรท้องถิ่นนี้


จริงa = 10ชุดตัวแปรในส่วนทั่วโลกขอบเขตเมื่อฟังก์ชั่นbจะถูกดำเนินการจนกว่าคุณจะเพิ่ม"use strict"(ในสภาพแวดล้อมเช่นการสนับสนุนที่สั่ง)
Sean Vieira

@ Sean: ไม่เนื่องจากคำสั่งฟังก์ชันสร้างตัวระบุท้องถิ่น
SLaks

... และ .... คุณพูดถูก ไม่ได้ตระหนักถึงผลที่ตามมาของการยกฟังก์ชันโดยเฉพาะ ขอบคุณ!
Sean Vieira

1

กระดูกของความขัดแย้งในข้อมูลโค้ดขนาดเล็กนี้คืออะไร?

กรณีที่ 1:

รวมfunction a(){}คำจำกัดความไว้ในเนื้อความfunction bดังนี้logs value of a = 1

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

  function a() {}
}
b();
console.log(a); // logs a = 1

กรณีที่ 2

ไม่รวมfunction a(){}คำจำกัดความภายในเนื้อหาfunction bดังต่อไปนี้logs value of a = 10

var a = 1;
function b() {
  a = 10;  // overwrites the value of global 'var a'
  return;
}
b();
console.log(a); // logs a = 10

การสังเกตจะช่วยให้คุณทราบว่าคำสั่งconsole.log(a)บันทึกค่าต่อไปนี้

กรณีที่ 1: a = 1

กรณีที่ 2: a = 10

ตำแหน่ง

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

คำอธิบายของทั้งสองกรณี

เนื่องจากfunction definition with name propertya นั้นเหมือนกับไฟล์variable a. variable aภายในfunction body bกลายเป็นตัวแปรท้องถิ่น บรรทัดก่อนหน้านี้หมายความว่าค่าส่วนกลางของ a ยังคงเหมือนเดิมและค่าท้องถิ่นของ a จะอัปเดตเป็น 10

ดังนั้นสิ่งที่เราตั้งใจจะบอกก็คือโค้ดด้านล่าง

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

  function a() {}
}
b();
console.log(a); // logs a = 1

มันถูกตีความโดยล่าม JS ดังนี้

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


}
b();
console.log(a); // logs a = 1

อย่างไรก็ตามเมื่อเราลบค่าfunction a(){} definitionที่value of 'a'ประกาศและกำหนดไว้ภายนอกฟังก์ชัน b ค่านั้นจะถูกเขียนทับและจะเปลี่ยนเป็น 10 ในกรณีที่ 2 ค่าจะถูกเขียนทับเนื่องจากa=10อ้างถึงการประกาศทั่วโลกและหากจะประกาศในเครื่องเราจะต้องมี เขียนvar a = 10;.

var a = 1;
function b() {
  var a = 10; // here var a is declared and defined locally because it uses a var keyword. 
  return;
}
b();
console.log(a); // logs a = 1

เราสามารถชี้แจงข้อสงสัยของเราเพิ่มเติมได้โดยเปลี่ยนname propertyin function a(){} definitionเป็นชื่ออื่นที่ไม่ใช่'a'

var a = 1;
function b() {
  a = 10; // here var a is declared and defined locally because it uses a var keyword. 
  return;

  function foo() {}
}
b();
console.log(a); // logs a = 1

1

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

เมื่อการประกาศเกิดขึ้นvar aแล้วfunction bและภายในที่bขอบเขตfunction aการประกาศ

ฟังก์ชันนี้จะเป็นเงาของตัวแปรที่มาจากขอบเขตส่วนกลาง

หลังจากการประกาศจะทำค่ากำหนดจะเริ่มต้นที่ทั่วโลกaจะได้รับความคุ้มค่า1และภายในจะได้รับfunction b 10เมื่อคุณทำalert(a)มันจะเรียกตัวแปรขอบเขตส่วนกลางที่แท้จริง การเปลี่ยนแปลงโค้ดเพียงเล็กน้อยนี้จะทำให้ชัดเจนยิ่งขึ้น

        var a = 1;

    function b() {
        a = 10;
        return a;

        function a() { }
    }

    alert(b());
    alert(a);

1
เป็นที่น่าแปลกใจที่ผู้เชี่ยวชาญจำนวนมากแม้แต่ในหลักสูตรที่ codechool.com อ้างถึงการชักรอกซึ่งไม่มีอะไรมากไปกว่ามุมมองที่เรียบง่ายของสิ่งที่เกิดขึ้น แต่จริงๆแล้วการชักรอกไม่ได้เกิดขึ้นเลย Ref: 1) developer.mozilla.org/en-US/docs/Glossary/Hoisting 2) Chapter 5 of Secrets of the JavaScript Ninja 2 / e โดย john resig, bear bebeault, josip maras
adnan2nd

1

น่าแปลกที่ไม่มีคำตอบใดที่กล่าวถึงความเกี่ยวข้องของบริบทการดำเนินการใน Scope Chain

JavaScript Engine จะรวมโค้ดที่กำลังดำเนินการอยู่ใน Execution Context บริบทการดำเนินการพื้นฐานคือบริบทการดำเนินการส่วนกลาง ทุกครั้งที่มีการเรียกใช้ฟังก์ชันใหม่บริบทการดำเนินการใหม่จะถูกสร้างขึ้นและวางบน Execution Stack ลองนึกถึง Stack Frame ที่อยู่บน Invocation Stack ในภาษาโปรแกรมอื่น ๆ เข้าก่อนออกก่อน ตอนนี้แต่ละ Execution Context มี Variable Environment และ Outer Environment ใน JavaScript

ฉันจะใช้ตัวอย่างด้านล่างเพื่อสาธิต

1) ขั้นแรกเราเข้าสู่ขั้นตอนการสร้างของบริบทการดำเนินการทั่วโลก ทั้งสภาพแวดล้อมภายนอกและสภาพแวดล้อมที่เปลี่ยนแปลงได้ของสภาพแวดล้อมคำศัพท์ถูกสร้างขึ้น Global Object ถูกตั้งค่าและวางไว้ในหน่วยความจำโดยมีตัวแปรพิเศษ 'this' ชี้ไปที่มัน ฟังก์ชัน a และรหัสและตัวแปร myVar ที่มีค่าที่ไม่ได้กำหนดจะอยู่ในหน่วยความจำในสภาพแวดล้อมตัวแปรส่วนกลาง สิ่งสำคัญคือต้องทราบว่าฟังก์ชัน a ของโค้ดจะไม่ถูกเรียกใช้งาน มันถูกวางไว้ในหน่วยความจำด้วยฟังก์ชันก.

2) ประการที่สองเป็นขั้นตอนการดำเนินการของบริบทการดำเนินการ myVar ไม่ใช่ค่าที่ไม่ได้กำหนดอีกต่อไป เริ่มต้นด้วยค่า 1 ซึ่งเก็บไว้ในสภาพแวดล้อมตัวแปรส่วนกลาง ฟังก์ชัน a ถูกเรียกใช้และสร้างบริบทการดำเนินการใหม่

3) ในฟังก์ชัน a ของ Execution Context จะผ่านขั้นตอนการสร้างและการดำเนินการของ Execution Context ของตัวเอง มันมีสภาพแวดล้อมภายนอกและสภาพแวดล้อมที่แปรปรวนดังนั้นสภาพแวดล้อมคำศัพท์ของตัวเอง ฟังก์ชัน b และตัวแปร myVar จะถูกเก็บไว้ในสภาพแวดล้อมที่เปลี่ยนแปลงได้ สภาพแวดล้อมตัวแปรนี้แตกต่างจากสภาพแวดล้อมตัวแปรทั่วโลก เนื่องจากฟังก์ชั่นตั้งค่าเป็นคำศัพท์ (ทางกายภาพในโค้ด) ในระดับเดียวกับบริบทการดำเนินการส่วนกลางสภาพแวดล้อมภายนอกจึงเป็นบริบทการดำเนินการส่วนกลาง ดังนั้นหากฟังก์ชัน a อ้างถึงตัวแปรที่ไม่ได้อยู่ในสภาพแวดล้อมตัวแปรฟังก์ชันจะค้นหา Scope Chain และพยายามค้นหาตัวแปรใน Variable Environment ของ global Execution Context

4) ฟังก์ชัน b ถูกเรียกใช้ในฟังก์ชันก. สร้างบริบทการดำเนินการใหม่ เนื่องจากมันอยู่ในคำศัพท์ในฟังก์ชัน a สภาพแวดล้อมภายนอกจึงเป็นไฟล์. ดังนั้นเมื่อมันอ้างถึง myVar เนื่องจาก myVar ไม่ได้อยู่ในสภาพแวดล้อมตัวแปรของฟังก์ชัน b มันจะดูใน function a ของ Variable Environment พบว่ามีและ console.log พิมพ์ 2 แต่ถ้าตัวแปรไม่อยู่ในฟังก์ชันสภาพแวดล้อมตัวแปรของฟังก์ชัน a ของสภาพแวดล้อมภายนอกเป็นบริบทการดำเนินการส่วนกลาง Scope Chain จะค้นหาต่อที่นั่น

5) หลังจากฟังก์ชั่น b และ a เสร็จสิ้นการเรียกใช้งานฟังก์ชันเหล่านั้นจะถูกดึงออกจาก Execution Stack JavaScript Engine เธรดเดียวยังคงดำเนินการต่อที่ Global Execution Context มันเรียกใช้ฟังก์ชัน b แต่ไม่มีฟังก์ชัน b ในสภาพแวดล้อมตัวแปรส่วนกลางและไม่มีสภาพแวดล้อมภายนอกอื่น ๆ ที่จะค้นหาในบริบทการดำเนินการส่วนกลาง ดังนั้นจึงมีการเพิ่มข้อยกเว้นโดย JavaScript Engine

function a(){
  function b(){
    console.log(myVar);
  }

  var myVar = 2;
  b();
}

var myVar = 1;
a();
b();
> 2
> Uncaught ReferenceError: b is not defined

ตัวอย่างด้านล่างแสดง Scope Chain ในการทำงาน ในสภาพแวดล้อมตัวแปรของ Execution Context ของฟังก์ชัน b ไม่มี myVar ดังนั้นจึงค้นหาสภาพแวดล้อมภายนอกซึ่งเป็นฟังก์ชันก. ฟังก์ชัน a ไม่มี myVar ในสภาพแวดล้อมตัวแปรเช่นกัน ดังนั้นการค้นหาเครื่องยนต์จึงทำงานเป็นสภาพแวดล้อมภายนอกซึ่งเป็นสภาพแวดล้อมภายนอกของ Execution Context ส่วนกลางและ myVar ถูกกำหนดไว้ที่นั่น ดังนั้น console.log จึงพิมพ์ 1

function a(){
  function b(){
    console.log(myVar);
  }

  b();
}

var myVar = 1;
a();
> 1

เกี่ยวกับ Execution Context และ Lexical Environment ที่เกี่ยวข้องรวมถึง Outer Environment และ Variable Environment เปิดใช้งานการกำหนดขอบเขตของตัวแปรใน JavaScript แม้ว่าคุณจะเรียกใช้ฟังก์ชันเดียวกันหลายครั้งสำหรับการเรียกแต่ละครั้งก็จะสร้างบริบทการดำเนินการของตัวเอง ดังนั้นแต่ละ Execution Context จะมีสำเนาของตัวแปรใน Variable Environment ไม่มีการแบ่งปันตัวแปร


0

เกิดขึ้นเนื่องจากชื่อตัวแปรเหมือนกับชื่อฟังก์ชันหมายถึง "a" ด้วยเหตุนี้ Javascript จึงพยายามแก้ไขข้อขัดแย้งในการตั้งชื่อและจะส่งคืน a = 1

ฉันยังสับสนเกี่ยวกับเรื่องนี้จนกระทั่งได้อ่านโพสต์นี้ใน "JavaScript Hoisting" http://www.ufthelp.com/2014/11/JavaScript-Hoisting.html

หวังว่าจะช่วยได้


0

นี่คือสรุปคำตอบของฉันพร้อมคำอธิบายประกอบเพิ่มเติมและซอที่เหมาะกับการเล่น

// hoisting_example.js

// top of scope ie. global var a = 1
var a = 1;

// new scope due to js' functional (not block) level scope
function b() {
    a = 10; // if the function 'a' didn't exist in this scope, global a = 10
  return; // the return illustrates that function 'a' is hoisted to top
  function a(){}; // 'a' will be hoisted to top as var a = function(){};
}

// exec 'b' and you would expect to see a = 10 in subsequent alert
// but the interpreter acutally 'hoisted' the function 'a' within 'b' 
// and in doing so, created a new named variable 'a' 
// which is a function within b's scope
b();

// a will alert 1, see comment above
alert(a);

https://jsfiddle.net/adjavaherian/fffpxjx7/


0

scpope และการปิดและยก (var / function)

  1. scpope: global var สามารถเข้าถึงได้ในทุกที่ (ขอบเขตไฟล์ทั้งหมด), local var สามารถเข้าถึงได้โดยขอบเขตโลคัล (function / block scope) เท่านั้น!
    หมายเหตุ: หากตัวแปรโลคัลไม่ใช้คีย์เวิร์ด var ในฟังก์ชันตัวแปรนั้นจะกลายเป็นตัวแปรส่วนกลาง!
  2. การปิด: ฟังก์ชั่นภายในฟังก์ชันอื่นซึ่งสามารถเข้าถึงขอบเขตท้องถิ่น (ฟังก์ชันหลัก) และขอบเขตทั่วโลกอย่างไรก็ตามผู้อื่นไม่สามารถเข้าถึง vars ได้! เว้นแต่คุณจะส่งคืนเป็นมูลค่าคืน!
  3. hoisting: ย้ายประกาศ / ไม่ประกาศ vars / function ทั้งหมดไปที่ด้านบนของขอบเขตมากกว่ากำหนดค่าหรือ null!
    หมายเหตุ: แค่ย้ายประกาศไม่ย้ายค่า!

var a = 1;                
//"a" is global scope
function b() {  
   var a = function () {}; 
   //"a" is local scope 
   var x = 12; 
   //"x" is local scope 
   a = 10;
   //global variable "a" was overwrited by the local variable "a"  
   console.log("local a =" + a);
   return console.log("local x = " + x);
}       
b();
// local a =10
// local x = 12
console.log("global a = " + a);
// global a = 1
console.log("can't access local x = \n");
// can't access local x = 
console.log(x);
// ReferenceError: x is not defined



0

การชักรอกใน JavaScript หมายถึงการประกาศตัวแปรจะดำเนินการผ่านโปรแกรมก่อนที่จะเรียกใช้โค้ดใด ๆ ดังนั้นการประกาศตัวแปรที่ใดก็ได้ในโค้ดจึงเท่ากับการประกาศตัวแปรในตอนต้น


0

ทุกอย่างขึ้นอยู่กับขอบเขตของตัวแปร 'a' ให้ฉันอธิบายโดยการสร้างขอบเขตเป็นภาพ

ที่นี่ JavaScript จะสร้าง 3 ขอบเขต

i) ขอบเขตทั่วโลก ii) ฟังก์ชัน b () ขอบเขต iii) ฟังก์ชัน a () ขอบเขต

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

มันชัดเจนเมื่อคุณเรียกขอบเขตเมธอด 'alert' เป็นของ Global ในเวลานั้นดังนั้นมันจะเลือกค่าของตัวแปร 'a' จาก Global scope เท่านั้นที่เป็น 1


0

โพสต์ยาว!

แต่จะล้างแอร์!

วิธีการทำงานของ Java Script คือเกี่ยวข้องกับกระบวนการสองขั้นตอน:

  1. การรวบรวม (เพื่อพูด) - ขั้นตอนนี้จะลงทะเบียนตัวแปรและการประกาศฟังก์ชันและขอบเขตตามลำดับ ไม่เกี่ยวข้องกับการประเมินนิพจน์ฟังก์ชัน: var a = function(){}หรือนิพจน์ตัวแปร (เช่นการกำหนด3ให้xในกรณีvar x =3;ที่ไม่มีอะไรนอกจากการประเมินส่วน RHS)

  2. ล่าม: นี่คือส่วนการดำเนินการ / การประเมินผล

ตรวจสอบผลลัพธ์ของโค้ดด้านล่างเพื่อทำความเข้าใจ:

//b() can be called here!
//c() cannot be called.
console.log("a is " + a);
console.log("b is " + b);
console.log("c is " + c);
var a = 1;
console.log("Now, a is " + a);
var c = function() {};
console.log("Now c is " + c);

function b() {
  //cannot write the below line:
  //console.log(e); 
  //since e is not declared.
  e = 10; //Java script interpreter after traversing from this function scope chain to global scope, is unable to find this variable and eventually initialises it with value 10 in global scope.
  console.log("e is " + e) //  works!
  console.log("f is " + f);
  var f = 7;
  console.log("Now f is " + f);
  console.log("d is " + d);
  return;

  function d() {}
}
b();
console.log(a);

มาทำลายมันกันเถอะ:

  1. ในขั้นตอนการคอมไพล์ 'a' จะถูกลงทะเบียนภายใต้ขอบเขตส่วนกลางด้วยค่า ' undefined' เช่นเดียวกันกับ " c" ค่าของมันในขณะนี้จะเป็น " undefined" ไม่ใช่ " function()" ' b' จะถูกลงทะเบียนเป็นฟังก์ชันในขอบเขตส่วนกลาง bขอบเขตภายใน' f' จะถูกลงทะเบียนเป็นตัวแปรซึ่งจะไม่ได้กำหนดในขณะนี้และฟังก์ชัน ' d' จะได้รับการลงทะเบียน

  2. เมื่อล่ามทำงานตัวแปรที่ประกาศและfunction()(ไม่ใช่นิพจน์) สามารถเข้าถึงได้ก่อนที่ล่ามจะไปถึงบรรทัดนิพจน์จริง ดังนั้นตัวแปรจะถูกพิมพ์ ' undefined' และสามารถเรียกใช้ฟังก์ชันนิรนามได้ก่อนหน้านี้ อย่างไรก็ตามการพยายามเข้าถึงตัวแปรที่ไม่ได้ประกาศก่อนการเริ่มต้นนิพจน์จะส่งผลให้เกิดข้อผิดพลาดเช่น:

console.log(e)
e = 3;

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

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

var a = 1;
console.log("a is " + a);

function b() {
  console.log("a inside the function b is " + a); //interpreter finds                                'a' as function() in current scope. No need to go outside the scope to find 'a'.
  a = 3; //a changed
  console.log("Now a is " + a);
  return;

  function a() {}
}
var a; //treated as duplicate and ignored.
b();
console.log("a is still " + a + " in global scope"); //This is global scope a.


0

Hoisting เป็นแนวคิดเชิงพฤติกรรมของ JavaScript Hoisting (พูดว่าเคลื่อนที่) เป็นแนวคิดที่อธิบายว่าควรประกาศตัวแปรอย่างไรและที่ไหน

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

เราพบรอกสองประเภทในกรณีส่วนใหญ่

1. การยกประกาศตัวแปร

มาทำความเข้าใจกับโค้ดนี้กัน

 a = 5; // Assign 5 to a
 elem = document.getElementById("demo"); // Find an element 
 elem.innerHTML = a;                     // Display a in the element
 var a; // Declare a
  //output-> 5

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

 var a = 5; // Assign and declare 5 to a
 elem = document.getElementById("demo"); // Find an element 
 elem.innerHTML = a;                     // Display a in the element
  // output -> 5

ลองพิจารณาตัวอย่างอื่น

  function foo() {
     console.log(x)
     var x = 1;
 }

ถูกตีความเช่นนี้จริง:

  function foo() {
     var x;
     console.log(x)
     x = 1;
  }

ในกรณีนี้ x จะไม่ถูกกำหนด

ไม่สำคัญว่าโค้ดได้ดำเนินการซึ่งมีการประกาศตัวแปรหรือไม่ ลองพิจารณาตัวอย่างนี้

  function foo() {
     if (false) {
         var a = 1;
     }
     return;
     var b = 1;
  }

ฟังก์ชันนี้กลายเป็นแบบนี้

  function foo() {
      var a, b;
      if (false) {
        a = 1;
     }
     return;
     b = 1;
  }

ในการประกาศตัวแปรเฉพาะรอกนิยามตัวแปรไม่ใช่การกำหนด

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

ซึ่งแตกต่างจากตัวแปรที่ยกตัวฟังก์ชันหรือค่าที่กำหนดจะถูกยกขึ้นด้วย พิจารณารหัสนี้

 function demo() {
     foo(); // this will give error because it is variable hoisting
     bar(); // "this will run!" as it is function hoisting
     var foo = function () {
         alert("this would not run!!");
     }
     function bar() { 
         alert("this will run!!");
     }
 }
 demo();

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

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

รหัสนี้จะกลายเป็นแบบนี้

var a = 1;                 //defines "a" in global scope
 function b() {  
   var a = function () {}; //defines "a" in local scope 
    a = 10;                 //overwrites local variable "a"
    return;      
 }       
 b();       
 alert(a); 

ฟังก์ชัน a () จะมีขอบเขตเฉพาะภายใน b () a () จะถูกย้ายไปด้านบนในขณะที่ตีความโค้ดด้วยคำจำกัดความ (เฉพาะในกรณีของการยกฟังก์ชัน) ดังนั้นตอนนี้จะมีขอบเขตเฉพาะที่ดังนั้นจะไม่ส่งผลกระทบต่อขอบเขตทั่วโลกในขณะที่มีขอบเขตของตัวเองภายในฟังก์ชัน b () .


0

จากความรู้ของฉันการชักรอกเกิดขึ้นพร้อมกับการประกาศตัวแปรและการประกาศฟังก์ชันตัวอย่างเช่น:

a = 7;
var a;
console.log(a) 

เกิดอะไรขึ้นภายในเอ็นจิ้นของ JavaScript:

var a;
a = 7;
console.log(a);
// 7

หรือ:

console.log(square(7)); // Output: 49
function square(n) { return n * n; }

มันจะกลายเป็น:

function square(n) { return n * n; }
console.log(square(7)); // 49

แต่การกำหนดเช่นการกำหนดตัวแปรการกำหนดนิพจน์ฟังก์ชันจะไม่ถูกยกตัวอย่างเช่น:

console.log(x);
var x = 7; // undefined

มันอาจกลายเป็นแบบนี้:

var x;
console.log(x); // undefined
x = 7;

0

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

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

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

 var v;
 console.log(v);
 console.log(abc);
/*
The output of the above codes are:
undefined
ReferenceError: abc is not defined*/

ตอนนี้ในรหัสร้องสิ่งที่เราเห็น? ตัวแปรและนิพจน์ฟังก์ชันคือ decleard

<script>
var totalAmo = 8;
var getSum = function(a, b){
      return a+b;
}
</script>

แต่ภาพจริงพร้อมหลักฐานว่าทั้งตัวแปรและฟังก์ชั่นถูกยกขึ้นที่ด้านบนของขอบเขต:

console.log(totalAmo);
console.log(getSum(8,9));
var totalAmo = 8;
var getSum = function(a, b){
      return a+b;
}
console.log(totalAmo);
console.log(getSum(9,7));

เอาต์พุตของบันทึกสองรายการแรกไม่ได้กำหนดไว้และTypeError: getSum ไม่ใช่ฟังก์ชันเนื่องจากทั้ง var totalAmoและgetSumถูกยกที่ด้านบนของขอบเขตเช่นการร้อง

 <script>
        var totalAmo;
        var getSum;

        console.log(totalAmo);
        console.log(getSum(8,9));
        var totalAmo = 8;
        var getSum = function(a, b){
            return a+b;
        }
        console.log(totalAmo);
        console.log(getSum(9,7));
    </script>

แต่สำหรับฟังก์ชั่นการประกาศฟังก์ชันทั้งหมดยกอยู่เหนือขอบเขต

console.log(getId());
function getId(){
   return 739373;
}
/* output: 739373, because the whole function hoisted on the top of the scope.*/

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

function functionScope(){
            var totalAmo;
            var getSum;

            console.log(totalAmo);
            console.log(getSum(8,9));
            var totalAmo = 8;
            var getSum = function(a, b){
                return a+b;
            }
        }

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

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

 function getTotal(){
            let total=0;
            for(var i = 0; i<10; i++){
                let valueToAdd = i;
                var multiplier = 2;
                total += valueToAdd*multiplier;
            }
            return total;
        }

ตัวแปรในตัวอย่าง abobe จะถูกยกขึ้นเหมือนร้อง

 function getTotal(){
            let total;
            var multiplier;
            total = 0;
            for(var i = 0; i<10; i++){
                let valueToAdd;
                valueToAdd = i;
                multiplier = 2;
                total += valueToAdd*multiplier;
            }
            return total;
        }

0

ES5: การยกฟังก์ชันและการยกแบบแปรผัน

function hoistingลำดับความสำคัญgreaterมากกว่าvariable hoisting

"use strict";

/**
 *
 * @author xgqfrms
 * @license MIT
 * @copyright xgqfrms
 * @created 2016-06-01
 * @modified
 *
 * @description function-hoisting.js
 * @augments
 * @example
 * @link
 *
 */

(function() {
  const log = console.log;

  var a = 1;
  function b() {
    a = 10;
    log(`local a`, a)
    return;
    // function hoisting priority is greater than variable hoisting
    function a() {}
  }
  b();
  log(`global a`, a);
  // local a 10
  // global a 1
})();



ซึ่งเท่ากับ

(function() {
  const log = console.log;

  // define "a" in global scope
  var a = 1;
  function b() {
    // define "a" in local scope
    var a ;
    // assign function to a
    a = function () {};
    // overwrites local variable "a"
    a = 10;
    log(`local a`, a);
    return;
  }

  b();
  // log global variable "a"
  log(`global a`, a);

  // local a 10
  // global a 1
})();

เหตุผลเบื้องหลังของการชักรอก

var a = 1;                
//"a" is global scope
function b() {  
   var a = function () {}; 
   //"a" is local scope 
   var x = 12; 
   //"x" is local scope 
   a = 10;
   //global variable "a" was overwrited by the local variable "a"  
   console.log("local a =" + a);
   return console.log("local x = " + x);
}       
b();
// local a =10
// local x = 12
console.log("global a = " + a);
// global a = 1
console.log("can't access local x = \n");
// can't access local x = 
console.log(x);
// ReferenceError: x is not defined

/**
 *  scpope & closure & hoisting (var/function)
 *  
 * 1. scpope : the global var can be access in any place(the whole file scope), local var only can be accessed by the local scope(function/block scope)!
 * Note: if a local variable not using var keywords in a function, it will become a global variable!
 * 
 * 2. closure : a function inner the other function, which can access local scope(parent function) & global scope, howerver it's vars can't be accessed by others! unless, your return it as return value!
 * 
 * 3. hoisting : move all declare/undeclare vars/function to the scope top, than assign the value or null!
 * Note: it just move the declare, not move the value!
 * 
 */

ES6 let, constไม่มีอยู่ hoisting

(() => {
  const log = console.log;
  log(a)
  // Error: Uncaught ReferenceError: Cannot access 'a' before initialization
  let a = 1;
})();



(() => {
  const log = console.log;
  log(b)
  // Error: Uncaught ReferenceError: Cannot access 'b' before initialization
  const b = 1;
})();

อ้างอิง

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

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

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

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