Chức năng Javascript phạm vi và lưu trữ


89

Tôi vừa đọc một bài viết hay về JavaScript Scoping và Hoisting của Ben Cherry, trong đó anh ấy đưa ra ví dụ sau:

var a = 1;

function b() {
    a = 10;
    return;

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

Sử dụng đoạn mã trên, trình duyệt sẽ cảnh báo "1".

Tôi vẫn không chắc tại sao nó trả về "1". Một số điều anh ấy nói khiến bạn nhớ đến như: Tất cả các khai báo hàm đều được đưa lên đầu. Bạn có thể xác định phạm vi một biến bằng cách sử dụng hàm. Vẫn không bấm cho tôi.

Câu trả lời:


120

Chức năng nâng có nghĩa là các chức năng được di chuyển lên đầu phạm vi của chúng. Đó là,

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

sẽ được viết lại bởi interpeter cho điều này

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

Kỳ lạ phải không?

Ngoài ra, trong trường hợp này,

function a() {}

cư xử giống như

var a = function () {};

Vì vậy, về bản chất, đây là những gì mã đang làm:

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
Vì vậy, tất cả các khai báo hàm cuối cùng được gán cho một biến?
dev.e.loper

15
@ dev.e.loper Có, trong Javascript, các hàm là các đối tượng lớp đầu tiên, giống như chuỗi và số. Điều đó có nghĩa là chúng được định nghĩa là các biến và có thể được chuyển cho các hàm khác, được lưu trữ trong mảng, v.v.
Peter Olson

4
Không có cách nào là cơ quan chức năng được "viết lại". Các tiêu chuẩn ECMAScript khác nhau nêu rõ rằng các khai báo biến và hàm được xử lý trước khi bắt đầu thực thi mã. Đó là, không có gì được di chuyển , đó là về thứ tự thực hiện (do đó tôi không thích thuật ngữ "cẩu", nghĩa là chuyển động hoặc sắp xếp lại). Trong đoạn mã được viết lại của bạn, phần khai báo var aphải ở trước phần khai báo hàm và phần gán a = 1phải ở sau. Nhưng lưu ý rằng điều này không được chỉ định để thực sự xảy ra bởi trình phân tích cú pháp, trình mã hóa, trình thông dịch, trình biên dịch, bất cứ điều gì, nó chỉ là tương đương.
RobG

3
@RobG Chắc chắn, tôi đoán bạn có thể gọi mô tả là một "lời nói dối trẻ em" nhỏ , nhưng cuối cùng thì hành vi vẫn giống nhau, cho dù mã được sắp xếp lại theo nghĩa đen hay chỉ là thứ tự thực hiện được sắp xếp lại. Những gì thực sự xảy ra đằng sau hậu trường là mối quan tâm học tập nhiều hơn, và thậm chí có thể phụ thuộc vào việc triển khai.
Peter Olson,

7
“Ngoài ra, trong trường hợp này, function a() {}hoạt động giống như var a = function () {};  - điều này không chính xác theo hai cách: thứ nhất, nếu có, thì nó sẽ như vậy var a = function a() {};(hàm thực sự không ẩn danh), thứ hai, hai dạng đó không thể hoán đổi cho nhau, bởi vì từ var a = function a() {};chỉ var a;một phần sẽ được nâng lên. Phần a = function a() {};sẽ vẫn nằm sau câu lệnh return. Bởi vì biểu mẫu ban đầu là một khai báo hàm chứ không phải là một biểu thức hàm, nó thực sự được đưa vào tổng thể.
user4642212

6

Điều bạn phải nhớ là nó phân tích cú pháp toàn bộ hàm và giải quyết tất cả các khai báo biến trước khi thực thi nó. Vì thế....

function a() {} 

thực sự trở thành

var a = function () {}

var a buộc nó vào một phạm vi cục bộ và phạm vi biến là thông qua toàn bộ hàm, do đó, biến toàn cục vẫn là 1 vì bạn đã khai báo a thành phạm vi cục bộ bằng cách biến nó thành một hàm.


5

Chức năng ađược nâng bên trong chức năng b:

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

gần giống như sử dụng var:

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

Hàm được khai báo cục bộ và cài đặt achỉ xảy ra trong phạm vi cục bộ, không phải var toàn cục.


1
dòng này "var a = function () {};" làm cho mọi thứ rõ ràng .. về cơ bản JavaScript là ngôn ngữ động và "function" cũng là một đối tượng trong JavaScript.
refactor

3
  1. khai báo hàm function a(){}được đưa vào đầu tiên và nó hoạt động như thế nào var a = function () {};, do đó trong phạm vi cục bộ ađược tạo.
  2. Nếu bạn có hai biến có cùng tên (một trong toàn cục khác trong cục bộ), biến cục bộ luôn được ưu tiên hơn biến toàn cục.
  3. Khi bạn đặt a=10, bạn đang đặt biến cục bộ a, không phải biến toàn cục.

Do đó, giá trị của biến toàn cục vẫn giữ nguyên và bạn nhận được, cảnh báo 1


1

function a() { }là một câu lệnh hàm, tạo một abiến cục bộ cho bhàm.
Các biến được tạo khi một hàm được phân tích cú pháp, bất kể varcâu lệnh hoặc hàm có được thực thi hay không.

a = 10 đặt biến cục bộ này.


thực sự a = 10đặt một biến trong phạm vi toàn cục khi hàm bđược thực thi trừ khi bạn thêm "use strict"(trong các môi trường như hỗ trợ chỉ thị đó).
Sean Vieira

@Sean: Không, vì câu lệnh hàm tạo một mã định danh cục bộ.
SLaks

... và .... bạn nói đúng. Đã không nhận ra rằng hệ quả cụ thể của chức năng cẩu. Cảm ơn!
Sean Vieira

1

Cốt lõi của sự tranh cãi trong đoạn mã nhỏ này là gì?

Trường hợp 1:

Bao gồm function a(){}định nghĩa bên trong nội dung function bnhư sau.logs value of a = 1

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

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

Trường hợp 2

Loại trừ function a(){}định nghĩa bên trong nội dung function bnhư sau.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

Quan sát sẽ giúp bạn nhận ra rằng câu lệnh console.log(a)ghi lại các giá trị sau.

Trường hợp 1: a = 1

Trường hợp 2: a = 10

Bài đăng

  1. var a đã được định nghĩa và khai báo từ vựng trong phạm vi toàn cầu.
  2. a=10 Câu lệnh này đang gán lại giá trị cho 10, nó nằm trong từ vựng bên trong hàm b.

Giải thích về cả hai trường hợp

Bởi vì function definition with name propertya cũng giống như variable a. Bên variable atrong function body btrở thành một biến cục bộ. Dòng trước ngụ ý rằng giá trị toàn cục của a vẫn còn nguyên và giá trị cục bộ của a được cập nhật thành 10.

Vì vậy, những gì chúng tôi định nói là đoạn mã dưới đây

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

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

Nó được giải thích bởi trình thông dịch JS như sau.

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


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

Tuy nhiên, khi chúng ta loại bỏ function a(){} definition, được value of 'a'khai báo và định nghĩa bên ngoài hàm b, giá trị đó sẽ bị ghi đè và nó chuyển thành 10 trong trường hợp 2. Giá trị bị ghi đè vì a=10tham chiếu đến khai báo toàn cục và nếu nó được khai báo cục bộ, chúng ta phải có bằng văn bản 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

Chúng tôi có thể làm rõ nghi ngờ của chúng tôi hơn nữa bằng cách thay đổi name propertytrong function a(){} definitionmột số tên khác hơn'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

Tời kéo là một khái niệm được tạo ra để chúng ta dễ hiểu hơn. Điều thực sự xảy ra là các khai báo được thực hiện đầu tiên đối với phạm vi của chúng và các nhiệm vụ sẽ xảy ra sau đó (không phải cùng một lúc).

Khi các khai báo xảy ra, var athì function bvà bên trong bphạm vi đó , function asẽ được khai báo.

Hàm a này sẽ ẩn biến a đến từ phạm vi toàn cục.

Sau khi khai báo xong, các giá trị được gán sẽ bắt đầu, toàn cục asẽ nhận giá trị 1và a bên trong function bsẽ nhận 10. khi bạn thực hiện alert(a), nó sẽ gọi biến phạm vi toàn cầu thực tế. Thay đổi nhỏ này đối với mã sẽ làm cho nó rõ ràng hơn

        var a = 1;

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

        function a() { }
    }

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

