Thắc mắc về Closure javascript

:wink: ,

function makeExponentiation(x) {
    var exponent = x;
    return function(y) {
        return Math.pow(y, exponent);
    }
}
var sqr = makeExponentiation(2);
var sqrt = makeExponentiation(0.5);
console.log('3 bình phương là ' + sqr(3));
console.log('căn bậc hai của 9 là ' + sqrt(9));

Chỗ truyền 3 và 9 vào hàm sqr(3), sqrt(9). Làm sao makeExponentiation biết số 3 và 9 phải truyền vào parameter y vậy ạ

:handshake:

Chắc viết gọn quá nên bạn không hiểu. Nếu viết lại như vầy thì có dễ hiểu hơn không bạn?

function makeExponentiation(x) {
    var exponent = x;

    var f = function(y) {
        return Math.pow(y, exponent);
    };

    return f;
}

var sqr = makeExponentiation(2);
var sqrt = makeExponentiation(0.5);

console.log('3 bình phương là ' + sqr(3));
console.log('căn bậc hai của 9 là ' + sqrt(9));
9 Likes

Chỗ sqrt(3) là truyền vào y rồi bạn. Chỗ make… Mới là truyền x

5 Likes

Em thấy biến exponent trong function makeExponentiation giống biến static trong OOP, makeExponentiation giống như một blueprint tạo ra các function f khác nhau.

Biến static trong OOP sẽ chỉ có 1 giá trị duy nhất trong toàn bộ chương trình, còn biến exponent sẽ có giá trị khác nhau với mỗi lần gọi hàm makeExponentiation khác nhau. Trong trường hợp này, nó chỉ giống data member bình thường thôi.

10 Likes

Vậy thì function trong javascript viết theo kiểu clousure thì function đó sẽ giống như class trong OOP đúng không anh?

Không đâu cậu.
Closure là concept liên quan tới functional programming, nên nó không phải cách viết class của JavaScript, hay tương đồng với class trong OOP.

6 Likes

Có thể cho em xin trường hợp thực tế cần sử dụng clousure được không ạ, em đã hiểu rồi nhưng code JS một thời gian em vẫn chưa biết cách vận dụng vào thực tế!

ví dụ của bạn cũng gần tương tự với thực tế rồi

trong thực tế, sẽ có lúc cần viết nhiều hàm có xử lý gần giống nhau, dùng chung logic, chỉ khác nhau tham số mặc định thôi
hoặc có lúc, cái người ta cần truyền vào là 1 function với một tham số gì đó không biết trước

image
image

ví dụ như có 50 role, không lẽ lại tạo ra 50 cái CanMatchFn cụ thể hay sao (giống như HasRoleUserView)
trong khi 50 cái đó lại xử lý giống y như nhau, chỉ khác cái role cần check
thay vì tạo sẵn 50 cái function (và có thể thêm nữa) thì ngừoi ta chỉ viết 1 lần

5 Likes

Bạn nên hiểu là khi bạn code javascript thì bạn đã dùng Closure rồi, bạn định nghĩa 1 hàm thì tức là bạn đã gián tiếp tạo 1 Closure (thực tế thì Javascript Engine tạo Closure Scope mỗi khi một function được định nghĩa). Không phải chỉ khi viết inner function thì mới có Closure.

Định nghĩa: A closure is the combination of a function and its lexical scope. In JavaScript, closures are created every time a function is created, at function creation time.

Vận dụng vào thực tế ở đây chắc nên hiểu là, dùng hiểu biết của bản thân về Closure để tạo ra pattern hoặc chức năng gì đó, mà vì có Closure mới giải quyết được, hoặc nhờ có Closure mà việc đó dễ dàng hơn. Vậy nên hiểu Closure sinh ra để giải quyết vấn đề gì, tại sao Javascript Engine lại tạo Closure mỗi khi create function.


Chi tiết dài dòng:
Định nghĩa function ở ECMAScript: function là first-class. First-class function nghĩa là 1 function có thể được lưu trữ trong 1 biến, có thể truyền vào hàm khác (arguments), hoặc return trong 1 hàm khác.

Problem xảy ra khi truyền function qua lại (concept Funarg problem).

VD: downwards funarg problem

let x = 10;
 
function foo() {
  console.log(x);
}
 
function bar(funArg) {
  let x = 20;
  funArg(); 
}

//pass foo to bar 
bar(foo);

Ở trên truyền foo vào function bar, biến x ở trong foo là free variable (free variable là biến ko phải local và ko phải biến argument, biến chưa định nghĩa). Problem là không biết nên binding value ở đâu vô cho free variable, nên lấy biến x = 10 hay 20 => sinh ra Closure. Người ta ko bind với biến ở hàm bar, vì biến đấy có thể bị sửa sau này bởi hàm call, khó debug, khó maintain. Closure lấy biến ở environment tạo ra nó, environment tạo ra nó lại chứa định nghĩa của hàm, nó tạo thành 1 vòng tròn (Closure tiếng anh nghĩa là 1 vòng khép kín). Nhờ có closure, 1 hàm có khả năng ghi nhớ outer function’s scope.

VD2: upwards funarg problem

function foo() {
  let x = 10;
   
  // Closure, capturing environment of `foo`.
  function bar() {
    return x;
  }

  return bar;
}
let x = 20;
let bar = foo();
bar(); // 10

Vận dụng Closure vào các pattern (để ý mấy cái use case nào cần pass function qua lại và cần dùng inner function để capture biến/hàm ở outer thì thường là cách sử dụng Closure):

  • 1 cái phổ biến là callback -> là truyền function vào một hàm khác
  • tạo function có private scope -> vd tạo function A có inner function là B và C, thì B với C có thể access biến và hàm ở A, nhưng ngoài global thì không access được -> có thể tạo oop class bằng function & closure
  • Function Factory: inner function sử dụng nhiều config khác nhau từ outer
  • Counter:
function A() {
  let state = 1;
  return function B() {
    state++;
    console.log(state);
  }
}
//2, 3, 4
  • share data giữa nhiều inner function

Hope it help!

2 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?