Hướng dẫn what is difference between nested function and closure in javascript? - Sự khác biệt giữa hàm lồng nhau và hàm đóng trong javascript là gì?

One function written inside another function is called a nested function. Suppose that there are two functions outer function and inside it there is an inner function. The inner function will have access to its own variables, the outer functions variables, arguments and it has access to global variables. This is done by a scope chain. Every function so created has a scope chain associated with which helps us in accessing the variables value.

Now what are closures? Closures - the nested functions continue to live even when the outer function has completed is closure. The day to day way in which we are able to see closure is callbacks. Callbacks are functions which are usually executed when an event completes, when they are executed they have access to outer functions variables even though the outer function has completed its execution.

Đóng cửa là sự kết hợp của một hàm được gói với nhau (kèm theo) với các tham chiếu đến trạng thái xung quanh (môi trường từ vựng). Nói cách khác, việc đóng cửa cho phép bạn truy cập vào phạm vi của hàm bên ngoài từ hàm bên trong. Trong JavaScript, đóng cửa được tạo mỗi khi một hàm được tạo, tại thời điểm tạo chức năng.closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

Phạm vi từ vựng

Xem xét mã ví dụ sau:

function init() {
  var name = 'Mozilla'; // name is a local variable created by init
  function displayName() {
    // displayName() is the inner function, a closure
    console.log(name); // use variable declared in the parent function
  }
  displayName();
}
init();

