diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/solution.js b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/solution.js
index d5a09efb3..29de3eb4f 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/solution.js
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/solution.js
@@ -1,7 +1,7 @@
function spy(func) {
function wrapper(...args) {
- // using ...args instead of arguments to store "real" array in wrapper.calls
+ // sử dụng ... args thay vì đối số để lưu trữ array "thực" trong wrapper.calls
wrapper.calls.push(args);
return func.apply(this, args);
}
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/source.js b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/source.js
index 38da0105f..e9d9d7895 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/source.js
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/source.js
@@ -1,5 +1,5 @@
function spy(func) {
- // your code
+ // mã của bạn
}
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/solution.md b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/solution.md
index 0c8a211b4..a3d83456e 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/solution.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/solution.md
@@ -1 +1 @@
-The wrapper returned by `spy(f)` should store all arguments and then use `f.apply` to forward the call.
+Wrapper được trả về bởi `spy(f)` nên lưu trữ tất cả các đối số và sau đó sử dụng `f.apply` để chuyển tiếp cuộc gọi.
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/task.md
index a3843107c..d997657ca 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/task.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/task.md
@@ -2,17 +2,17 @@ importance: 5
---
-# Spy decorator
+# Decorator gián điệp
-Create a decorator `spy(func)` that should return a wrapper that saves all calls to function in its `calls` property.
+Tạo một decorator `spy(func)` sẽ trả về một wrapper lưu tất cả các lệnh gọi để hoạt động trong thuộc tính `calls` của nó.
-Every call is saved as an array of arguments.
+Mỗi cuộc gọi được lưu dưới dạng một array đối số.
-For instance:
+Ví dụ:
```js
function work(a, b) {
- alert( a + b ); // work is an arbitrary function or method
+ alert( a + b ); // công việc là một hàm hoặc phương thức tùy ý
}
*!*
@@ -27,4 +27,4 @@ for (let args of work.calls) {
}
```
-P.S. That decorator is sometimes useful for unit-testing. Its advanced form is `sinon.spy` in [Sinon.JS](http://sinonjs.org/) library.
+Tái bút: Decorator đó đôi khi hữu ích cho thử nghiệm đơn vị. Dạng nâng cao của nó là `sinon.spy` trong thư viện [Sinon.JS](http://sinonjs.org/).
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/solution.md b/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/solution.md
index 24bb4d448..3d6b0b809 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/solution.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/solution.md
@@ -1,4 +1,4 @@
-The solution:
+Lời giải:
```js run demo
function delay(f, ms) {
@@ -11,22 +11,22 @@ function delay(f, ms) {
let f1000 = delay(alert, 1000);
-f1000("test"); // shows "test" after 1000ms
+f1000("kiểm tra"); // hiển thị "kiểm tra" sau 1000ms
```
-Please note how an arrow function is used here. As we know, arrow functions do not have own `this` and `arguments`, so `f.apply(this, arguments)` takes `this` and `arguments` from the wrapper.
+Hãy lưu ý cách sử dụng arrow function ở đây. Như chúng ta đã biết, các arrow function không có `this` và `arguments` riêng, vì vậy `f.apply(this, arguments)` lấy `this` và `arguments` từ wrapper.
-If we pass a regular function, `setTimeout` would call it without arguments and `this=window` (assuming we're in the browser).
+Nếu chúng ta chuyển một hàm thông thường, `setTimeout` sẽ gọi nó mà không có đối số và `this=window` (giả sử chúng ta đang ở trong trình duyệt).
-We still can pass the right `this` by using an intermediate variable, but that's a little bit more cumbersome:
+Chúng ta vẫn có thể chuyển `this` bên phải bằng cách sử dụng một biến trung gian, nhưng điều đó hơi rườm rà hơn một chút:
```js
function delay(f, ms) {
return function(...args) {
- let savedThis = this; // store this into an intermediate variable
+ let savedThis = this; // lưu cái này vào một biến trung gian
setTimeout(function() {
- f.apply(savedThis, args); // use it here
+ f.apply(savedThis, args); // sử dụng nó ở đây
}, ms);
};
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/task.md
index c04c68d7e..5f1fd5528 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/task.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/task.md
@@ -2,25 +2,25 @@ importance: 5
---
-# Delaying decorator
+# Decorator trì hoãn
-Create a decorator `delay(f, ms)` that delays each call of `f` by `ms` milliseconds.
+Tạo một decorator `delay(f, ms)` để trì hoãn mỗi lần gọi `f` bằng `ms` mili giây.
-For instance:
+Ví dụ:
```js
function f(x) {
alert(x);
}
-// create wrappers
+// tạo các wrapper
let f1000 = delay(f, 1000);
let f1500 = delay(f, 1500);
-f1000("test"); // shows "test" after 1000ms
-f1500("test"); // shows "test" after 1500ms
+f1000("kiểm tra"); // hiển thị "kiểm tra" sau 1000ms
+f1500("kiểm tra"); // hiển thị "kiểm tra" sau 1500ms
```
-In other words, `delay(f, ms)` returns a "delayed by `ms`" variant of `f`.
+Nói cách khác, `delay(f, ms)` trả về một biến thể "trễ bởi `ms`" của `f`.
-In the code above, `f` is a function of a single argument, but your solution should pass all arguments and the context `this`.
+Trong đoạn mã trên, `f` là một hàm của một đối số duy nhất, nhưng giải pháp của bạn phải chuyển tất cả các đối số và ngữ cảnh `this`.
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/debounce.svg b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/debounce.svg
index bd82f0245..41631bd48 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/debounce.svg
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/debounce.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/debounce.view/index.html b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/debounce.view/index.html
index e3b4d5842..8d741ca35 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/debounce.view/index.html
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/debounce.view/index.html
@@ -1,18 +1,18 @@
-Function handler
is called on this input:
+Hàm trình xử lý
được gọi trên đầu vào này:
-
+
-Debounced function debounce(handler, 1000)
is called on this input:
+Hàm gỡ lỗi debounce(handler, 1000)
được gọi trên đầu vào này:
-
+
-
+
\ No newline at end of file
+
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md
index 83e75f315..d70c12a36 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md
@@ -9,5 +9,5 @@ function debounce(func, ms) {
```
-A call to `debounce` returns a wrapper. When called, it schedules the original function call after given `ms` and cancels the previous such timeout.
+Lệnh gọi `debounce` trả về một wrapper. Khi được gọi, nó lên lịch cuộc gọi hàm ban đầu sau `ms` đã cho và hủy bỏ thời gian chờ như vậy trước đó.
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md
index 5b0fcc5f8..49aa0cfc9 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md
@@ -2,21 +2,21 @@ importance: 5
---
-# Debounce decorator
+# Decorator gỡ lỗi
-The result of `debounce(f, ms)` decorator is a wrapper that suspends calls to `f` until there's `ms` milliseconds of inactivity (no calls, "cooldown period"), then invokes `f` once with the latest arguments.
+Kết quả của decorator `debounce(f, ms)` là một wrapper tạm dừng các lệnh gọi đến `f` cho đến khi có `ms` mili giây không hoạt động (không có lệnh gọi, "thời gian hồi"), sau đó gọi `f` một lần với các đối số mới nhất .
-In other words, `debounce` is like a secretary that accepts "phone calls", and waits until there's `ms` milliseconds of being quiet. And only then it transfers the latest call information to "the boss" (calls the actual `f`).
+Nói cách khác, `debounce` giống như một thư ký chấp nhận "các cuộc điện thoại" và đợi cho đến khi có `ms` mili giây để im lặng. Và chỉ khi đó nó mới chuyển thông tin cuộc gọi mới nhất cho "ông chủ" (gọi `f` thực tế).
-For instance, we had a function `f` and replaced it with `f = debounce(f, 1000)`.
+Chẳng hạn, chúng ta có một hàm `f` và thay thế nó bằng `f = debounce(f, 1000)`.
-Then if the wrapped function is called at 0ms, 200ms and 500ms, and then there are no calls, then the actual `f` will be only called once, at 1500ms. That is: after the cooldown period of 1000ms from the last call.
+Sau đó, nếu hàm được gói được gọi ở 0ms, 200ms và 500ms, sau đó không có lệnh gọi nào, thì `f` thực tế sẽ chỉ được gọi một lần, ở 1500ms. Đó là: sau khoảng thời gian hồi 1000ms kể từ lần gọi cuối cùng.
![](debounce.svg)
-...And it will get the arguments of the very last call, other calls are ignored.
+...Và nó sẽ nhận được các đối số của cuộc gọi cuối cùng, các cuộc gọi khác sẽ bị bỏ qua.
-Here's the code for it (uses the debounce decorator from the [Lodash library](https://lodash.com/docs/4.17.15#debounce)):
+Đây là mã cho nó (sử dụng decorator gỡ lỗi từ [thư viện Lodash](https://lodash.com/docs/4.17.15#debounce)):
```js
let f = _.debounce(alert, 1000);
@@ -24,28 +24,28 @@ let f = _.debounce(alert, 1000);
f("a");
setTimeout( () => f("b"), 200);
setTimeout( () => f("c"), 500);
-// debounced function waits 1000ms after the last call and then runs: alert("c")
+// hàm gỡ lỗi đợi 1000 mili giây sau cuộc gọi cuối cùng rồi chạy: alert("c")
```
-Now a practical example. Let's say, the user types something, and we'd like to send a request to the server when the input is finished.
+Bây giờ là một ví dụ thực tế. Giả sử, người dùng nhập nội dung nào đó và chúng ta muốn gửi yêu cầu đến máy chủ khi quá trình nhập kết thúc.
-There's no point in sending the request for every character typed. Instead we'd like to wait, and then process the whole result.
+Không có lý do nào để gửi yêu cầu cho mọi ký tự được nhập. Thay vào đó, chúng ta muốn đợi và sau đó xử lý toàn bộ kết quả.
-In a web-browser, we can setup an event handler -- a function that's called on every change of an input field. Normally, an event handler is called very often, for every typed key. But if we `debounce` it by 1000ms, then it will be only called once, after 1000ms after the last input.
+Trong trình duyệt web, chúng ta có thể thiết lập trình xử lý sự kiện -- một hàm được gọi trên mọi thay đổi của vùng nhập vào. Thông thường, một trình xử lý sự kiện được gọi rất thường xuyên cho mỗi phím được nhập. Nhưng nếu chúng ta `debounce` nó sau 1000 mili giây, thì nó sẽ chỉ được gọi một lần, sau 1000 mili giây sau lần nhập cuối cùng.
```online
-In this live example, the handler puts the result into a box below, try it:
+Trong ví dụ trực tiếp này, trình xử lý đặt kết quả vào hộp bên dưới, hãy thử:
[iframe border=1 src="debounce" height=200]
-See? The second input calls the debounced function, so its content is processed after 1000ms from the last input.
+Nhìn thấy? Đầu vào thứ hai gọi hàm gỡ lỗi, vì vậy nội dung của nó được xử lý sau 1000 mili giây kể từ đầu vào cuối cùng.
```
-So, `debounce` is a great way to process a sequence of events: be it a sequence of key presses, mouse movements or something else.
+Vì vậy, `debounce` là một cách tuyệt vời để xử lý một chuỗi sự kiện: có thể là chuỗi các lần nhấn phím, di chuyển chuột hoặc thứ gì đó khác.
-It waits the given time after the last call, and then runs its function, that can process the result.
+Nó đợi thời gian nhất định sau cuộc gọi cuối cùng, rồi chạy hàm của nó, có thể xử lý kết quả.
-The task is to implement `debounce` decorator.
+Nhiệm vụ là triển khai decorator `debounce`.
-Hint: that's just a few lines if you think about it :)
+Gợi ý: đó chỉ là một vài dòng nếu bạn nghĩ về nó :)
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/solution.js b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/solution.js
index 8071be9d4..79a6c6503 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/solution.js
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/solution.js
@@ -7,23 +7,23 @@ function throttle(func, ms) {
function wrapper() {
if (isThrottled) {
- // memo last arguments to call after the cooldown
+ // ghi nhớ đối số cuối cùng để gọi sau thời gian hồi
savedArgs = arguments;
savedThis = this;
return;
}
- // otherwise go to cooldown state
+ // nếu không thì chuyển sang trạng thái hồi
func.apply(this, arguments);
isThrottled = true;
- // plan to reset isThrottled after the delay
+ // kế hoạch thiết lập lại isThrottled sau sự chậm trễ
setTimeout(function() {
isThrottled = false;
if (savedArgs) {
- // if there were calls, savedThis/savedArgs have the last one
- // recursive call runs the function and sets cooldown again
+ // nếu có cuộc gọi, thì saveThis/savedArgs có cuộc gọi cuối cùng
+ //cuộc gọi đệ quy chạy hàm và đặt lại thời gian hồi
wrapper.apply(savedThis, savedArgs);
savedArgs = savedThis = null;
}
@@ -31,4 +31,4 @@ function throttle(func, ms) {
}
return wrapper;
-}
\ No newline at end of file
+}
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js
index e671438f6..690638e1f 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js
@@ -12,32 +12,32 @@ describe("throttle(f, 1000)", function() {
});
it("the first call runs now", function() {
- f1000(1); // runs now
+ f1000(1); // chạy ngay bây giờ
assert.equal(log, "1");
});
it("then calls are ignored till 1000ms when the last call works", function() {
- f1000(2); // (throttling - less than 1000ms since the last run)
- f1000(3); // (throttling - less than 1000ms since the last run)
- // after 1000 ms f(3) call is scheduled
+ f1000(2); // (điều tiết - ít hơn 1000 ms kể từ lần chạy cuối cùng)
+ f1000(3); // (điều tiết - ít hơn 1000 ms kể từ lần chạy cuối cùng)
+ // sau 1000 ms cuộc gọi f(3) được lên lịch
- assert.equal(log, "1"); // right now only the 1st call done
+ assert.equal(log, "1"); // ngay bây giờ chỉ có cuộc gọi đầu tiên được thực hiện
- this.clock.tick(1000); // after 1000ms...
- assert.equal(log, "13"); // log==13, the call to f1000(3) is made
+ this.clock.tick(1000); // sau 1000ms...
+ assert.equal(log, "13"); // log==13, cuộc gọi đến f1000(3) được thực hiện
});
it("the third call waits 1000ms after the second call", function() {
this.clock.tick(100);
- f1000(4); // (throttling - less than 1000ms since the last run)
+ f1000(4); // (điều tiết - ít hơn 1000 ms kể từ lần chạy cuối cùng)
this.clock.tick(100);
- f1000(5); // (throttling - less than 1000ms since the last run)
+ f1000(5); // (điều tiết - ít hơn 1000 ms kể từ lần chạy cuối cùng)
this.clock.tick(700);
- f1000(6); // (throttling - less than 1000ms since the last run)
+ f1000(6); // (điều tiết - ít hơn 1000 ms kể từ lần chạy cuối cùng)
- this.clock.tick(100); // now 100 + 100 + 700 + 100 = 1000ms passed
+ this.clock.tick(100); // bây giờ 100 + 100 + 700 + 100 = 1000 ms đã trôi qua
- assert.equal(log, "136"); // the last call was f(6)
+ assert.equal(log, "136"); // cuộc gọi cuối cùng là f(6)
});
after(function() {
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md
index 6950664be..7843f1137 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md
@@ -29,10 +29,10 @@ function throttle(func, ms) {
}
```
-A call to `throttle(func, ms)` returns `wrapper`.
+Lệnh gọi `throttle(func, ms)` trả về `wrapper`.
-1. During the first call, the `wrapper` just runs `func` and sets the cooldown state (`isThrottled = true`).
-2. In this state all calls are memorized in `savedArgs/savedThis`. Please note that both the context and the arguments are equally important and should be memorized. We need them simultaneously to reproduce the call.
-3. After `ms` milliseconds pass, `setTimeout` triggers. The cooldown state is removed (`isThrottled = false`) and, if we had ignored calls, `wrapper` is executed with the last memorized arguments and context.
+1. Trong lần gọi đầu tiên, `wrapper` chỉ chạy `func` và đặt trạng thái hồi (`isThrottled = true`).
+2. Ở trạng thái này, tất cả các lệnh gọi đều được ghi nhớ trong `savedArgs/savedThis`. Hãy lưu ý rằng cả ngữ cảnh và đối số đều quan trọng như nhau và cần được ghi nhớ. Chúng ta cần chúng đồng thời để tái tạo cuộc gọi.
+3. Sau khi `ms` mili giây trôi qua, `setTimeout` sẽ kích hoạt. Trạng thái hồi bị loại bỏ (`isThrottled = false`) và nếu chúng ta đã bỏ qua các lệnh gọi, `wrapper` sẽ được thực thi với các đối số và ngữ cảnh được ghi nhớ cuối cùng.
-The 3rd step runs not `func`, but `wrapper`, because we not only need to execute `func`, but once again enter the cooldown state and setup the timeout to reset it.
+Bước thứ 3 chạy không phải `func`, mà là `wrapper`, bởi vì chúng ta không chỉ cần thực thi `func`, mà một lần nữa vào trạng thái hồi và thiết lập thời gian chờ để đặt lại nó.
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md
index 6df7af132..c07df7f97 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md
@@ -2,52 +2,52 @@ importance: 5
---
-# Throttle decorator
+# Decorator điều tiết
-Create a "throttling" decorator `throttle(f, ms)` -- that returns a wrapper.
+Tạo một decorator "điều tiết" `throttle(f, ms)` -- trả về một wrapper.
-When it's called multiple times, it passes the call to `f` at maximum once per `ms` milliseconds.
+Khi được gọi nhiều lần, nó sẽ chuyển lệnh gọi tới `f` tối đa một lần trong mỗi mili giây `ms`.
-The difference with debounce is that it's completely different decorator:
-- `debounce` runs the function once after the "cooldown" period. Good for processing the final result.
-- `throttle` runs it not more often than given `ms` time. Good for regular updates that shouldn't be very often.
+Sự khác biệt với debounce là nó là decorator hoàn toàn khác:
+- `debounce` chạy hàm một lần sau khoảng thời gian "hồi". Tốt để xử lý kết quả cuối cùng.
+- `throttle` chạy nó không thường xuyên hơn thời gian `ms` đã cho. Tốt cho các cập nhật thường xuyên mà không nên không thường xuyên.
-In other words, `throttle` is like a secretary that accepts phone calls, but bothers the boss (calls the actual `f`) not more often than once per `ms` milliseconds.
+Nói cách khác, `throttle` giống như một thư ký chấp nhận các cuộc gọi điện thoại, nhưng làm phiền sếp (gọi thực tế là `f`) không quá một lần mỗi `ms` mili giây.
-Let's check the real-life application to better understand that requirement and to see where it comes from.
+Hãy kiểm tra ứng dụng thực tế để hiểu rõ hơn về yêu cầu đó và xem nó đến từ đâu.
-**For instance, we want to track mouse movements.**
+**Chẳng hạn, chúng ta muốn theo dõi chuyển động của chuột.**
-In a browser we can setup a function to run at every mouse movement and get the pointer location as it moves. During an active mouse usage, this function usually runs very frequently, can be something like 100 times per second (every 10 ms).
-**We'd like to update some information on the web-page when the pointer moves.**
+Trong trình duyệt, chúng ta có thể thiết lập một hàm để chạy ở mọi chuyển động của chuột và nhận vị trí con trỏ khi nó di chuyển. Trong quá trình sử dụng chuột hoạt động, hàm này thường chạy rất thường xuyên, có thể khoảng 100 lần mỗi giây (cứ sau 10 ms).
+**Chúng ta muốn cập nhật một số thông tin trên trang web khi con trỏ di chuyển.**
-...But updating function `update()` is too heavy to do it on every micro-movement. There is also no sense in updating more often than once per 100ms.
+...Nhưng cập nhật hàm `update()` quá nặng để thực hiện trên mọi chuyển động vi mô. Cũng không có ý nghĩa gì khi cập nhật thường xuyên hơn một lần trong 100 ms.
-So we'll wrap it into the decorator: use `throttle(update, 100)` as the function to run on each mouse move instead of the original `update()`. The decorator will be called often, but forward the call to `update()` at maximum once per 100ms.
+Vì vậy, chúng ta sẽ đưa nó vào decorator: sử dụng `throttle(update, 100)` làm hàm chạy trên mỗi lần di chuyển chuột thay vì `update()` ban đầu. Decorator sẽ được gọi thường xuyên, nhưng chuyển tiếp lệnh gọi tới `update()` tối đa một lần trong 100 ms.
-Visually, it will look like this:
+Cách trực quan, nó sẽ trông như thế này
-1. For the first mouse movement the decorated variant immediately passes the call to `update`. That's important, the user sees our reaction to their move immediately.
-2. Then as the mouse moves on, until `100ms` nothing happens. The decorated variant ignores calls.
-3. At the end of `100ms` -- one more `update` happens with the last coordinates.
-4. Then, finally, the mouse stops somewhere. The decorated variant waits until `100ms` expire and then runs `update` with last coordinates. So, quite important, the final mouse coordinates are processed.
+1. Đối với chuyển động chuột đầu tiên, biến thể được trang trí ngay lập tức chuyển lệnh gọi tới `update`. Điều đó quan trọng, người dùng sẽ thấy phản ứng của chúng ta đối với động thái của họ ngay lập tức.
+2. Sau đó, khi chuột di chuyển, cho đến `100ms` thì không có gì xảy ra. Biến thể được trang trí bỏ qua các cuộc gọi.
+3. Vào cuối `100ms` -- một `update` nữa xảy ra với tọa độ cuối cùng.
+4. Sau đó, cuối cùng, con chuột dừng lại ở đâu đó. Biến thể được trang trí đợi cho đến khi hết hạn `100ms` rồi chạy `update` với tọa độ cuối cùng. Vì vậy, khá quan trọng, tọa độ chuột cuối cùng được xử lý.
-A code example:
+Một ví dụ mã:
```js
function f(a) {
console.log(a);
}
-// f1000 passes calls to f at maximum once per 1000 ms
+// f1000 chuyển cuộc gọi tới f tối đa một lần trong 1000 ms
let f1000 = throttle(f, 1000);
-f1000(1); // shows 1
-f1000(2); // (throttling, 1000ms not out yet)
-f1000(3); // (throttling, 1000ms not out yet)
+f1000(1); // hiện 1
+f1000(2); // (điều tiết, 1000ms chưa hết)
+f1000(3); // (điều tiết, 1000ms chưa hết)
-// when 1000 ms time out...
-// ...outputs 3, intermediate value 2 was ignored
+// khi hết thời gian 1000 ms ...
+// ...đầu ra 3, giá trị trung gian 2 bị bỏ qua
```
-P.S. Arguments and the context `this` passed to `f1000` should be passed to the original `f`.
+Tái bút: Các đối số và ngữ cảnh `this` được truyền cho `f1000` phải được truyền cho `f` ban đầu.
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/article.md b/1-js/06-advanced-functions/09-call-apply-decorators/article.md
index acba0965c..1b79ac5b4 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/article.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/article.md
@@ -1,21 +1,21 @@
-# Decorators and forwarding, call/apply
+# Decorator và chuyển tiếp, gọi/áp dụng
-JavaScript gives exceptional flexibility when dealing with functions. They can be passed around, used as objects, and now we'll see how to *forward* calls between them and *decorate* them.
+JavaScript mang đến sự linh hoạt đặc biệt khi xử lý các hàm. Chúng có thể được truyền đi khắp nơi, được sử dụng làm đối tượng và bây giờ chúng ta sẽ xem cách *chuyển tiếp* lệnh gọi giữa chúng và *trang trí* chúng.
-## Transparent caching
+## Bộ nhớ đệm trong suốt
-Let's say we have a function `slow(x)` which is CPU-heavy, but its results are stable. In other words, for the same `x` it always returns the same result.
+Giả sử chúng ta có một hàm `slow(x)` nặng về CPU, nhưng kết quả của nó ổn định. Nói cách khác, với cùng một `x`, nó luôn trả về cùng một kết quả.
-If the function is called often, we may want to cache (remember) the results to avoid spending extra-time on recalculations.
+Nếu hàm được gọi thường xuyên, chúng ta có thể muốn lưu trữ (ghi nhớ) các kết quả để tránh tốn thêm thời gian cho việc tính toán lại.
-But instead of adding that functionality into `slow()` we'll create a wrapper function, that adds caching. As we'll see, there are many benefits of doing so.
+Nhưng thay vì thêm hàm đó vào `slow()`, chúng ta sẽ tạo một hàm wrapper, có thêm bộ nhớ đệm. Như chúng ta sẽ thấy, có rất nhiều lợi ích khi làm như vậy.
-Here's the code, and explanations follow:
+Đây là mã và giải thích như sau:
```js run
function slow(x) {
- // there can be a heavy CPU-intensive job here
- alert(`Called with ${x}`);
+ // có thể có một công việc sử dụng nhiều CPU ở đây
+ alert(`Gọi với ${x}`);
return x;
}
@@ -23,65 +23,65 @@ function cachingDecorator(func) {
let cache = new Map();
return function(x) {
- if (cache.has(x)) { // if there's such key in cache
- return cache.get(x); // read the result from it
+ if (cache.has(x)) { // nếu có khóa như vậy trong bộ đệm
+ return cache.get(x); // đọc kết quả từ nó
}
- let result = func(x); // otherwise call func
+ let result = func(x); // nếu không thì gọi func
- cache.set(x, result); // and cache (remember) the result
+ cache.set(x, result); // và cache (ghi nhớ) kết quả
return result;
};
}
slow = cachingDecorator(slow);
-alert( slow(1) ); // slow(1) is cached and the result returned
-alert( "Again: " + slow(1) ); // slow(1) result returned from cache
+alert( slow(1) ); // slow(1) được lưu trữ và kết quả trả về
+alert( "Lại: " + slow(1) ); // kết quả slow(1) được trả về từ bộ nhớ đệm
-alert( slow(2) ); // slow(2) is cached and the result returned
-alert( "Again: " + slow(2) ); // slow(2) result returned from cache
+alert( slow(2) ); // slow(2) được lưu trữ và kết quả trả về
+alert( "Lại: " + slow(2) ); // kết quả slow(2) được trả về từ bộ đệm
```
-In the code above `cachingDecorator` is a *decorator*: a special function that takes another function and alters its behavior.
+Trong đoạn mã trên `cachingDecorator` là một *decorator*: một hàm đặc biệt nhận một hàm khác và thay đổi hành vi của nó.
-The idea is that we can call `cachingDecorator` for any function, and it will return the caching wrapper. That's great, because we can have many functions that could use such a feature, and all we need to do is to apply `cachingDecorator` to them.
+Ý tưởng là chúng ta có thể gọi `cachingDecorator` cho bất kỳ hàm nào và nó sẽ trả về wrapper bộ nhớ đệm. Điều đó thật tuyệt, bởi vì chúng ta có thể có nhiều hàm có thể sử dụng một tính năng như vậy và tất cả những gì chúng ta cần làm là áp dụng `cachingDecorator` cho chúng.
-By separating caching from the main function code we also keep the main code simpler.
+Bằng cách tách bộ nhớ đệm khỏi mã hàm chính, chúng ta cũng giữ cho mã chính đơn giản hơn.
-The result of `cachingDecorator(func)` is a "wrapper": `function(x)` that "wraps" the call of `func(x)` into caching logic:
+Kết quả của `cachingDecorator(func)` là một "wrapper": `function(x)` "bao bọc" cuộc gọi của `func(x)` vào logic bộ nhớ đệm:
![](decorator-makecaching-wrapper.svg)
-From an outside code, the wrapped `slow` function still does the same. It just got a caching aspect added to its behavior.
+Từ một mã bên ngoài, hàm `slow` được bao bọc vẫn hoạt động như vậy. Nó chỉ có một khía cạnh lưu trữ được thêm vào hành vi của nó
-To summarize, there are several benefits of using a separate `cachingDecorator` instead of altering the code of `slow` itself:
+Tóm lại, có một số lợi ích khi sử dụng `cachingDecorator` riêng biệt thay vì thay đổi mã của chính `slow`:
-- The `cachingDecorator` is reusable. We can apply it to another function.
-- The caching logic is separate, it did not increase the complexity of `slow` itself (if there was any).
-- We can combine multiple decorators if needed (other decorators will follow).
+- `cachingDecorator` có thể tái sử dụng. Chúng ta có thể áp dụng nó cho một hàm khác.
+- Logic bộ nhớ đệm là riêng biệt, nó không làm tăng độ phức tạp của bản thân `slow` (nếu có).
+- Chúng ta có thể kết hợp nhiều decorator nếu cần (các decorator khác sẽ làm theo).
-## Using "func.call" for the context
+## Sử dụng "func.call" cho ngữ cảnh
-The caching decorator mentioned above is not suited to work with object methods.
+Decorator bộ nhớ đệm được đề cập ở trên không phù hợp để hoạt động với các phương thức đối tượng.
-For instance, in the code below `worker.slow()` stops working after the decoration:
+Chẳng hạn, trong mã bên dưới `worker.slow()` ngừng hoạt động sau khi trang trí:
```js run
-// we'll make worker.slow caching
+// chúng ta sẽ tạo bộ nhớ đệm worker.slow
let worker = {
someMethod() {
return 1;
},
slow(x) {
- // scary CPU-heavy task here
- alert("Called with " + x);
+ // nhiệm vụ nặng về CPU đáng sợ ở đây
+ alert("Gọi với " + x);
return x * this.someMethod(); // (*)
}
};
-// same code as before
+// mã giống như trước đây
function cachingDecorator(func) {
let cache = new Map();
return function(x) {
@@ -96,49 +96,49 @@ function cachingDecorator(func) {
};
}
-alert( worker.slow(1) ); // the original method works
+alert( worker.slow(1) ); // phương thức ban đầu hoạt động
-worker.slow = cachingDecorator(worker.slow); // now make it caching
+worker.slow = cachingDecorator(worker.slow); // bây giờ làm cho nó lưu vào bộ nhớ đệm
*!*
-alert( worker.slow(2) ); // Whoops! Error: Cannot read property 'someMethod' of undefined
+alert( worker.slow(2) ); // Rất tiếc! Error: Cannot read property 'someMethod' of undefined
*/!*
```
-The error occurs in the line `(*)` that tries to access `this.someMethod` and fails. Can you see why?
+Lỗi xảy ra trong dòng `(*)` cố truy cập `this.someMethod` nhưng không thành công. Bạn có thể thấy tại sao không?
-The reason is that the wrapper calls the original function as `func(x)` in the line `(**)`. And, when called like that, the function gets `this = undefined`.
+Lý do là wrapper gọi hàm ban đầu là `func(x)` trong dòng `(**)`. Và, khi được gọi như vậy, hàm nhận được `this = undefined`.
-We would observe a similar symptom if we tried to run:
+Chúng ta sẽ quan sát thấy một triệu chứng tương tự nếu chúng ta cố chạy:
```js
let func = worker.slow;
func(2);
```
-So, the wrapper passes the call to the original method, but without the context `this`. Hence the error.
+Vì vậy, wrapper chuyển lệnh gọi đến phương thức ban đầu, nhưng không có ngữ cảnh `this`. Do đó lỗi.
-Let's fix it.
+Hãy sửa nó.
-There's a special built-in function method [func.call(context, ...args)](mdn:js/Function/call) that allows to call a function explicitly setting `this`.
+Có một phương thức hàm tích hợp đặc biệt [func.call(context, ...args)](mdn:js/Function/call) cho phép gọi một hàm thiết lập rõ ràng `this`.
-The syntax is:
+Cú pháp là:
```js
func.call(context, arg1, arg2, ...)
```
-It runs `func` providing the first argument as `this`, and the next as the arguments.
+Nó chạy `func` cung cấp đối số đầu tiên là `this` và đối số tiếp theo là các đối số.
-To put it simply, these two calls do almost the same:
+Nói một cách đơn giản, hai cuộc gọi này thực hiện gần như giống nhau:
```js
func(1, 2, 3);
func.call(obj, 1, 2, 3)
```
-They both call `func` with arguments `1`, `2` and `3`. The only difference is that `func.call` also sets `this` to `obj`.
+Cả hai đều gọi `func` với các đối số `1`, `2` và `3`. Điểm khác biệt duy nhất là `func.call` cũng đặt `this` thành `obj`.
-As an example, in the code below we call `sayHi` in the context of different objects: `sayHi.call(user)` runs `sayHi` providing `this=user`, and the next line sets `this=admin`:
+Ví dụ: trong mã bên dưới, chúng ta gọi `sayHi` trong ngữ cảnh của các đối tượng khác nhau: `sayHi.call(user)` chạy `sayHi` cung cấp `this=user` và dòng tiếp theo đặt `this=admin`:
```js run
function sayHi() {
@@ -148,12 +148,12 @@ function sayHi() {
let user = { name: "John" };
let admin = { name: "Admin" };
-// use call to pass different objects as "this"
+// sử dụng lệnh gọi để chuyển các đối tượng khác nhau dưới dạng "this"
sayHi.call( user ); // John
sayHi.call( admin ); // Admin
```
-And here we use `call` to call `say` with the given context and phrase:
+Và ở đây chúng ta sử dụng `call` để gọi `say` với ngữ cảnh và cụm từ đã cho:
```js run
@@ -163,11 +163,11 @@ function say(phrase) {
let user = { name: "John" };
-// user becomes this, and "Hello" becomes the first argument
-say.call( user, "Hello" ); // John: Hello
+// người dùng trở thành this và "Xin chào" trở thành đối số đầu tiên
+say.call( user, "Xin chào" ); // John: Xin chào
```
-In our case, we can use `call` in the wrapper to pass the context to the original function:
+Trong trường hợp của chúng ta, chúng ta có thể sử dụng `call` trong wrapper để chuyển ngữ cảnh cho hàm ban đầu:
```js run
let worker = {
@@ -188,62 +188,62 @@ function cachingDecorator(func) {
return cache.get(x);
}
*!*
- let result = func.call(this, x); // "this" is passed correctly now
+ let result = func.call(this, x); // "this" bây giờ được thông qua một cách chính xác
*/!*
cache.set(x, result);
return result;
};
}
-worker.slow = cachingDecorator(worker.slow); // now make it caching
+worker.slow = cachingDecorator(worker.slow); // bây giờ làm cho nó lưu vào bộ nhớ đệm
-alert( worker.slow(2) ); // works
-alert( worker.slow(2) ); // works, doesn't call the original (cached)
+alert( worker.slow(2) ); // hoạt động
+alert( worker.slow(2) ); // hoạt động, không gọi bản gốc (được lưu trong bộ nhớ cache)
```
-Now everything is fine.
+Bây giờ mọi thứ đều ổn.
-To make it all clear, let's see more deeply how `this` is passed along:
+Để làm cho mọi thứ rõ ràng, chúng ta hãy tìm hiểu sâu hơn về cách thức `this` được truyền lại:
-1. After the decoration `worker.slow` is now the wrapper `function (x) { ... }`.
-2. So when `worker.slow(2)` is executed, the wrapper gets `2` as an argument and `this=worker` (it's the object before dot).
-3. Inside the wrapper, assuming the result is not yet cached, `func.call(this, x)` passes the current `this` (`=worker`) and the current argument (`=2`) to the original method.
+1. Sau phần trang trí `worker.slow` bây giờ là wrapper `function (x) { ... }`.
+2. Vì vậy, khi `worker.slow(2)` được chạy, wrapper sẽ lấy `2` làm đối số và `this=worker` (nó là đối tượng trước dấu chấm).
+3. Bên trong wrapper, giả sử kết quả chưa được lưu vào bộ nhớ cache, `func.call(this, x)` chuyển `this` (`=worker`) hiện tại và đối số hiện tại (`=2`) sang phương thức ban đầu.
-## Going multi-argument
+## Đi nhiều đối số
-Now let's make `cachingDecorator` even more universal. Till now it was working only with single-argument functions.
+Bây giờ, hãy làm cho `cachingDecorator` trở nên phổ biến hơn nữa. Cho đến bây giờ nó chỉ hoạt động với các hàm một đối số.
-Now how to cache the multi-argument `worker.slow` method?
+Bây giờ làm cách nào để lưu trữ phương thức `worker.slow` đa đối số?
```js
let worker = {
slow(min, max) {
- return min + max; // scary CPU-hogger is assumed
+ return min + max; // quái vật huỷ diệt CPU đáng sợ được giả định :)
}
};
-// should remember same-argument calls
+// nên nhớ các cuộc gọi cùng đối số
worker.slow = cachingDecorator(worker.slow);
```
-Previously, for a single argument `x` we could just `cache.set(x, result)` to save the result and `cache.get(x)` to retrieve it. But now we need to remember the result for a *combination of arguments* `(min,max)`. The native `Map` takes single value only as the key.
+Trước đây, đối với một đối số `x`, chúng ta chỉ có thể `cache.set(x, result)` để lưu kết quả và `cache.get(x)` để truy xuất nó. Nhưng bây giờ chúng ta cần ghi nhớ kết quả cho *tổ hợp các đối số* `(min, max`. `Map` gốc chỉ lấy một giá trị làm khóa.
-There are many solutions possible:
+Có nhiều giải pháp khả thi:
-1. Implement a new (or use a third-party) map-like data structure that is more versatile and allows multi-keys.
-2. Use nested maps: `cache.set(min)` will be a `Map` that stores the pair `(max, result)`. So we can get `result` as `cache.get(min).get(max)`.
-3. Join two values into one. In our particular case we can just use a string `"min,max"` as the `Map` key. For flexibility, we can allow to provide a *hashing function* for the decorator, that knows how to make one value from many.
+1. Triển khai cấu trúc dữ liệu giống như map mới (hoặc sử dụng bên thứ ba) linh hoạt hơn và cho phép nhiều khóa.
+2. Sử dụng các map lồng nhau: `cache.set(min)` sẽ là một `Map` lưu trữ cặp `(max, result)`. Vì vậy, chúng ta có thể nhận được `result` là `cache.get(min).get(max)`.
+3. Nối hai giá trị thành một. Trong trường hợp cụ thể của chúng ta, chúng ta chỉ có thể sử dụng một chuỗi `"min,max"` làm khóa `Map`. Để linh hoạt, chúng ta có thể cho phép cung cấp *hàm băm* cho decorator, biết cách tạo một giá trị từ nhiều giá trị.
-For many practical applications, the 3rd variant is good enough, so we'll stick to it.
+Đối với nhiều ứng dụng thực tế, biến thể thứ 3 là đủ tốt, vì vậy chúng ta sẽ sử dụng nó.
-Also we need to pass not just `x`, but all arguments in `func.call`. Let's recall that in a `function()` we can get a pseudo-array of its arguments as `arguments`, so `func.call(this, x)` should be replaced with `func.call(this, ...arguments)`.
+Ngoài ra, chúng ta không chỉ cần chuyển `x` mà tất cả các đối số trong `func.call`. Hãy nhớ lại rằng trong `function()` chúng ta có thể nhận được một array giả các đối số của nó là `arguments`, vì vậy `func.call(this, x)` nên được thay thế bằng `func.call(this, ... lập luận)`.
-Here's a more powerful `cachingDecorator`:
+Đây là một `cachingDecorator` mạnh mẽ hơn:
```js run
let worker = {
slow(min, max) {
- alert(`Called with ${min},${max}`);
+ alert(`Gọi với ${min},${max}`);
return min + max;
}
};
@@ -273,50 +273,50 @@ function hash(args) {
worker.slow = cachingDecorator(worker.slow, hash);
-alert( worker.slow(3, 5) ); // works
-alert( "Again " + worker.slow(3, 5) ); // same (cached)
+alert( worker.slow(3, 5) ); // hoạt động
+alert( "Lại " + worker.slow(3, 5) ); // giống nhau (được lưu trong bộ nhớ cache)
```
-Now it works with any number of arguments (though the hash function would also need to be adjusted to allow any number of arguments. An interesting way to handle this will be covered below).
+Bây giờ nó hoạt động với bất kỳ số lượng đối số nào (mặc dù hàm băm cũng cần được điều chỉnh để cho phép bất kỳ số lượng đối số nào. Một cách thú vị để xử lý vấn đề này sẽ được trình bày bên dưới).
-There are two changes:
+Có hai thay đổi:
-- In the line `(*)` it calls `hash` to create a single key from `arguments`. Here we use a simple "joining" function that turns arguments `(3, 5)` into the key `"3,5"`. More complex cases may require other hashing functions.
-- Then `(**)` uses `func.call(this, ...arguments)` to pass both the context and all arguments the wrapper got (not just the first one) to the original function.
+- Trong dòng `(*)`, nó gọi `hash` để tạo một khóa duy nhất từ `arguments`. Ở đây, chúng ta sử dụng một hàm "nối" đơn giản để biến đối số `(3, 5)` thành khóa `"3,5"`. Các trường hợp phức tạp hơn có thể yêu cầu các hàm băm khác.
+- Sau đó, `(**)` sử dụng `func.call(this, ...arguments)` để chuyển cả ngữ cảnh và tất cả các đối số mà wrapper nhận được (không chỉ đối số đầu tiên) cho hàm ban đầu.
## func.apply
-Instead of `func.call(this, ...arguments)` we could use `func.apply(this, arguments)`.
+Thay vì `func.call(this, ...arguments)` chúng ta có thể sử dụng `func.apply(this, arguments)`.
-The syntax of built-in method [func.apply](mdn:js/Function/apply) is:
+Cú pháp của phương thức tích hợp [func.apply](mdn:js/Function/apply) là:
```js
func.apply(context, args)
```
-It runs the `func` setting `this=context` and using an array-like object `args` as the list of arguments.
+Nó chạy cài đặt `func` `this=context` và sử dụng một đối tượng dạng array `args` làm danh sách các đối số.
-The only syntax difference between `call` and `apply` is that `call` expects a list of arguments, while `apply` takes an array-like object with them.
+Sự khác biệt duy nhất về cú pháp giữa `call` và `apply` là `call` cần một danh sách các đối số, trong khi `apply` mang theo một đối tượng dạng array.
-So these two calls are almost equivalent:
+Vì vậy, hai cuộc gọi này gần như tương đương:
```js
-func.call(context, ...args); // pass an array as list with spread syntax
-func.apply(context, args); // is same as using call
+func.call(context, ...args); // chuyển một array dưới dạng danh sách với cú pháp trải rộng
+func.apply(context, args); // giống như sử dụng cuộc gọi
```
-There's only a subtle difference:
+Chỉ có một sự khác biệt rất nhỏ:
-- The spread syntax `...` allows to pass *iterable* `args` as the list to `call`.
-- The `apply` accepts only *array-like* `args`.
+- Cú pháp trải rộng `...` cho phép chuyển *iterable* `args` dưới dạng danh sách tới `call`.
+- `apply` chỉ chấp nhận *dạng array* `args`.
-So, where we expect an iterable, `call` works, and where we expect an array-like, `apply` works.
+Vì vậy, nơi chúng ta mong đợi một lần lặp, `call` hoạt động và nơi chúng ta mong đợi một array giống như `apply` hoạt động.
-And for objects that are both iterable and array-like, like a real array, we can use any of them, but `apply` will probably be faster, because most JavaScript engines internally optimize it better.
+Và đối với các đối tượng vừa có thể lặp lại vừa có dạng array, giống như một array thực, chúng ta có thể sử dụng bất kỳ đối tượng nào trong số chúng, nhưng `apply` có thể sẽ nhanh hơn, bởi vì hầu hết các JavaScript engine bên trong tối ưu hóa nó tốt hơn.
-Passing all arguments along with the context to another function is called *call forwarding*.
+Việc chuyển tất cả các đối số cùng với ngữ cảnh sang một hàm khác được gọi là *chuyển tiếp cuộc gọi*.
-That's the simplest form of it:
+Đây là hình thức đơn giản nhất của nó:
```js
let wrapper = function() {
@@ -324,11 +324,11 @@ let wrapper = function() {
};
```
-When an external code calls such `wrapper`, it is indistinguishable from the call of the original function `func`.
+Khi một mã bên ngoài gọi `wrapper` như vậy, nó không thể phân biệt được với lệnh gọi của hàm ban đầu `func`.
-## Borrowing a method [#method-borrowing]
+## Mượn một phương thức [#method-borrowing]
-Now let's make one more minor improvement in the hashing function:
+Bây giờ, hãy thực hiện thêm một cải tiến nhỏ nữa trong hàm băm:
```js
function hash(args) {
@@ -336,9 +336,9 @@ function hash(args) {
}
```
-As of now, it works only on two arguments. It would be better if it could glue any number of `args`.
+Hiện tại, nó chỉ hoạt động trên hai đối số. Sẽ tốt hơn nếu nó có thể dán bất kỳ số lượng `args` nào.
-The natural solution would be to use [arr.join](mdn:js/Array/join) method:
+Giải pháp tự nhiên sẽ là sử dụng phương thức [arr.join](mdn:js/Array/join):
```js
function hash(args) {
@@ -346,9 +346,9 @@ function hash(args) {
}
```
-...Unfortunately, that won't work. Because we are calling `hash(arguments)`, and `arguments` object is both iterable and array-like, but not a real array.
+...Thật không may, điều đó sẽ không làm việc. Bởi vì chúng ta đang gọi `băm(đối số)` và đối tượng `đối số` vừa có thể lặp lại vừa có dạng array, nhưng không phải là một array thực.
-So calling `join` on it would fail, as we can see below:
+Vì vậy, việc gọi `join` trên đó sẽ thất bại, như chúng ta có thể thấy bên dưới:
```js run
function hash() {
@@ -360,7 +360,7 @@ function hash() {
hash(1, 2);
```
-Still, there's an easy way to use array join:
+Tuy nhiên, vẫn có một cách dễ dàng để sử dụng phép nối array:
```js run
function hash() {
@@ -372,48 +372,48 @@ function hash() {
hash(1, 2);
```
-The trick is called *method borrowing*.
+Thủ thuật được gọi là *mượn phương thức*.
-We take (borrow) a join method from a regular array (`[].join`) and use `[].join.call` to run it in the context of `arguments`.
+Chúng ta lấy (mượn) một phương thức nối từ một array thông thường (`[].join`) và sử dụng `[].join.call` để chạy nó trong ngữ cảnh của `arguments`.
-Why does it work?
+Tại sao nó lại hoạt động?
-That's because the internal algorithm of the native method `arr.join(glue)` is very simple.
+Đó là vì thuật toán bên trong của phương thức gốc `arr.join(glue)` rất đơn giản.
-Taken from the specification almost "as-is":
+Lấy từ thông số kỹ thuật gần như "nguyên trạng":
-1. Let `glue` be the first argument or, if no arguments, then a comma `","`.
-2. Let `result` be an empty string.
-3. Append `this[0]` to `result`.
-4. Append `glue` and `this[1]`.
-5. Append `glue` and `this[2]`.
-6. ...Do so until `this.length` items are glued.
-7. Return `result`.
+1. Đặt `glue` là đối số đầu tiên hoặc nếu không có đối số thì dấu phẩy `","`.
+2. Đặt `result` là một chuỗi rỗng.
+3. Nối `this[0]` vào `kết quả`.
+4. Nối `glue` và `this[1]`.
+5. Nối `glue` và `this[2]`.
+6. ...Làm như vậy cho đến khi các mục `this.length` được dán.
+7. Trả về `result`.
-So, technically it takes `this` and joins `this[0]`, `this[1]` ...etc together. It's intentionally written in a way that allows any array-like `this` (not a coincidence, many methods follow this practice). That's why it also works with `this=arguments`.
+Vì vậy, về mặt kỹ thuật, nó cần `this` và nối `this[0]`, `this[1]` ...v.v với nhau. Nó được cố ý viết theo cách cho phép bất kỳ `this` nào dạng array (không phải ngẫu nhiên, nhiều phương thức tuân theo cách làm này). Đó là lý do tại sao nó cũng hoạt động với `this=arguments`.
-## Decorators and function properties
+## Decorator và thuộc tính hàm
-It is generally safe to replace a function or a method with a decorated one, except for one little thing. If the original function had properties on it, like `func.calledCount` or whatever, then the decorated one will not provide them. Because that is a wrapper. So one needs to be careful if one uses them.
+Nói chung là an toàn khi thay thế một hàm hoặc một phương thức bằng một hàm được trang trí, ngoại trừ một điều nhỏ. Nếu hàm ban đầu có các thuộc tính trên đó, chẳng hạn như `func.calledCount` hoặc bất cứ thứ gì, thì hàm được trang trí sẽ không cung cấp các thuộc tính đó. Bởi vì đó là một wrapper. Vì vậy, hàm này cần phải cẩn thận nếu hàm kia sử dụng chúng.
-E.g. in the example above if `slow` function had any properties on it, then `cachingDecorator(slow)` is a wrapper without them.
+Ví dụ. trong ví dụ trên nếu hàm `slow` có bất kỳ thuộc tính nào trên đó, thì `cachingDecorator(slow)` là wrapper không có thuộc tính đó.
-Some decorators may provide their own properties. E.g. a decorator may count how many times a function was invoked and how much time it took, and expose this information via wrapper properties.
+Một số decorator có thể cung cấp các thuộc tính riêng của chúng. Ví dụ. một decorator có thể đếm số lần một hàm được gọi và mất bao nhiêu thời gian, đồng thời hiển thị thông tin này thông qua các thuộc tính của wrapper.
-There exists a way to create decorators that keep access to function properties, but this requires using a special `Proxy` object to wrap a function. We'll discuss it later in the article