Series JavaScript sida – Bind, Call và Apply trong JavaScript
js
5
Javascript
247
code
39
series
6
White

Huy Hoàng Phạm viết ngày 08/04/2016

Series JavaScript sida – Bind, Call và Apply trong JavaScript

Để hiểu rõ về các hàm được đề cập trong bài viết này, các bạn nên ôn lại chút kiến thức về object trong javascriptđít (this) trong javascript nhé. Như đã hứa, trong bài này mình sẽ giới thiệu bộ 3 function _bind, call ,apply. _Đây là 3 hàm tạo nên sự mạnh mẽ và bá đạo của JavaScript.

Trói đít (this) lại bằng bind

Bind là một hàm nằm trong Function.prototype, do đó chỉ có function mới có khả năng gọi nó. Như đã nhắc tới trong bài viết về this, bind được dùng để xác định tham số this cho một function.

1

Như trong trường hợp dưới đây, khi ta truyền hàm showName vào như một callback cho hàm button.click, giá trị this ở đây chính là button đó. Để hàm chạy đúng, ta dùng bind để bind giá trị personthis.

var person = {
  firstName: 'Hoang',
  lastName: 'Pham',
  showName: function() {
    console.log(this.firstName + ' ' + this.lastName);
  }
};

//showName truyền vào như callback, ở đây this chính là button
$('button').click(person.showName); 

// Dùng bind để xác định giá trị this
$('button').click(person.showName.bind(person)); //this ở đây vẫn là object person

Không chỉ bind được giá trị this, bind còn bind được các tham số truyền vào cho hàm nữa. Do đó, Bind còn được dùng để viết partial function.

Nói một cách đơn giản, partial function tức là tạo ra 1 function mới từ 1 function cũ bằng cách gán mặc định một số tham số cho function cũ đó. Bạn hãy xem ví dụ cụ thể sau. Mình có một hàm log đơn giản có 3 tham số:

function log(level, time, message) {
  console.log(level + ' - ' + time + ': ' + message);
}

Giả sử mình muốn tạo một hàm log khác, ghi lại các log error của hôm nay, mình có thể viết một hàm mới dựa theo hàm log cũ:

function log(level, time, message) {
  console.log(level + ' - ' + time + ': ' + message);
}

function logErrToday(message) {
  log("Error", "Today", message);
}

logErrToday("Server die."); // Error - Today: Server die.

Thay vì viết như thế, mình có thể viết đơn giản hơn bằng các dùng bind. Ở đây log là function cũ, logErrToday là function mới, được tạo ra bằng cách gán mặc định 2 tham số leveltime.

function log(level, time, message) {
  console.log(level + ' - ' + time + ': ' + message);
}

// Không có this nên set this là null
// Set mặc định 2 tham số level và time
var logErrToday = log.bind(null, 'Error', 'Today');

// Hàm này tương ứng với log('Error', 'Today', 'Server die.')
logErrToday("Server die."); 
// Error - Today: Server die.

Partial Function còn được gọi là Curry (Nhiều người bảo 2 cái đó là một, nhiều người bảo 2 cái đấy khác nhau). Nếu bạn thấy khái niệm partial function/curry khá lạ tai cũng đừng lo, chúng ít được dùng trong Java, C# mà hay được sử dụng khá nhiều trong một số ngôn ngữ kiểu lập trình hàm (functional programming) như Haskell, F#, Scala,…. . Functional programming khá là khó học, dễ nhức đầu đau não, bạn nào muốn thử sức thì cứ kiếm ngôn ngữ Haskell mà phang nhé.

Call và Apply, tuy 2 mà 1, thấy 1 mà 2

Đây là 1 cặp anh em… nhầm, chị em song sinh trong JavaScript. Hai hàm này nằm trong prototype của Function (Function.prototype), do đó chỉ function mới có thể gọi. Chúng có chung một chức năng lại: Gọi 1 function, xác định tham số this, truyền các tham số còn lại vào.

Điểm khác nhau là apply truyền vào một array chứa toàn bộ các tham số còn call truyền lần lượt từng tham số. Dể dễ nhớ, ta có thể nhẩm "A là một Array, C là nhiều Cục".

apply

call

Cùng xem ví dụ đơn giản này về callapply, bạn sẽ hiểu ngay.

// Tìm max bằng cách gọi hàm Math.max
Math.max(4, 3, 2, 10);

// Thay vì gọi trực tiếp hàm Math.max, ta có thể dùng call
// Set this bằng null
Math.max.call(null, 4, 3, 2, 10);

// Apply tương tự call, nhưng không truyền lần lượt
// Mà truyền một array chứa toàn bộ các tham số
Math.max.apply(null, [4, 3, 2, 10]);