1
Thật là tò mò khi rất nhiều chuyên gia ngay cả trong một khóa học tại Codechool.com đề cập đến việc nâng cấp mà chỉ là một cái nhìn đơn giản về những gì sẽ xảy ra, thực sự việc nâng cấp hoàn toàn không xảy ra. Tham khảo: 1) developer.mozilla.org/en-US/docs/Glossary/ Danh sách 2) Chương 5 của Bí mật về JavaScript Ninja 2 / e của John từ chức, bear bebeault, josip maras
adnan2nd

1

Đáng ngạc nhiên là không có câu trả lời nào ở đây đề cập đến mức độ liên quan của Bối cảnh Thực thi trong Chuỗi Phạm vi.

Công cụ JavaScript bao bọc mã hiện đang thực thi trong Ngữ cảnh thực thi. Bối cảnh thực thi cơ sở là Bối cảnh thực thi toàn cầu. Mỗi khi một hàm mới được gọi, một Bối cảnh thực thi mới sẽ được tạo và đưa vào Ngăn xếp Thực thi. Hãy nghĩ về một Stack Frame nằm trên một Invocation Stack bằng các ngôn ngữ lập trình khác. Cuối cùng trong lần xuất trước. Bây giờ mỗi Bối cảnh thực thi có Môi trường biến và Môi trường bên ngoài riêng trong JavaScript.

