JavaScript là một ngôn ngữ rất hướng chức năng. Nó cho chúng ta rất nhiều tự do. Một hàm có thể được tạo bất cứ lúc nào, được truyền dưới dạng đối số cho một hàm khác và sau đó được gọi từ một vị trí mã hoàn toàn khác
Chúng ta đã biết rằng một hàm có thể truy cập các biến bên ngoài nó ["biến ngoài"]
Nhưng điều gì sẽ xảy ra nếu các biến bên ngoài thay đổi kể từ khi một hàm được tạo?
Và điều gì sẽ xảy ra nếu một hàm được truyền dưới dạng đối số và được gọi từ một vị trí khác của mã, liệu nó có quyền truy cập vào các biến bên ngoài ở vị trí mới không?
Hãy mở rộng kiến thức của chúng ta để hiểu những tình huống này và những tình huống phức tạp hơn
Chúng ta sẽ nói về biến
3 ở đây{
// show message
let message = "Hello";
alert[message];
}
{
// show another message
let message = "Goodbye";
alert[message];
}
Trong JavaScript có 3 cách khai báo biến.
{
// show message
let message = "Hello";
alert[message];
}
{
// show another message
let message = "Goodbye";
alert[message];
}
4, {
// show message
let message = "Hello";
alert[message];
}
{
// show another message
let message = "Goodbye";
alert[message];
}
5 [hiện đại], và {
// show message
let message = "Hello";
alert[message];
}
{
// show another message
let message = "Goodbye";
alert[message];
}
6 [tàn dư của quá khứ]- Trong bài viết này, chúng ta sẽ sử dụng biến
4 trong các ví dụ{ // show message let message = "Hello"; alert[message]; } { // show another message let message = "Goodbye"; alert[message]; }
- Các biến, được khai báo với
5, hoạt động giống nhau, vì vậy bài viết này cũng về{ // show message let message = "Hello"; alert[message]; } { // show another message let message = "Goodbye"; alert[message]; }
5{ // show message let message = "Hello"; alert[message]; } { // show another message let message = "Goodbye"; alert[message]; }
6 cũ có một số điểm khác biệt đáng chú ý, chúng sẽ được đề cập trong bài viết "var" cũ{ // show message let message = "Hello"; alert[message]; } { // show another message let message = "Goodbye"; alert[message]; }
Nếu một biến được khai báo bên trong khối mã
// show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
1, thì nó chỉ hiển thị bên trong khối đóVí dụ
{
// do some job with local variables that should not be seen outside
let message = "Hello"; // only visible in this block
alert[message]; // Hello
}
alert[message]; // Error: message is not defined
Chúng ta có thể sử dụng điều này để cô lập một đoạn mã thực hiện nhiệm vụ riêng của nó, với các biến chỉ thuộc về nó
{
// show message
let message = "Hello";
alert[message];
}
{
// show another message
let message = "Goodbye";
alert[message];
}
Sẽ có lỗi nếu không có khối
Xin lưu ý, nếu không có các khối riêng biệt, sẽ có lỗi, nếu chúng tôi sử dụng
{
// show message
let message = "Hello";
alert[message];
}
{
// show another message
let message = "Goodbye";
alert[message];
}
4 với tên biến hiện có________số 8_______
Đối với
// show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
3, // show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
4, // show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
5, v.v., các biến được khai báo trong // show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
1 cũng chỉ hiển thị bên trongif [true] {
let phrase = "Hello!";
alert[phrase]; // Hello!
}
alert[phrase]; // Error, no such variable!
Ở đây, sau khi
// show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
3 kết thúc, // show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
8 bên dưới sẽ không nhìn thấy // show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
9, do đó xảy ra lỗiĐiều đó thật tuyệt, vì nó cho phép chúng tôi tạo các biến khối cục bộ, dành riêng cho nhánh
// show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
3Điều tương tự cũng đúng với các vòng lặp
// show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
4 và // show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
5for [let i = 0; i < 3; i++] {
// the variable i is only visible inside this for
alert[i]; // 0, then 1, then 2
}
alert[i]; // Error, no such variable
Trực quan,
if [true] {
let phrase = "Hello!";
alert[phrase]; // Hello!
}
alert[phrase]; // Error, no such variable!
3 nằm ngoài // show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
1. Nhưng cấu trúc // show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
4 đặc biệt ở đây. biến, được khai báo bên trong nó, được coi là một phần của khốiMột chức năng được gọi là “lồng nhau” khi nó được tạo bên trong một chức năng khác
Có thể dễ dàng làm điều này với JavaScript
Chúng ta có thể sử dụng nó để sắp xếp mã của mình, như thế này
function sayHiBye[firstName, lastName] {
// helper nested function to use below
function getFullName[] {
return firstName + " " + lastName;
}
alert[ "Hello, " + getFullName[] ];
alert[ "Bye, " + getFullName[] ];
}
Ở đây, hàm lồng nhau
if [true] {
let phrase = "Hello!";
alert[phrase]; // Hello!
}
alert[phrase]; // Error, no such variable!
6 được tạo ra để thuận tiện. Nó có thể truy cập các biến bên ngoài và do đó có thể trả về tên đầy đủ. Các hàm lồng nhau khá phổ biến trong JavaScriptĐiều thú vị hơn nhiều, một hàm lồng nhau có thể được trả về. hoặc là một thuộc tính của một đối tượng mới hoặc là kết quả của chính nó. Sau đó nó có thể được sử dụng ở nơi khác. Bất kể ở đâu, nó vẫn có quyền truy cập vào các biến bên ngoài giống nhau
Dưới đây,
if [true] {
let phrase = "Hello!";
alert[phrase]; // Hello!
}
alert[phrase]; // Error, no such variable!
7 tạo hàm “bộ đếm” trả về số tiếp theo trên mỗi lệnh gọifunction makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
Mặc dù đơn giản, các biến thể được sửa đổi một chút của mã đó có những ứng dụng thực tế, chẳng hạn như một trình tạo số ngẫu nhiên để tạo các giá trị ngẫu nhiên cho các thử nghiệm tự động
Cái này hoạt động ra sao?
Hiểu những điều như vậy là rất tốt cho kiến thức tổng thể về JavaScript và có lợi cho các tình huống phức tạp hơn. Vì vậy, hãy đi sâu một chút
Đây là những con rồng
Phần giải thích kỹ thuật chuyên sâu nằm ở phía trước
Theo như tôi muốn tránh các chi tiết ngôn ngữ cấp thấp, bất kỳ sự hiểu biết nào nếu không có chúng sẽ thiếu sót và không đầy đủ, vì vậy hãy sẵn sàng
Để rõ ràng, giải thích được chia thành nhiều bước
Trong JavaScript, mọi chức năng đang chạy, khối mã
// show message
let message = "Hello";
alert[message];
// show another message
let message = "Goodbye"; // Error: variable already declared
alert[message];
1 và toàn bộ tập lệnh đều có một đối tượng liên quan [ẩn] bên trong được gọi là Môi trường từ vựngĐối tượng Lexical Environment bao gồm hai phần
- Bản ghi môi trường – một đối tượng lưu trữ tất cả các biến cục bộ làm thuộc tính của nó [và một số thông tin khác như giá trị của
9]if [true] { let phrase = "Hello!"; alert[phrase]; // Hello! } alert[phrase]; // Error, no such variable!
- Tham chiếu đến môi trường từ vựng bên ngoài, môi trường được liên kết với mã bên ngoài
Một “biến” chỉ là một thuộc tính của đối tượng bên trong đặc biệt,
for [let i = 0; i < 3; i++] {
// the variable i is only visible inside this for
alert[i]; // 0, then 1, then 2
}
alert[i]; // Error, no such variable
0. “Nhận hoặc thay đổi một biến” có nghĩa là “lấy hoặc thay đổi một thuộc tính của đối tượng đó”Trong mã đơn giản này không có chức năng, chỉ có một Môi trường từ điển
Đây được gọi là Môi trường từ điển toàn cầu, được liên kết với toàn bộ tập lệnh
Ở hình trên, hình chữ nhật có nghĩa là Bản ghi môi trường [lưu trữ biến] và mũi tên có nghĩa là tham chiếu bên ngoài. Môi trường từ điển toàn cầu không có tham chiếu bên ngoài, đó là lý do tại sao mũi tên chỉ tới
for [let i = 0; i < 3; i++] {
// the variable i is only visible inside this for
alert[i]; // 0, then 1, then 2
}
alert[i]; // Error, no such variable
1Khi mã bắt đầu thực thi và tiếp tục, Môi trường từ vựng sẽ thay đổi
Đây là đoạn mã dài hơn một chút
Hình chữ nhật ở phía bên tay phải thể hiện cách Môi trường từ điển toàn cầu thay đổi trong quá trình thực thi
- Khi tập lệnh bắt đầu, Môi trường từ vựng được điền trước với tất cả các biến đã khai báo
- Ban đầu, chúng ở trạng thái “Uninitialized”. Đó là một trạng thái bên trong đặc biệt, nó có nghĩa là động cơ biết về biến, nhưng nó không thể được tham chiếu cho đến khi nó được khai báo với
4. Nó gần giống như thể biến không tồn tại{ // show message let message = "Hello"; alert[message]; } { // show another message let message = "Goodbye"; alert[message]; }
- Ban đầu, chúng ở trạng thái “Uninitialized”. Đó là một trạng thái bên trong đặc biệt, nó có nghĩa là động cơ biết về biến, nhưng nó không thể được tham chiếu cho đến khi nó được khai báo với
- Sau đó, định nghĩa
3 xuất hiện. Chưa có bài tập nào, vì vậy giá trị của nó làfor [let i = 0; i < 3; i++] { // the variable i is only visible inside this for alert[i]; // 0, then 1, then 2 } alert[i]; // Error, no such variable
4. Chúng ta có thể sử dụng biến từ thời điểm này trở đifor [let i = 0; i < 3; i++] { // the variable i is only visible inside this for alert[i]; // 0, then 1, then 2 } alert[i]; // Error, no such variable
9 được gán một giá trị// show message let message = "Hello"; alert[message]; // show another message let message = "Goodbye"; // Error: variable already declared alert[message];
9 thay đổi giá trị// show message let message = "Hello"; alert[message]; // show another message let message = "Goodbye"; // Error: variable already declared alert[message];
Mọi thứ có vẻ đơn giản cho bây giờ, phải không?
- Biến là một thuộc tính của một đối tượng bên trong đặc biệt, được liên kết với khối/chức năng/tập lệnh hiện đang thực thi
- Làm việc với biến thực chất là làm việc với thuộc tính của đối tượng đó
Lexical Environment là một đối tượng đặc tả
“Môi trường từ điển” là một đối tượng đặc tả. nó chỉ tồn tại “về mặt lý thuyết” để mô tả cách mọi thứ hoạt động. Chúng tôi không thể lấy đối tượng này trong mã của mình và thao tác trực tiếp với nó
Các công cụ JavaScript cũng có thể tối ưu hóa nó, loại bỏ các biến không được sử dụng để tiết kiệm bộ nhớ và thực hiện các thủ thuật nội bộ khác, miễn là hành vi hiển thị vẫn như mô tả
Một chức năng cũng là một giá trị, giống như một biến
Sự khác biệt là Khai báo hàm được khởi tạo đầy đủ ngay lập tức
Khi một Môi trường từ điển được tạo, một Khai báo hàm ngay lập tức trở thành một hàm sẵn sàng sử dụng [không giống như
{
// show message
let message = "Hello";
alert[message];
}
{
// show another message
let message = "Goodbye";
alert[message];
}
4, không thể sử dụng được cho đến khi khai báo]Đó là lý do tại sao chúng ta có thể sử dụng một hàm, được khai báo là Khai báo hàm, ngay cả trước khi chính khai báo đó
Ví dụ, đây là trạng thái ban đầu của Global Lexical Environment khi chúng ta thêm một hàm
Đương nhiên, hành vi này chỉ áp dụng cho Khai báo Hàm, không áp dụng cho Biểu thức Hàm khi chúng ta gán một hàm cho một biến, chẳng hạn như
for [let i = 0; i < 3; i++] {
// the variable i is only visible inside this for
alert[i]; // 0, then 1, then 2
}
alert[i]; // Error, no such variable
8Khi một chức năng chạy, khi bắt đầu cuộc gọi, một Môi trường từ điển mới được tạo tự động để lưu trữ các biến cục bộ và tham số của cuộc gọi
Chẳng hạn, đối với
for [let i = 0; i < 3; i++] {
// the variable i is only visible inside this for
alert[i]; // 0, then 1, then 2
}
alert[i]; // Error, no such variable
9, nó trông như thế này [việc thực thi nằm ở dòng, được đánh dấu bằng một mũi tên]Trong khi gọi hàm, chúng ta có hai Môi trường từ điển. cái bên trong [đối với lệnh gọi hàm] và cái bên ngoài [toàn cầu]
- Môi trường từ vựng bên trong tương ứng với việc thực thi hiện tại của
0. Nó có một tài sản duy nhất.function sayHiBye[firstName, lastName] { // helper nested function to use below function getFullName[] { return firstName + " " + lastName; } alert[ "Hello, " + getFullName[] ]; alert[ "Bye, " + getFullName[] ]; }
1, đối số chức năng. Chúng tôi đã gọifunction sayHiBye[firstName, lastName] { // helper nested function to use below function getFullName[] { return firstName + " " + lastName; } alert[ "Hello, " + getFullName[] ]; alert[ "Bye, " + getFullName[] ]; }
9, vì vậy giá trị củafor [let i = 0; i < 3; i++] { // the variable i is only visible inside this for alert[i]; // 0, then 1, then 2 } alert[i]; // Error, no such variable
1 làfunction sayHiBye[firstName, lastName] { // helper nested function to use below function getFullName[] { return firstName + " " + lastName; } alert[ "Hello, " + getFullName[] ]; alert[ "Bye, " + getFullName[] ]; }
4function sayHiBye[firstName, lastName] { // helper nested function to use below function getFullName[] { return firstName + " " + lastName; } alert[ "Hello, " + getFullName[] ]; alert[ "Bye, " + getFullName[] ]; }
- Lexical Environment bên ngoài là Lexical Environment toàn cầu. Nó có biến
9 và chính hàm// show message let message = "Hello"; alert[message]; // show another message let message = "Goodbye"; // Error: variable already declared alert[message];
Môi trường từ vựng bên trong có một tham chiếu đến
function sayHiBye[firstName, lastName] {
// helper nested function to use below
function getFullName[] {
return firstName + " " + lastName;
}
alert[ "Hello, " + getFullName[] ];
alert[ "Bye, " + getFullName[] ];
}
6Khi mã muốn truy cập một biến – Môi trường từ vựng bên trong được tìm kiếm trước, sau đó đến môi trường bên ngoài, sau đó là môi trường bên ngoài hơn, v.v. cho đến môi trường toàn cầu
Nếu không tìm thấy biến ở bất kỳ đâu, thì đó là lỗi ở chế độ nghiêm ngặt [không có
function sayHiBye[firstName, lastName] {
// helper nested function to use below
function getFullName[] {
return firstName + " " + lastName;
}
alert[ "Hello, " + getFullName[] ];
alert[ "Bye, " + getFullName[] ];
}
7, việc gán cho một biến không tồn tại sẽ tạo ra một biến toàn cục mới, để tương thích với mã cũ]Trong ví dụ này, quá trình tìm kiếm diễn ra như sau
- Đối với biến
1, biếnfunction sayHiBye[firstName, lastName] { // helper nested function to use below function getFullName[] { return firstName + " " + lastName; } alert[ "Hello, " + getFullName[] ]; alert[ "Bye, " + getFullName[] ]; }
8 bên trong// show message let message = "Hello"; alert[message]; // show another message let message = "Goodbye"; // Error: variable already declared alert[message];
0 sẽ tìm thấy nó ngay lập tức trong Môi trường từ vựng bên trongfunction sayHiBye[firstName, lastName] { // helper nested function to use below function getFullName[] { return firstName + " " + lastName; } alert[ "Hello, " + getFullName[] ]; alert[ "Bye, " + getFullName[] ]; }
- Khi nó muốn truy cập
9, thì không có ____8_______9 cục bộ, vì vậy nó theo tham chiếu đến Lexical Environment bên ngoài và tìm thấy nó ở đó// show message let message = "Hello"; alert[message]; // show another message let message = "Goodbye"; // Error: variable already declared alert[message];
Hãy quay lại ví dụ về
if [true] {
let phrase = "Hello!";
alert[phrase]; // Hello!
}
alert[phrase]; // Error, no such variable!
7function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
Khi bắt đầu mỗi lệnh gọi
function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
4, một đối tượng Môi trường Lexical mới được tạo để lưu trữ các biến cho lần chạy if [true] {
let phrase = "Hello!";
alert[phrase]; // Hello!
}
alert[phrase]; // Error, no such variable!
7 nàyVì vậy, chúng tôi có hai Môi trường từ vựng lồng nhau, giống như trong ví dụ trên
Điều khác biệt là, trong quá trình thực hiện
function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
4, một hàm nhỏ lồng nhau được tạo chỉ bằng một dòng. function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
7. Chúng tôi chưa chạy nó, chỉ tạoTất cả các chức năng ghi nhớ Môi trường từ điển mà chúng được tạo ra. Về mặt kỹ thuật, không có phép thuật nào ở đây. tất cả các hàm đều có thuộc tính ẩn tên là
function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
8, giữ tham chiếu đến Môi trường từ vựng nơi hàm được tạoVì vậy,
function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
9 có tham chiếu đến function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
0 Môi trường từ điển. Đó là cách chức năng nhớ nơi nó được tạo, bất kể nó được gọi ở đâu. Tham chiếu function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
8 được đặt một lần và mãi mãi tại thời điểm tạo hàmSau đó, khi
function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
2 được gọi, một Môi trường từ điển mới được tạo cho cuộc gọi và tham chiếu Môi trường từ điển bên ngoài của nó được lấy từ function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
9Bây giờ, khi mã bên trong
function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
2 tìm kiếm biến function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
5, đầu tiên nó tìm kiếm Môi trường từ điển của chính nó [trống, vì không có biến cục bộ ở đó], sau đó là Môi trường từ vựng của lệnh gọi function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
4 bên ngoài, nơi nó tìm và thay đổi nóMột biến được cập nhật trong Lexical Environment nơi nó tồn tại
Đây là trạng thái sau khi thực hiện
Nếu chúng ta gọi
function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
2 nhiều lần, biến function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
5 sẽ được tăng lên thành function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
9, function f[] {
let value = 123;
return function[] {
alert[value];
}
}
let g = f[]; // g.[[Environment]] stores a reference to the Lexical Environment
// of the corresponding f[] call
0, v.v., tại cùng một vị tríKhép kín
Có một thuật ngữ lập trình chung là “đóng cửa”, mà các nhà phát triển nói chung nên biết
Bao đóng là một hàm ghi nhớ các biến bên ngoài của nó và có thể truy cập chúng. Ở một số ngôn ngữ, điều đó là không thể hoặc một chức năng phải được viết theo cách đặc biệt để thực hiện. Nhưng như đã giải thích ở trên, trong JavaScript, tất cả các hàm đều là các hàm đóng một cách tự nhiên [chỉ có một ngoại lệ, được trình bày trong cú pháp "Hàm mới"]
Đó là. họ tự động ghi nhớ nơi chúng được tạo bằng cách sử dụng thuộc tính
function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
8 ẩn và sau đó mã của họ có thể truy cập các biến bên ngoàiKhi tham gia một cuộc phỏng vấn, một nhà phát triển giao diện người dùng nhận được câu hỏi về “đóng cửa là gì?”, một câu trả lời hợp lệ sẽ là định nghĩa về đóng cửa và giải thích rằng tất cả các chức năng trong JavaScript đều là đóng cửa và có thể thêm một vài từ về chi tiết kỹ thuật. thuộc tính
function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
8 và cách thức hoạt động của Môi trường từ điểnThông thường, một Môi trường từ điển sẽ bị xóa khỏi bộ nhớ cùng với tất cả các biến sau khi lệnh gọi hàm kết thúc. Đó là bởi vì không có tài liệu tham khảo cho nó. Giống như bất kỳ đối tượng JavaScript nào, nó chỉ được lưu trong bộ nhớ khi có thể truy cập được
Tuy nhiên, nếu có một hàm lồng nhau vẫn có thể truy cập được sau khi kết thúc hàm, thì hàm đó có thuộc tính
function makeCounter[] {
let count = 0;
return function[] {
return count++;
};
}
let counter = makeCounter[];
alert[ counter[] ]; // 0
alert[ counter[] ]; // 1
alert[ counter[] ]; // 2
8 tham chiếu đến môi trường từ vựngTrong trường hợp đó, Môi trường từ điển vẫn có thể truy cập được ngay cả sau khi hoàn thành chức năng, vì vậy nó vẫn tồn tại
Ví dụ
function f[] {
let value = 123;
return function[] {
alert[value];
}
}
let g = f[]; // g.[[Environment]] stores a reference to the Lexical Environment
// of the corresponding f[] call
Xin lưu ý rằng nếu
function f[] {
let value = 123;
return function[] {
alert[value];
}
}
let g = f[]; // g.[[Environment]] stores a reference to the Lexical Environment
// of the corresponding f[] call
4 được gọi nhiều lần và các hàm kết quả được lưu, thì tất cả các đối tượng Môi trường từ vựng tương ứng cũng sẽ được giữ lại trong bộ nhớ. Trong đoạn mã dưới đây, cả 3 người trong số họfunction f[] {
let value = Math.random[];
return function[] { alert[value]; };
}
// 3 functions in array, every one of them links to Lexical Environment
// from the corresponding f[] run
let arr = [f[], f[], f[]];
Một đối tượng Lexical Environment chết khi không thể truy cập được [giống như bất kỳ đối tượng nào khác]. Nói cách khác, nó chỉ tồn tại khi có ít nhất một hàm lồng nhau tham chiếu đến nó.
Trong mã bên dưới, sau khi hàm lồng nhau bị xóa, Môi trường từ điển kèm theo của nó [và do đó là _
{
// show message
let message = "Hello";
alert[message];
}
{
// show another message
let message = "Goodbye";
alert[message];
}
5] được xóa khỏi bộ nhớ{
// show message
let message = "Hello";
alert[message];
}
{
// show another message
let message = "Goodbye";
alert[message];
}
0Như chúng ta đã thấy, theo lý thuyết, khi một hàm còn hoạt động, tất cả các biến bên ngoài cũng được giữ lại
Nhưng trên thực tế, các công cụ JavaScript cố gắng tối ưu hóa điều đó. Họ phân tích việc sử dụng biến và nếu mã rõ ràng rằng một biến bên ngoài không được sử dụng – nó sẽ bị xóa
Một tác dụng phụ quan trọng trong V8 [Chrome, Edge, Opera] là biến đó sẽ không khả dụng khi gỡ lỗi
Hãy thử chạy ví dụ bên dưới trong Chrome khi mở Công cụ dành cho nhà phát triển
Khi nó tạm dừng, trong bảng điều khiển gõ
function f[] {
let value = 123;
return function[] {
alert[value];
}
}
let g = f[]; // g.[[Environment]] stores a reference to the Lexical Environment
// of the corresponding f[] call
6{
// show message
let message = "Hello";
alert[message];
}
{
// show another message
let message = "Goodbye";
alert[message];
}
1Như bạn có thể thấy - không có biến như vậy. Về lý thuyết, nó có thể truy cập được, nhưng công cụ đã tối ưu hóa nó
Điều đó có thể dẫn đến các vấn đề gỡ lỗi buồn cười [nếu không muốn nói là tốn thời gian]. Một trong số đó – chúng ta có thể thấy một biến ngoài cùng tên thay vì biến như mong đợi
{
// show message
let message = "Hello";
alert[message];
}
{
// show another message
let message = "Goodbye";
alert[message];
}
2Tính năng này của V8 thì hay biết. Nếu bạn đang debug với Chrome/Edge/Opera thì sớm muộn cũng gặp thôi
Đó không phải là lỗi trong trình gỡ lỗi, mà là một tính năng đặc biệt của V8. Có lẽ nó sẽ được thay đổi đôi khi. Bạn luôn có thể kiểm tra nó bằng cách chạy các ví dụ trên trang này