Callapply thường được dùng để mượn hàm (borrowing function). Các bạn thử đọc xem dòng code dưới đây làm gì?

function test(firstParam, secondParam, thirdParam){
  var args = Array.apply(null, arguments);
  console.log(args);
}

test(1, 2, 3); // [1, 2, 3]

Gợi ý: Arguments là một biến cục bộ trong function, chứa toàn bộ các tham số được truyền vào.

Đáp án: A_rguments_ là một object giống array nhưng không phải là array (đấy, thấy sida chưa). Arguments giống array vì nó có field length, có thể truy cập các giá trị nó chứa thông qua index 0,1,2. Tuy nhiên, do arguments không phải là array nên nó không thể gọi các hàm của Array.prototype.

Do đó, ta phải sử dụng call/apply để mượn một số hàm trong Array.prototype, các hàm này sẽ trả ra một array cho ta xử lý. Dòng code phía trên chuyển object arguments thành một array. Sao rắc rối quá vậy?? Sao arguments không phải là array luôn đi cho mọi chuyện đơn giản? Lý do là Javascript nó sida. Hồi mới biết thằng arguments này mình cũng muốn chửi thề như vậy đấy.

4

Ngoài ra, callapply còn được dùng để monkey-patching hoặc tạo spy. Ta có thể mở rộng chức năng của một hàm mà không cần sửa source code của hàm đó. Ví dụ ta có hàm accessWeb của object computer.

var computer = {
  accessWeb : function(site) {
    // Đi tới site nào đó
    console.log ('Go to: ' + site);
  }
};

computer.accessWeb('thiend*a.com'); //Go to: thiend*a.com

Sử dụng call, ta có thể ghi thêm log trước và sau khi hàm accessWeb được gọi mà không can thiệp vào code của hàm đó

var computer = {
  accessWeb : function(site) {
    // Đi tới site nào đó
    console.log ('Go to: ' + site);
  }
};

var oldFunction = computer.accessWeb;
// Tráo function accessWeb bằng hàm mới
computer.accessWeb = function() {
  console.log('Con gà bắt đầu vào web');
  oldFunction.apply(this, arguments); // giữ nguyên hàm cũ
  console.log('Con gà đã vào web');
}

computer.accessWeb('thiend*a.com'); 
// Con gà bắt đầu vào web
// Go to: thiend*a.com
// Con gà đã vào web

Nói nhỏ các bạn nghe nè, mấy cái call/apply với bind này cũng ít người rành, do đó các bạn đọc xong bài này có thể lấy kiến thức ra để lòe thiên hạ. Vì ít người biết nên nếu viết code mà dùng call, apply v…v bạn nhớ giải thích rõ nhé, viết code khó hiểu dễ bị ăn chửi lắm (Nói thật đấy, ngày xưa đoạn code Array.call gì đó làm mình khó hiểu, đau cả đầu nên phải lôi cả họ nhà thằng code ra mà chửi).

Bản gốc: Blog Tôi đi code dạo.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Huy Hoàng Phạm

54 bài viết.
924 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
74 8
SOLID là gì – Áp dụng các nguyên lý SOLID để trở thành lập trình viên code “cứng” Trong quá trình học, hầu như các bạn sinh viên đều được học một s...
Huy Hoàng Phạm viết gần 3 năm trước
74 8
White
58 8
Nhập đề Kinh thư ghi lại rằng, con đường tu chân có 3 cảnh giới: Luyện khí, Trúc cơ và Kết đan. Luyện khí là quá trình rèn thân luyện thể, cho phà...
Huy Hoàng Phạm viết hơn 2 năm trước
58 8
White
53 23
Luận về comment code (Phong cách kiếm hiệp) Comment code luôn là vấn đề gây tranh cãi sứt đầu mẻ trán trong giới võ lâm. Xưa kia, thuở còn mài đít...
Huy Hoàng Phạm viết gần 3 năm trước
53 23
Bài viết liên quan
White
10 2
I. Sass và Gulp là gì? 1. Sass (Link) là một CSS PreProcessor (Tương tự Less, Stylus, …). Sass được sinh ra để giúp bạn viết CSS nhanh và rõ ràn...
Sang Nguyễn Đắc viết hơn 1 năm trước
10 2
White
19 0
Dạo gần đây mình đi làm thêm]1], project hiện tại có sử dụng React với cả ES6]2] – chuẩn Javascript mới. Do mấy bài viết trước mình chê JavaScript ...
Huy Hoàng Phạm viết hơn 2 năm trước
19 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
54 bài viết.
924 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!