Tôi sẽ sử dụng ví dụ dưới đây như một minh chứng.

1) Đầu tiên, chúng ta bước vào Giai đoạn tạo ra bối cảnh thực thi toàn cầu. Cả Môi trường Bên ngoài và Môi trường Biến đổi của Môi trường Lexical đều được tạo ra. Đối tượng Toàn cục được thiết lập và đặt trong bộ nhớ với biến đặc biệt 'this' trỏ đến nó. Hàm a và mã của nó và biến myVar có giá trị không xác định được đặt trong bộ nhớ trong Môi trường biến toàn cục. điều quan trọng cần lưu ý là mã của hàm a không được thực thi. Nó chỉ được đặt trong bộ nhớ với chức năng a.

2) Thứ hai, đó là Giai đoạn Thực thi của Bối cảnh Thực thi. myVar không còn là một giá trị không xác định. Nó được khởi tạo với giá trị 1, được lưu trữ trong Môi trường biến toàn cầu. Hàm a được gọi và một ngữ cảnh thực thi mới được tạo.

3) Trong Ngữ cảnh Thực thi của hàm a, nó trải qua Giai đoạn Tạo và Thực thi của Ngữ cảnh Thực thi của chính nó. Nó có Môi trường bên ngoài và Môi trường biến đổi, do đó, Môi trường Lexical của riêng nó. Hàm b và biến myVar được lưu trữ trong Môi trường biến của nó. Môi trường Biến đổi này khác biệt với Môi trường Biến đổi toàn cầu. Vì hàm a nằm về mặt từ vựng (về mặt vật lý trong mã) ở cùng cấp với Ngữ cảnh thực thi toàn cầu, Môi trường bên ngoài của nó là Ngữ cảnh thực thi toàn cầu. Do đó, nếu hàm a tham chiếu đến một biến không có trong Môi trường biến của nó, nó sẽ tìm kiếm Chuỗi phạm vi và cố gắng tìm biến trong Môi trường biến của Bối cảnh thực thi toàn cục.