if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
8 tạo ra một biến cục bộ gọi là
if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
9 và một hàm gọi là
function makeFunc() {
  const name = 'Mozilla';
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();
0. Hàm
function makeFunc() {
  const name = 'Mozilla';
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();
0 là một hàm bên trong được xác định bên trong
if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
8 và chỉ có sẵn trong phần thân của hàm
if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
8. Lưu ý rằng hàm
function makeFunc() {
  const name = 'Mozilla';
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();
0 không có biến cục bộ của riêng nó. Tuy nhiên, vì các hàm bên trong có quyền truy cập vào các biến của các hàm bên ngoài,
function makeFunc() {
  const name = 'Mozilla';
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();
0 có thể truy cập biến
if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
9 được khai báo trong hàm cha,
if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
8.

Chạy mã bằng liên kết JSFiddle này và nhận thấy rằng câu lệnh

function makeFunc() {
  const name = 'Mozilla';
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();
8 trong hàm
function makeFunc() {
  const name = 'Mozilla';
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();
0 hiển thị thành công giá trị của biến
if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
9, được khai báo trong hàm cha của nó. Đây là một ví dụ về phạm vi từ vựng, mô tả cách trình phân tích cú pháp giải quyết các tên biến khi các hàm được lồng. Từ từ vựng đề cập đến thực tế là phạm vi từ vựng sử dụng vị trí mà một biến được khai báo trong mã nguồn để xác định vị trí đó có sẵn. Các hàm lồng nhau có quyền truy cập vào các biến được khai báo trong phạm vi bên ngoài của chúng.

Trong ví dụ cụ thể này, phạm vi được gọi là phạm vi hàm, bởi vì biến có thể truy cập và chỉ có thể truy cập trong thân hàm nơi nó được khai báo.

Phạm vi với LET và const

Theo truyền thống (trước ES6), JavaScript chỉ có hai loại phạm vi: phạm vi chức năng và phạm vi toàn cầu. Các biến được khai báo với

function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
1 là các chức năng hoặc kết quả toàn cầu, tùy thuộc vào việc chúng được khai báo trong một hàm hay bên ngoài một hàm. Điều này có thể khó khăn, bởi vì các khối có niềng răng xoăn không tạo ra phạm vi:

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);

Đối với những người từ các ngôn ngữ khác (ví dụ: C, Java) trong đó các khối tạo ra phạm vi, mã trên sẽ gây ra lỗi trên dòng

function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
2, vì chúng ta nằm ngoài phạm vi
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
3 trong một số khối. Tuy nhiên, vì các khối không tạo ra phạm vi cho
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
1, các câu lệnh
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
1 ở đây thực sự tạo ra một biến toàn cầu. Ngoài ra còn có một ví dụ thực tế được giới thiệu dưới đây minh họa cách điều này có thể gây ra lỗi thực tế khi kết hợp với đóng cửa.

Trong ES6, JavaScript đã giới thiệu các khai báo

function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
6 và
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
7, trong số những thứ khác như vùng chết tạm thời, cho phép bạn tạo các biến có khối khối.

if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined

Về bản chất, các khối cuối cùng được coi là phạm vi trong ES6, nhưng chỉ khi bạn khai báo các biến có

function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
6 hoặc
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
7. Ngoài ra, ES6 đã giới thiệu các mô -đun, giới thiệu một loại phạm vi khác. Đóng cửa có thể nắm bắt các biến trong tất cả các phạm vi này, mà chúng tôi sẽ giới thiệu sau.

Khép kín

Xem xét ví dụ mã sau:

function makeFunc() {
  const name = 'Mozilla';
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();

Chạy mã này có hiệu ứng chính xác như ví dụ trước của hàm

if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
8 ở trên. Điều khác biệt (và thú vị) là hàm bên trong
function makeFunc() {
  const name = 'Mozilla';
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();
0 được trả về từ hàm bên ngoài trước khi được thực thi.

Thoạt nhìn, có vẻ không trực quan khi mã này vẫn hoạt động. Trong một số ngôn ngữ lập trình, các biến cục bộ trong một hàm tồn tại chỉ trong thời gian thực hiện của hàm đó. Khi

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h2 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
2 kết thúc thực thi, bạn có thể mong đợi rằng biến
if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
9 sẽ không còn có thể truy cập được nữa. Tuy nhiên, vì mã vẫn hoạt động như mong đợi, điều này rõ ràng không phải là trường hợp trong JavaScript.

Lý do là các chức năng trong các hình thức đóng mẫu JavaScript. Đóng cửa là sự kết hợp của một hàm và môi trường từ vựng trong đó hàm đó được khai báo. Môi trường này bao gồm bất kỳ biến cục bộ nào trong phạm vi tại thời điểm đóng cửa được tạo. Trong trường hợp này,

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h2 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
4 là một tham chiếu đến thể hiện của hàm
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h2 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
5 được tạo khi
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h2 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
6 được chạy. Ví dụ của
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h2 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
5 duy trì một tham chiếu đến môi trường từ vựng của nó, trong đó biến
if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
9 tồn tại. Vì lý do này, khi
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h2 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
4 được gọi, biến
if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
9 vẫn có sẵn để sử dụng và "Mozilla" được chuyển đến
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
2.

Đây là một ví dụ thú vị hơn một chút, một hàm

function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

const size12 = makeSizer(12);
const size14 = makeSizer(14);
const size16 = makeSizer(16);
2:

function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12

Trong ví dụ này, chúng tôi đã xác định một hàm

function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

const size12 = makeSizer(12);
const size14 = makeSizer(14);
const size16 = makeSizer(16);
3, có một đối số duy nhất
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
3 và trả về một hàm mới. Hàm mà nó trả về có một đối số duy nhất
function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

const size12 = makeSizer(12);
const size14 = makeSizer(14);
const size16 = makeSizer(16);
5 và trả về tổng của
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
3 và
function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

const size12 = makeSizer(12);
const size14 = makeSizer(14);
const size16 = makeSizer(16);
5.

Về bản chất,

function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

const size12 = makeSizer(12);
const size14 = makeSizer(14);
const size16 = makeSizer(16);
2 là một nhà máy chức năng. Nó tạo ra các chức năng có thể thêm một giá trị cụ thể vào đối số của chúng. Trong ví dụ trên, nhà máy chức năng tạo ra hai chức năng mới, một chức năng thêm năm cho đối số của nó và một chức năng thêm 10.

function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

const size12 = makeSizer(12);
const size14 = makeSizer(14);
const size16 = makeSizer(16);
9 và
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
0 đều đóng cửa. Họ chia sẻ cùng một định nghĩa cơ thể chức năng, nhưng lưu trữ các môi trường từ vựng khác nhau. Trong môi trường từ vựng của ____ 69,
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
3 là 5, trong khi trong môi trường từ vựng cho
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
0,
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
3 là 10.

Đóng cửa thực tế

Đóng cửa rất hữu ích vì chúng cho phép bạn liên kết dữ liệu (môi trường từ vựng) với chức năng hoạt động trên dữ liệu đó. Điều này có sự tương đồng rõ ràng với lập trình hướng đối tượng, trong đó các đối tượng cho phép bạn liên kết dữ liệu (thuộc tính của đối tượng) với một hoặc nhiều phương thức.

Do đó, bạn có thể sử dụng một đóng cửa ở bất cứ đâu mà bạn thường có thể sử dụng một đối tượng chỉ với một phương thức duy nhất.

Các tình huống mà bạn có thể muốn làm điều này là đặc biệt phổ biến trên web. Phần lớn mã được viết ở JavaScript phía trước là dựa trên sự kiện. Bạn xác định một số hành vi, và sau đó đính kèm nó vào một sự kiện được người dùng kích hoạt (chẳng hạn như nhấp chuột hoặc phím keypress). Mã được đính kèm dưới dạng gọi lại (một hàm duy nhất được thực thi để đáp ứng với sự kiện).

Chẳng hạn, giả sử chúng tôi muốn thêm các nút vào một trang để điều chỉnh kích thước văn bản. Một cách để làm điều này là chỉ định kích thước phông chữ của phần tử

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
5 (tính bằng pixel), sau đó đặt kích thước của các phần tử khác trên trang (như tiêu đề) bằng cách sử dụng đơn vị tương đối
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
6:

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h2 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}

Các nút kích thước văn bản tương tác như vậy có thể thay đổi thuộc tính

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
7 của phần tử
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
5 và các điều chỉnh được chọn bởi các phần tử khác trên trang nhờ các đơn vị tương đối.

Đây là JavaScript:

function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

const size12 = makeSizer(12);
const size14 = makeSizer(14);
const size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
9,
<a href="#" id="size-12">12a>
<a href="#" id="size-14">14a>
<a href="#" id="size-16">16a>
0 và
<a href="#" id="size-12">12a>
<a href="#" id="size-14">14a>
<a href="#" id="size-16">16a>
1 hiện là các chức năng thay đổi kích thước văn bản cơ thể thành 12, 14 và 16 pixel, tương ứng. Bạn có thể đính kèm chúng vào các nút (trong trường hợp này là các siêu liên kết) như được trình bày trong ví dụ mã sau.

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

<a href="#" id="size-12">12a>
<a href="#" id="size-14">14a>
<a href="#" id="size-16">16a>

Chạy mã bằng JSFiddle.

Mô phỏng các phương pháp riêng với việc đóng cửa

Các ngôn ngữ như Java cho phép bạn khai báo các phương thức là riêng tư, có nghĩa là chúng chỉ có thể được gọi bằng các phương pháp khác trong cùng một lớp.

JavaScript, trước các lớp học, không có cách khai báo phương pháp riêng tư, nhưng có thể mô phỏng các phương thức riêng bằng cách sử dụng đóng cửa. Các phương thức riêng tư không chỉ hữu ích để hạn chế quyền truy cập vào mã. Họ cũng cung cấp một cách mạnh mẽ để quản lý không gian tên toàn cầu của bạn.

Mã sau đây minh họa cách sử dụng đóng cửa để xác định các chức năng công cộng có thể truy cập các chức năng và biến riêng tư. Lưu ý rằng những đóng cửa này theo mô hình thiết kế mô -đun.

const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.

Trong các ví dụ trước, mỗi lần đóng cửa đều có môi trường từ vựng riêng. Tuy nhiên, ở đây, có một môi trường từ vựng duy nhất được chia sẻ bởi ba chức năng:

<a href="#" id="size-12">12a>
<a href="#" id="size-14">14a>
<a href="#" id="size-16">16a>
2,
<a href="#" id="size-12">12a>
<a href="#" id="size-14">14a>
<a href="#" id="size-16">16a>
3 và
<a href="#" id="size-12">12a>
<a href="#" id="size-14">14a>
<a href="#" id="size-16">16a>
4.

Môi trường từ vựng được chia sẻ được tạo ra trong cơ thể của một hàm ẩn danh, được thực hiện ngay khi nó được xác định (còn được gọi là IIFE). Môi trường từ vựng chứa hai mục riêng tư: một biến gọi là

<a href="#" id="size-12">12a>
<a href="#" id="size-14">14a>
<a href="#" id="size-16">16a>
5 và một hàm gọi là
<a href="#" id="size-12">12a>
<a href="#" id="size-14">14a>
<a href="#" id="size-16">16a>
6. Bạn không thể truy cập vào một trong hai thành viên riêng này từ bên ngoài chức năng ẩn danh. Thay vào đó, bạn có thể truy cập chúng bằng ba chức năng công cộng được trả về từ trình bao bọc ẩn danh.

Ba chức năng công cộng đó là những đóng cửa có chung môi trường từ vựng. Nhờ phạm vi từ vựng của JavaScript, mỗi người đều có quyền truy cập vào biến

<a href="#" id="size-12">12a>
<a href="#" id="size-14">14a>
<a href="#" id="size-16">16a>
5 và hàm
<a href="#" id="size-12">12a>
<a href="#" id="size-14">14a>
<a href="#" id="size-16">16a>
6.

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
0

Lưu ý cách hai quầy duy trì sự độc lập của họ với nhau. Mỗi lần đóng tham chiếu một phiên bản khác nhau của biến

<a href="#" id="size-12">12a>
<a href="#" id="size-14">14a>
<a href="#" id="size-16">16a>
5 thông qua việc đóng cửa riêng của nó. Mỗi lần một trong các bộ đếm được gọi, môi trường từ vựng của nó thay đổi bằng cách thay đổi giá trị của biến này. Các thay đổi đối với giá trị biến trong một lần đóng không ảnh hưởng đến giá trị trong phần đóng khác.

Lưu ý: Sử dụng đóng cửa theo cách này cung cấp các lợi ích thường được liên kết với lập trình hướng đối tượng. Cụ thể, ẩn dữ liệu và đóng gói. Using closures in this way provides benefits that are normally associated with object-oriented programming. In particular, data hiding and encapsulation.

Chuỗi phạm vi đóng cửa

Mỗi lần đóng cửa đều có ba phạm vi:

  • Phạm vi địa phương (phạm vi riêng)
  • Phạm vi kèm theo (có thể là khối, chức năng hoặc phạm vi mô -đun)
  • Phạm vi toàn cầu

Một sai lầm phổ biến là không nhận ra rằng trong trường hợp hàm bên ngoài tự nó là hàm lồng nhau, truy cập vào phạm vi của hàm bên ngoài bao gồm phạm vi kèm theo của hàm bên ngoài, tạo ra một chuỗi phạm vi chức năng. Để chứng minh, hãy xem xét mã ví dụ sau.

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
1

Bạn cũng có thể viết mà không có chức năng ẩn danh:

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
2

Trong ví dụ trên, có một loạt các chức năng lồng nhau, tất cả đều có quyền truy cập vào phạm vi của các hàm bên ngoài. Trong bối cảnh này, chúng ta có thể nói rằng việc đóng cửa có quyền truy cập vào tất cả các phạm vi chức năng bên ngoài.

Đóng cửa có thể nắm bắt các biến trong phạm vi khối và phạm vi mô -đun là tốt. Ví dụ: sau đây tạo ra một đóng cửa trong biến có khối

function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

const size12 = makeSizer(12);
const size14 = makeSizer(14);
const size16 = makeSizer(16);
5:

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
3

Đóng cửa trên các mô -đun có thể thú vị hơn.

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
4

Ở đây, mô-đun xuất một cặp chức năng thiết lập getter, đóng trên biến số lượng mô-đun

function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
3. Ngay cả khi
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
3 không thể truy cập trực tiếp từ các mô -đun khác, nó có thể được đọc và viết với các chức năng.

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
5

Việc đóng cửa cũng có thể đóng các giá trị đã nhập quá, được coi là ràng buộc trực tiếp, bởi vì khi giá trị ban đầu thay đổi, một thay đổi được nhập khẩu tương ứng.

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
6

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
7

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
8

Tạo đóng cửa theo vòng lặp: Một sai lầm phổ biến

Trước khi giới thiệu từ khóa

function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
6, một vấn đề phổ biến với việc đóng cửa đã xảy ra khi bạn tạo chúng bên trong một vòng lặp. Để chứng minh, hãy xem xét mã ví dụ sau.

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
9

if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
0

Hãy thử chạy mã trong jsfiddle.

Mảng

const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
4 xác định ba gợi ý hữu ích, mỗi gợi ý với ID của trường đầu vào trong tài liệu. Vòng lặp chu kỳ thông qua các định nghĩa này, kết nối một sự kiện
const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
5 với mỗi sự kiện hiển thị phương pháp trợ giúp liên quan.

Nếu bạn thử mã này, bạn sẽ thấy rằng nó không hoạt động như mong đợi. Bất kể bạn tập trung vào lĩnh vực nào, thông điệp về tuổi của bạn sẽ được hiển thị.

Lý do cho điều này là các chức năng được gán cho

const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
5 là đóng cửa; Chúng bao gồm định nghĩa hàm và môi trường bị bắt từ phạm vi của hàm
const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
7. Ba lần đóng đã được tạo ra bởi vòng lặp, nhưng mỗi người có cùng môi trường từ vựng duy nhất, có một biến với các giá trị thay đổi (
const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
8). Điều này là do biến
const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
8 được khai báo với
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
1 và do đó có phạm vi chức năng do nâng. Giá trị của
if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
01 được xác định khi các cuộc gọi lại
const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
5 được thực thi. Bởi vì vòng lặp đã chạy khóa học của mình vào thời điểm đó, đối tượng biến
const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
8 (được chia sẻ bởi cả ba lần đóng) đã được chỉ ra vào mục cuối cùng trong danh sách
const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
4.

Một giải pháp trong trường hợp này là sử dụng nhiều đóng hơn: đặc biệt, sử dụng một nhà máy chức năng như mô tả trước đó:

if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
1

Chạy mã bằng liên kết jsfiddle này.

Điều này hoạt động như mong đợi. Thay vì các cuộc gọi lại, tất cả đều chia sẻ một môi trường từ vựng duy nhất, hàm

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
05 tạo ra một môi trường từ vựng mới cho mỗi cuộc gọi lại, trong đó
if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
06 đề cập đến chuỗi tương ứng từ mảng
const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
4.

Một cách khác để viết ở trên bằng cách sử dụng đóng ẩn danh là:

if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
2

Nếu bạn không muốn sử dụng nhiều đóng hơn, bạn có thể sử dụng từ khóa

function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
6 hoặc
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
7:

if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
3

Ví dụ này sử dụng

function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
7 thay vì
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
1, do đó, mỗi lần đóng đều liên kết với biến có khối khối, có nghĩa là không cần đóng bổ sung.

Một cách khác có thể là sử dụng

if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
12 để lặp lại mảng
const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
4 và gắn người nghe vào mỗi
if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
14, như được hiển thị:

if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
4

Cân nhắc hiệu suất

Như đã đề cập trước đây, mỗi trường hợp chức năng quản lý phạm vi và đóng cửa riêng của nó. Do đó, việc tạo các chức năng không cần thiết trong các chức năng khác là không cần thiết cho một nhiệm vụ cụ thể, vì nó sẽ ảnh hưởng tiêu cực đến hiệu suất tập lệnh cả về tốc độ xử lý và mức tiêu thụ bộ nhớ.

Chẳng hạn, khi tạo một đối tượng/lớp mới, các phương thức thường được liên kết với nguyên mẫu của đối tượng thay vì được xác định thành hàm tạo đối tượng. Lý do là bất cứ khi nào hàm tạo được gọi, các phương thức sẽ được chỉ định lại (nghĩa là, cho mọi tạo đối tượng).

Xem xét trường hợp sau:

if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
5

Bởi vì mã trước không tận dụng lợi ích của việc sử dụng đóng cửa trong trường hợp cụ thể này, chúng tôi có thể viết lại nó để tránh sử dụng đóng cửa như sau:

if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
6

Tuy nhiên, xác định lại nguyên mẫu không được khuyến khích. Thay vào đó, ví dụ sau đây nối vào nguyên mẫu hiện có:

if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); // ReferenceError: x is not defined
7

Trong hai ví dụ trước, nguyên mẫu được kế thừa có thể được chia sẻ bởi tất cả các đối tượng và các định nghĩa phương thức không cần phải xảy ra ở mọi tạo đối tượng. Xem kế thừa và chuỗi nguyên mẫu để biết thêm.

Sự khác biệt giữa đóng cửa và chức năng là gì?

Sự khác biệt giữa chức năng và chức năng đóng được khai báo bằng cách sử dụng từ khóa func trong khi đóng không có từ khóa func. Chức năng luôn luôn tên nhưng đóng cửa không có. Chức năng không có trong từ khóa nhưng đóng có trong từ khóa.Function is declared using func keyword whereas Closure doesn't have func keyword. Function has always name but Closure doesn't have. Function doesn't have in keyword but closure has in the keyword.

Chức năng lồng nhau trong JavaScript là gì?

Vì một hàm lồng nhau là một đóng cửa, điều này có nghĩa là một hàm lồng nhau có thể "kế thừa" các đối số và biến của hàm chứa của nó.Nói cách khác, hàm bên trong chứa phạm vi của hàm bên ngoài.Để tóm tắt: Hàm bên trong chỉ có thể được truy cập từ các câu lệnh trong hàm bên ngoài.a nested function can "inherit" the arguments and variables of its containing function. In other words, the inner function contains the scope of the outer function. To summarize: The inner function can be accessed only from statements in the outer function.

Sự khác biệt giữa đóng cửa và cà ri trong JavaScript là gì?

Công việc của đóng cửa là đảm bảo rằng biến đó được bảo tồn khi Lambda thoát khỏi bối cảnh đó.Hàm Curried là một hàm có thể lấy các đối số của nó theo nhiều bước hoặc nhiều ứng dụng chức năng khác nhau.Điều này thường có nghĩa là có Lambdas được lồng trong Lambdas.

Sự khác biệt giữa đóng cửa và gọi lại trong JavaScript là gì?

Một cuộc gọi lại là mã thực thi được truyền dưới dạng đối số cho mã khác. Đóng là một hàm được đánh giá trong một môi trường chứa một hoặc nhiều biến bị ràng buộc.Khi được gọi, hàm có thể truy cập các biến này. a closure is a function that is evaluated in an environment containing one or more bound variables. When called, the function can access these variables.