HomeLập trìnhJavaScriptHướng dẫn đóng...

Hướng dẫn đóng JavaScript – Với mã ví dụ đóng JS


đóng cửa – nhiều người trong số các nhà phát triển JavaScript của bạn có thể đã nghe thuật ngữ này trước đây. Khi tôi bắt đầu hành trình của mình với JavaScript, tôi thường xuyên gặp phải sự đóng cửa. Và tôi nghĩ chúng là một trong những khái niệm quan trọng và thú vị nhất trong JavaScript.

Bạn không nghĩ rằng họ đang thú vị? Điều này thường xảy ra khi bạn không hiểu một khái niệm – bạn không thấy nó thú vị. (Tôi không biết điều này có xảy ra với bạn hay không, nhưng đây là trường hợp của tôi).

Vì vậy, trong bài viết này, tôi sẽ cố gắng làm cho các bao đóng trở nên thú vị đối với bạn.

Trước khi đi vào thế giới của bao đóng, trước tiên chúng ta hãy hiểu phạm vi từ vựng. Nếu bạn đã biết về nó, hãy bỏ qua phần tiếp theo. Nếu không thì nhảy vào nó để hiểu rõ hơn về đóng cửa.

Phạm vi từ vựng

Bạn có thể đang nghĩ – Tôi biết phạm vi cục bộ và phạm vi toàn cầu, nhưng phạm vi từ vựng là cái quái gì vậy? Tôi đã phản ứng như vậy khi tôi nghe thuật ngữ này. Đừng lo lắng! Hãy xem xét kỹ hơn.

Nó đơn giản giống như hai phạm vi khác:

function greetCustomer() {
    var customerName = "anchal";
    function greetingMsg() {
	  console.log("Hi! " + customerName); // Hi! anchal
    }
   greetingMsg();
}

Bạn có thể thấy từ đầu ra ở trên rằng hàm bên trong có thể truy cập biến của hàm bên ngoài. Đây là phạm vi từ vựng, trong đó phạm vi và giá trị của một biến được xác định bởi nơi nó được xác định/tạo (nghĩa là vị trí của nó trong mã). Hiểu rồi?

Tôi biết rằng chút cuối cùng có thể đã làm bạn bối rối. Vì vậy, hãy để tôi đưa bạn sâu hơn. Bạn có biết rằng phạm vi từ vựng còn được gọi là phạm vi tĩnh? Vâng, đó là tên khác của nó.

Ngoài ra còn có phạm vi động, mà một số ngôn ngữ lập trình hỗ trợ. Tại sao tôi lại đề cập đến phạm vi động? Bởi vì nó có thể giúp bạn hiểu rõ hơn về phạm vi từ vựng.

Hãy xem xét một số ví dụ:

function greetingMsg() {
  console.log(customerName);// ReferenceError: customerName is not defined
}

function greetCustomer() {
   var customerName = "anchal";
   greetingMsg();
}

greetCustomer();

Bạn có đồng ý với đầu ra? Có, nó sẽ báo lỗi tham chiếu. Điều này là do cả hai chức năng không có quyền truy cập vào phạm vi của nhau, vì chúng được xác định riêng.

Hãy xem xét một ví dụ khác:

function addNumbers(number1) {
  console.log(number1 + number2);
}

function addNumbersGenerate() {
  var number2 = 10;
  addNumbers(number2);
}

addNumbersGenerate();

Đầu ra ở trên sẽ là 20 cho ngôn ngữ có phạm vi động. Các ngôn ngữ hỗ trợ phạm vi từ vựng sẽ cung cấp referenceError: number2 is not defined. Tại sao?

Đọc thêm  JavaScript 객체 배열 튜토리얼 - JS 배열 메서드를 사용해 객체를 생성, 업데이트 및 루프하는 방법

Bởi vì trong phạm vi động, việc tìm kiếm diễn ra trong hàm cục bộ trước, sau đó nó đi vào hàm mà gọi điện chức năng cục bộ đó. Sau đó, nó tìm kiếm trong chức năng được gọi là điều đó chức năng, v.v., lên ngăn xếp cuộc gọi.

Tên của nó là tự giải thích – “động” có nghĩa là thay đổi. Phạm vi và giá trị của biến có thể khác nhau vì nó phụ thuộc vào nơi hàm được gọi. Ý nghĩa của một biến có thể thay đổi trong thời gian chạy.

Có ý chính của phạm vi năng động? Nếu có, thì chỉ cần nhớ rằng phạm vi từ vựng thì ngược lại.

Trong phạm vi từ vựng, việc tìm kiếm diễn ra trong chức năng cục bộ trước, sau đó nó đi vào chức năng bên trong đó điều đó chức năng được xác định. Sau đó, nó tìm kiếm trong chức năng bên trong đó điều đó chức năng được xác định và như vậy.