4) Hàm b được gọi trong hàm a. Một bối cảnh thực thi mới được tạo. Vì nó nằm trong từ vựng trong hàm a, Môi trường bên ngoài của nó là a. Vì vậy, khi nó tham chiếu đến myVar, vì myVar không có trong Môi trường biến của hàm b, nên nó sẽ tìm trong Môi trường biến của hàm a. Nó tìm thấy nó ở đó và console.log in ra 2. Nhưng nếu biến không nằm trong Môi trường biến của hàm a, thì vì Môi trường bên ngoài của hàm a là Bối cảnh thực thi toàn cục, thì Chuỗi phạm vi sẽ tiếp tục tìm kiếm ở đó.

5) Sau khi hàm b và a được thực thi xong, chúng sẽ xuất hiện từ Ngăn xếp thực thi. Công cụ JavaScript một luồng tiếp tục thực thi tại Ngữ cảnh thực thi toàn cầu. Nó gọi hàm b. Nhưng không có hàm b trong Môi trường biến toàn cục và không có Môi trường bên ngoài nào khác để tìm kiếm trong Ngữ cảnh thực thi toàn cầu. Do đó, một ngoại lệ được đưa ra bởi 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

Ví dụ dưới đây cho thấy Chuỗi phạm vi đang hoạt động. Trong Môi trường Biến của Bối cảnh Thực thi của hàm b, không có myVar. Vì vậy, nó tìm kiếm Môi trường bên ngoài của nó, đó là chức năng a. Hàm a cũng không có myVar trong Môi trường biến của nó. Vì vậy, Engine tìm kiếm chức năng Môi trường bên ngoài của a, là Môi trường bên ngoài của Bối cảnh thực thi toàn cầu và myVar được xác định ở đó. Do đó, console.log in ra 1.

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

  b();
}

var myVar = 1;
a();
> 1

Về Bối cảnh thực thi và Môi trường hợp lý liên quan đến nó, bao gồm Môi trường bên ngoài và Môi trường biến, cho phép xác định phạm vi của các biến trong JavaScript. Ngay cả khi bạn gọi cùng một hàm nhiều lần, đối với mỗi lệnh gọi, nó sẽ tạo Ngữ cảnh thực thi của riêng mình. Vì vậy, mỗi Ngữ cảnh thực thi sẽ có bản sao riêng của các biến trong Môi trường biến của nó. Không có chia sẻ của các biến.


0

Nó đang xảy ra vì tên biến giống như tên hàm có nghĩa là "a". Do đó, do Javascript lưu trữ nó cố gắng giải quyết xung đột đặt tên và nó sẽ trả về a = 1.

Tôi cũng bối rối về điều này cho đến khi tôi đọc bài đăng này trên "JavaScript Hoisting" http://www.ufthelp.com/2014/11/JavaScript-Hoisting.html

Hy vọng nó giúp.


0

Đây là bản tóm tắt của tôi về câu trả lời với nhiều chú thích hơn và một trò chơi thú vị để chơi với.

// 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

phạm vi & đóng & nâng (var / function)

  1. phạm vi: var toàn cầu có thể được truy cập ở bất kỳ nơi nào (toàn bộ phạm vi tệp), chỉ var cục bộ mới có thể được truy cập bởi phạm vi cục bộ (phạm vi hàm / khối)!
    Lưu ý: nếu một biến cục bộ không sử dụng từ khóa var trong một hàm, nó sẽ trở thành một biến toàn cục!
  2. close: một hàm bên trong hàm kia, có thể truy cập phạm vi cục bộ (hàm cha) & phạm vi toàn cục, các vars của nó không thể bị người khác truy cập! trừ khi, bạn trả lại nó dưới dạng giá trị trả lại!
  3. hoisting: di chuyển tất cả các vars / function khai báo / hủy khai báo lên đầu phạm vi, thay vì gán giá trị hoặc null!
    Lưu ý: nó chỉ di chuyển khai báo, không di chuyển giá trị!

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

Hoisting Trong JavaScript có nghĩa là, các khai báo biến được thực thi thông qua chương trình trước khi bất kỳ mã nào được thực thi. Do đó việc khai báo một biến ở bất kỳ đâu trong đoạn mã tương đương với việc khai báo nó ở đầu.


0

Tất cả phụ thuộc vào phạm vi của biến 'a'. Hãy để tôi giải thích bằng cách tạo phạm vi dưới dạng hình ảnh.

Ở đây JavaScript sẽ tạo 3 phạm vi.

i) Phạm vi toàn cầu. ii) Phạm vi chức năng b (). iii) Hàm a () phạm vi.

nhập mô tả hình ảnh ở đây

Nó rõ ràng khi bạn gọi phạm vi phương thức 'alert' thuộc về Global vào thời điểm đó, vì vậy nó sẽ chọn giá trị của biến 'a' từ Global scope chỉ là 1.


0

Bài dài!

Nhưng nó sẽ làm sạch không khí!

Cách thức hoạt động của Java Script là nó bao gồm một quá trình gồm hai bước:

  1. Biên dịch (có thể nói là) - Bước này đăng ký các biến và khai báo hàm và phạm vi tương ứng của chúng. Nó không liên quan đến việc đánh giá biểu thức hàm: var a = function(){}hoặc biểu thức biến (như gán 3cho xtrong trường hợp var x =3;đó không là gì ngoài việc đánh giá phần RHS.)

  2. Phiên dịch: Đây là phần thực thi / đánh giá.

Kiểm tra đầu ra của mã dưới đây để hiểu:

//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);

Hãy phá vỡ nó:

  1. Trong giai đoạn biên dịch, 'a' sẽ được đăng ký trong phạm vi toàn cầu với giá trị ' undefined'. Tương tự đối với ' c', giá trị của nó tại thời điểm này sẽ là ' undefined' chứ không phải ' function()'. ' b' sẽ được đăng ký như một hàm trong phạm vi toàn cầu. Inside b's scope,' f'sẽ được đăng ký dưới dạng một biến sẽ không được xác định tại thời điểm này và hàm' d'sẽ được đăng ký.

  2. Khi trình thông dịch chạy, các biến đã khai báo và function()(chứ không phải biểu thức) có thể được truy cập trước khi trình thông dịch đến dòng biểu thức thực. Vì vậy, các biến sẽ được in ra ' undefined' và hàm ẩn danh đã khai báo có thể được gọi trước đó. Tuy nhiên, việc cố gắng truy cập biến chưa được khai báo trước khi khởi tạo biểu thức của nó sẽ dẫn đến lỗi như:

console.log(e)
e = 3;

Bây giờ, điều gì sẽ xảy ra khi bạn có khai báo biến và hàm có cùng tên.

Câu trả lời là - các hàm luôn được đưa vào trước và nếu biến trùng tên được khai báo, nó được coi là trùng lặp và bị bỏ qua. Hãy nhớ rằng, thứ tự không quan trọng. Các hàm luôn được ưu tiên. Nhưng trong giai đoạn đánh giá, bạn có thể thay đổi tham chiếu biến thành bất kỳ thứ gì (Nó lưu trữ bất cứ thứ gì là nhiệm vụ cuối cùng) Hãy xem đoạn mã dưới đây:

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 là khái niệm hành vi của JavaScript. Hoists (ví dụ như chuyển động) là khái niệm giải thích cách thức và vị trí các biến nên được khai báo.

Trong JavaScript, một biến có thể được khai báo sau khi nó đã được sử dụng vì khai báo Hàm và khai báo biến luôn được trình thông dịch JavaScript di chuyển (“hoisted”) lên đầu phạm vi chứa của chúng.

Chúng ta gặp hai loại cẩu trong hầu hết các trường hợp.

1. khai báo biến số lưu trữ

Hãy hiểu điều này bằng đoạn mã này.

 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

Ở đây, khai báo của biến a sẽ được trình thông dịch javascript lưu trữ ở trên cùng một cách vô hình tại thời điểm biên dịch. Vì vậy, chúng tôi đã có thể nhận được giá trị của a. Nhưng cách khai báo biến này không được khuyến khích vì chúng ta nên khai báo biến lên đầu như thế này.

 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

hãy xem xét một ví dụ khác.

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

thực sự được hiểu như thế này:

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