Cho nên, từ vựng hoặc phạm vi tĩnh có nghĩa là phạm vi và giá trị của một biến được xác định từ nơi nó được xác định. Nó không thay đổi.

Hãy xem lại ví dụ trên và cố gắng tìm ra kết quả của riêng bạn. Chỉ cần một bước ngoặt – tuyên bố number2 ở trên cùng:

var number2 = 2;
function addNumbers(number1) {
  console.log(number1 + number2);
}

function addNumbersGenerate() {
  var number2 = 10;
  addNumbers(number2);
}

addNumbersGenerate();

Bạn có biết đầu ra sẽ là gì không?

Chính xác – đó là 12 cho các ngôn ngữ có phạm vi từ vựng. Điều này là bởi vì đầu tiên, nó nhìn vào một addNumbers chức năng (phạm vi trong cùng) thì nó tìm kiếm bên trong, nơi chức năng này được xác định. Khi nó nhận được number2 biến, nghĩa là đầu ra là 12.

Bạn có thể thắc mắc tại sao tôi lại dành quá nhiều thời gian cho phạm vi từ vựng ở đây. Đây là một bài báo kết thúc, không phải là một bài viết về phạm vi từ vựng. Nhưng nếu bạn không biết về phạm vi từ vựng thì bạn sẽ không hiểu về bao đóng.

Tại sao? Bạn sẽ nhận được câu trả lời của mình khi chúng tôi xem xét định nghĩa về sự đóng cửa. Vì vậy, hãy bắt đầu theo dõi và quay lại phần kết thúc.

Đóng cửa là gì?

Hãy xem định nghĩa của một bao đóng:

Bao đóng được tạo khi một hàm bên trong có quyền truy cập vào các biến và đối số của hàm bên ngoài. Chức năng bên trong có quyền truy cập vào –
1. Các biến riêng của nó.
2. Các biến và đối số của hàm bên ngoài.
3. Biến toàn cục.

Đợi đã! Đây có phải là định nghĩa về phạm vi đóng hoặc phạm vi từ vựng không? Cả hai định nghĩa trông giống nhau. Chúng khác nhau như thế nào?

Đọc thêm  Lời hứa JavaScript được giải thích

Chà, đó là lý do tại sao tôi đã xác định phạm vi từ vựng ở trên. Bởi vì bao đóng có liên quan đến phạm vi từ vựng/tĩnh.

Hãy xem lại định nghĩa khác của nó sẽ cho bạn biết các bao đóng khác nhau như thế nào.

Đóng là khi một chức năng có thể truy cập phạm vi từ vựng của nó, ngay cả khi chức năng đó đang thực thi bên ngoài phạm vi từ vựng của nó.

Hoặc,

Các hàm bên trong có thể truy cập phạm vi cha của nó, ngay cả sau khi hàm cha đã được thực thi.

Bối rối? Đừng lo lắng nếu bạn chưa nhận được điểm. Tôi có ví dụ để giúp bạn hiểu rõ hơn. Hãy sửa đổi ví dụ đầu tiên về phạm vi từ vựng:

function greetCustomer() {
  const customerName = "anchal";
  function greetingMsg() {
    console.log("Hi! " + customerName);
  }
  return greetingMsg;
}

const callGreetCustomer = greetCustomer();
callGreetCustomer(); // output – Hi! anchal

Sự khác biệt trong đoạn mã này là chúng ta sẽ trả lại hàm bên trong và thực thi nó sau. Trong một số ngôn ngữ lập trình, biến cục bộ tồn tại trong quá trình thực thi hàm. Nhưng một khi hàm được thực thi, các biến cục bộ đó không tồn tại và chúng sẽ không thể truy cập được.

Tuy nhiên, ở đây, khung cảnh lại khác. Sau khi hàm cha được thực thi, hàm bên trong (hàm trả về) vẫn có thể truy cập các biến của hàm cha. Vâng, bạn đã đoán đúng. Đóng cửa là lý do.

Hàm bên trong duy trì phạm vi từ vựng của nó khi hàm mẹ đang thực thi và do đó, sau đó, hàm bên trong đó có thể truy cập các biến đó.

Để có cảm giác tốt hơn về nó, hãy sử dụng dir() phương thức của bàn điều khiển để xem danh sách các thuộc tính của callGreetCustomer:

console.dir(callGreetCustomer);
Khép kín

Từ hình trên, bạn có thể thấy cách hàm bên trong bảo toàn phạm vi cha của nó (customerName) khi nào greetCustomer() được thực thi. Và sau này, nó được sử dụng customerName khi nào callGreetCustomer() đã được thực hiện.

Tôi hy vọng ví dụ này đã giúp bạn hiểu rõ hơn về định nghĩa của một bao đóng ở trên. Và có lẽ bây giờ bạn thấy việc đóng cửa vui hơn một chút.