Trong trường hợp này x sẽ là không xác định

Không quan trọng nếu mã đã thực thi có chứa khai báo biến. Hãy xem xét ví dụ này.

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

Chức năng này hóa ra là như thế này.

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

Trong khai báo biến, chỉ có định nghĩa biến được nâng lên, không có phép gán.

  1. Khai báo hàm hoists

Không giống như biến lưu trữ, nội dung hàm hoặc giá trị được gán cũng sẽ được nâng lên. Hãy xem xét mã này

 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();

Bây giờ chúng ta đã hiểu cả biến và hàm hoisting, hãy hiểu đoạn mã này ngay bây giờ.

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

Mã này sẽ thành ra như thế này.

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); 

Hàm a () sẽ có phạm vi cục bộ bên trong b (). a () sẽ được chuyển lên trên cùng trong khi giải thích mã với định nghĩa của nó (chỉ trong trường hợp hàm hoisting) vì vậy a bây giờ sẽ có phạm vi cục bộ và do đó sẽ không ảnh hưởng đến phạm vi toàn cục của a trong khi có phạm vi riêng bên trong hàm b () .


0

Từ kiến ​​thức của tôi, việc nâng cấp xảy ra với khai báo biến và khai báo hàm, ví dụ:

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

Điều gì xảy ra bên trong engine của JavaScript:

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

Hoặc là:

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

Nó sẽ trở thành:

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

Nhưng các phép gán như gán biến, gán biểu thức hàm sẽ không được lưu vào: Ví dụ:

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

Nó có thể trở thành như thế này:

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

0

Để mô tả lưu trữ trong javascript trong một câu là các biến và hàm được đưa lên đầu phạm vi mà chúng được khai báo.

nhập mô tả hình ảnh ở đây

Tôi giả sử bạn là người mới bắt đầu, để hiểu đúng về việc nâng cẩu, lúc đầu chúng ta đã hiểu sự khác biệt giữa undefinedReferenceError

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

bây giờ trong đoạn mã dưới đây chúng ta thấy gì? một biến và một biểu thức hàm là decleard.

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

nhưng hình ảnh thực tế với bằng chứng rằng cả biến và hàm đều được nâng lên trên cùng của phạm vi đó:

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));

Đầu ra của hai bản ghi đầu tiên không được xác địnhTypeError: getSum không phải là một hàm vì cả var totalAmogetSum đều được lưu trữ trên đầu phạm vi của chúng như dưới đây

 <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>

Nhưng đối với khai báo các hàm, toàn bộ các hàm được nâng lên trên cùng phạm vi của chúng.

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

Bây giờ, cùng một logic đối với các biến thể đó, giải thích hàm và khai báo hàm được khai báo bên trong phạm vi hàm. Điểm chính: chúng sẽ không được đưa lên đầu tệp ;

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;
            }
        }

Vì vậy, khi bạn sử dụng từ khóa var , biến và hàm được nâng lên trên cùng của phạm vi đó (phạm vi toàn cục và phạm vi hàm). Còn letconst , const và let vẫn nhận thức được phạm vi toàn cục và phạm vi hàm giống như var, nhưng các biến const và let cũng nhận thức được phạm vi khác được gọi là phạm vi bị chặn. phạm vi khối hiện diện bất cứ khi nào có một khối mã, chẳng hạn như vòng lặp for, câu lệnh if else, vòng lặp while, v.v.

Khi chúng ta sử dụng const và let để khai báo một biến trong phạm vi khối này, chỉ khai báo biến sẽ được lưu trên đầu của khối mà nó nằm trong đó và nó sẽ không được lưu trên đầu của hàm mẹ hoặc đầu của phạm vi toàn cầu mà nó được nâng lên.

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

Các biến trong ví dụ trên tàu sẽ được liệt kê như sau

 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: chức năng cẩu & cẩu biến đổi

function hoistingưu tiên greaterhơnvariable 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
})();


bằng

(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
})();

lý do đằng sau việc cẩu

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, constkhông tồn tại cần cẩu

(() => {
  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;
})();

refs

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.