Vì vậy, những gì tiếp theo? Hãy làm cho chủ đề này thú vị hơn bằng cách xem xét các ví dụ khác nhau.

Ví dụ về đóng cửa trong hành động

function counter() {
  let count = 0;
  return function() {
    return count++;
  };
}

const countValue = counter();
countValue(); // 0
countValue(); // 1
countValue(); // 2

Mỗi khi bạn gọi countValuegiá trị của biến đếm được tăng thêm 1. Đợi đã – bạn có nghĩ rằng giá trị của biến đếm là 0 không?

Đọc thêm  Hàm bậc cao hơn trong JavaScript – Hướng dẫn cho người mới bắt đầu

Chà, điều đó sẽ sai vì một lần đóng không hoạt động với một giá trị. Nó lưu trữ các thẩm quyền giải quyết của biến. Đó là lý do tại sao, khi chúng tôi cập nhật giá trị, nó sẽ phản ánh trong lần gọi thứ hai hoặc thứ ba, v.v. khi bao đóng lưu trữ tham chiếu.

Cảm thấy rõ ràng hơn một chút bây giờ? Hãy xem xét một ví dụ khác:

function counter() {
  let count = 0;
  return function () {
    return count++;
  };
}

const countValue1 = counter();
const countValue2 = counter();
countValue1();  // 0
countValue1();  // 1
countValue2();   // 0
countValue2();   // 1

Tôi hy vọng bạn đoán đúng câu trả lời. Nếu không, đây là lý do. Như countValue1countValue2, cả hai đều bảo tồn phạm vi từ vựng của riêng mình. Họ có môi trường từ vựng độc lập. Bạn có thể dùng dir() để kiểm tra [[scopes]] giá trị trong cả hai trường hợp.

Hãy xem xét một ví dụ thứ ba.

Cái này hơi khác một chút. Trong đó, chúng ta phải viết một hàm để đạt được đầu ra:

const addNumberCall = addNumber(7);
addNumberCall(8) // 15
addNumberCall(6) // 13

Giản dị. Sử dụng kiến ​​thức đóng cửa mới đạt được của bạn:

function addNumber(number1) {
  return function (number2) {
    return number1 + number2;
  };
}

Bây giờ hãy xem xét một số ví dụ phức tạp:

function countTheNumber() {
  var arrToStore = [];
  for (var x = 0; x < 9; x++) {
    arrToStore[x] = function () {
      return x;
    };
  }
  return arrToStore;
}

const callInnerFunctions = countTheNumber();
callInnerFunctions[0]() // 9
callInnerFunctions[1]() // 9

Mọi phần tử mảng lưu trữ một hàm sẽ cho bạn kết quả là 9. Bạn có đoán đúng không? Tôi hy vọng như vậy, nhưng vẫn để tôi nói cho bạn biết lý do. Điều này là do hành vi của việc đóng cửa.

Việc đóng cửa lưu trữ các thẩm quyền giải quyết, không phải giá trị. Lần đầu tiên vòng lặp chạy, giá trị của x là 0. Sau đó, lần thứ hai x là 1, v.v. Bởi vì bao đóng lưu trữ tham chiếu, nên mỗi khi vòng lặp chạy, nó sẽ thay đổi giá trị của x. Và cuối cùng, giá trị của x sẽ là 9. Vậy callInnerFunctions[0]() cho đầu ra là 9.

Nhưng nếu bạn muốn đầu ra từ 0 đến 8 thì sao? Giản dị! Sử dụng một đóng cửa.

Hãy suy nghĩ về nó trước khi nhìn vào giải pháp dưới đây:

function callTheNumber() {
  function getAllNumbers(number) {
    return function() {
      return number;
    };
  }
  var arrToStore = [];
  for (var x = 0; x < 9; x++) {
    arrToStore[x] = getAllNumbers(x);
  }
  return arrToStore;
}

const callInnerFunctions = callTheNumber();
console.log(callInnerFunctions[0]()); // 0
console.log(callInnerFunctions[1]()); // 1

Ở đây, chúng tôi đã tạo phạm vi riêng cho mỗi lần lặp. Bạn có thể dùng console.dir(arrToStore) để kiểm tra giá trị của x trong [[scopes]] cho các phần tử mảng khác nhau.

Đó là nó! Tôi hy vọng bây giờ bạn có thể nói rằng bạn thấy sự khép kín thú vị.

Để đọc các bài viết khác của tôi, hãy kiểm tra hồ sơ của tôi ở đây.



Zik.vn – Biên dịch & Biên soạn Lại

spot_img

Create a website from scratch

Just drag and drop elements in a page to get started with Newspaper Theme.

Buy Now ⟶

Bài viết liên quang

DMCA.com Protection Status