Javascript this
Gọi hàm đơn giản
Hãy xem xét các chức năng sau:
function foo() {
console.log("bar");
console.log(this);
}
foo(); // calling the function
Lưu ý rằng chúng tôi đang chạy này trong chế độ bình thường, tức là chế độ nghiêm ngặt không được sử dụng.
Khi chạy trong trình duyệt, giá trị của this
sẽ được ghi lại dưới dạng window
. Đây là vìwindow
biến toàn cục trong phạm vi của trình duyệt web.
Nếu bạn chạy cùng một đoạn mã này trong một môi trường như node.js, this
sẽ đề cập đến biến toàn cục trong ứng dụng của bạn.
Bây giờ nếu chúng ta chạy nó trong chế độ nghiêm ngặt bằng cách thêm câu lệnh "use strict";
vào đầu khai báo hàm, this
sẽ không còn đề cập đến biến toàn cục trong một trong hai môi trường. Điều này được thực hiện để tránh nhầm lẫn trong chế độ nghiêm ngặt. this
trong trường hợp này chỉ cần đăng nhậpundefined
, bởi vì đó là những gì nó được, nó không được xác định.
Trong các trường hợp sau đây, chúng ta sẽ thấy cách thao tác giá trị của this
.
Gọi một hàm trên một đối tượng
Có nhiều cách khác nhau để làm điều này. Nếu bạn đã gọi các phương thức gốc trong Javascript như forEach
và slice
, bạn sẽ biết rằng this
biến trong trường hợp đó đề cập đến hàm Object
mà bạn đã gọi hàm đó (Lưu ý rằng trong javascript, mọi thứ đều là một Object
, bao gồm cả Array
s và Function
s). Lấy mã sau đây làm ví dụ.
var myObj = {key: "Obj"};
myObj.logThis = function () {
// I am a method
console.log(this);
}
myObj.logThis(); // myObj is logged
Nếu một thuộc tính Object
chứa một thuộc tính Function
, thuộc tính đó được gọi là một phương thức. Phương thức này, khi được gọi, sẽ luôn có this
biến được đặt thànhObject
được liên kết với nó. Điều này đúng cho cả chế độ nghiêm ngặt và không nghiêm ngặt.
Lưu ý rằng nếu một phương thức được lưu trữ (hoặc đúng hơn, được sao chép) trong một biến khác, thì tham chiếu đến this
không còn được bảo tồn trong biến mới. Ví dụ:
// continuing with the previous code snippet
var myVar = myObj.logThis;
myVar();
// logs either of window/global/undefined based on mode of operation
Xem xét một kịch bản phổ biến hơn thực tế:
var el = document.getElementById('idOfEl');
el.addEventListener('click', function() { console.log(this) });
// the function called by addEventListener contains this as the reference to the element
// so clicking on our element would log that element itself
các new
từ khóa
Hãy xem xét một hàm tạo trong Javascript:
function Person (name) {
this.name = name;
this.sayHello = function () {
console.log ("Hello", this);
}
}
var awal = new Person("Awal");
awal.sayHello();
// In `awal.sayHello`, `this` contains the reference to the variable `awal`
Cái này hoạt động ra sao? Chà, hãy xem điều gì xảy ra khi chúng ta sử dụng new
từ khóa.
- Gọi hàm bằng
new
từ khóa sẽ ngay lập tức khởi tạo một Object
kiểuPerson
.
- Hàm tạo của cái này
Object
có hàm tạo của nó được đặt thành Person
. Ngoài ra, lưu ý rằng typeof awal
sẽ trở lạiObject
chỉ .
- Điều này mới
Object
sẽ được chỉ định nguyên mẫu của Person.prototype
. Điều này có nghĩa là mọi phương thức hoặc thuộc tính trong Person
nguyên mẫu sẽ có sẵn cho tất cả các trường hợp Person
, bao gồmawal
.
- Các chức năng
Person
chính nó bây giờ được gọi; this
là một tài liệu tham khảo cho các đối tượng mới được xây dựng awal
.
Khá đơn giản nhỉ?
Lưu ý rằng đặc tả ECMAScript chính thức không nêu rõ rằng các loại hàm đó là các constructor
hàm thực tế . Chúng chỉ là các chức năng bình thường, và new
có thể được sử dụng trên bất kỳ chức năng nào. Chỉ là chúng tôi sử dụng chúng như vậy, và vì vậy chúng tôi gọi chúng là như vậy mà thôi.
Các hàm gọi trên Hàm: call
vàapply
Vì vậy, vâng, vì function
cũng là Objects
(và trên thực tế là các biến hạng nhất trong Javascript), ngay cả các hàm cũng có các phương thức ... tốt, chính chúng là các hàm.
Tất cả các chức năng kế thừa từ toàn cầu Function
, và hai trong số nhiều phương pháp của nó là call
và apply
, và cả hai có thể được sử dụng để thao tác các giá trị this
trong hàm mà chúng được gọi.
function foo () { console.log (this, arguments); }
var thisArg = {myObj: "is cool"};
foo.call(thisArg, 1, 2, 3);
Đây là một ví dụ điển hình của việc sử dụng call
. Về cơ bản, nó lấy tham số đầu tiên và đặt this
trong hàm foo
làm tham chiếu thisArg
. Tất cả các tham số khác được truyền vào call
được truyền cho hàm foo
dưới dạng đối số.
Vì vậy, đoạn mã trên sẽ đăng nhập {myObj: "is cool"}, [1, 2, 3]
vào giao diện điều khiển. Cách khá hay để thay đổi giá trị của this
bất kỳ chức năng nào.
apply
gần giống như call
chấp nhận rằng nó chỉ cần hai tham số: thisArg
và một mảng chứa các đối số được truyền cho hàm. Vì vậy, call
cuộc gọi trên có thể được dịch thành apply
như thế này:
foo.apply(thisArg, [1,2,3])
Lưu ý rằng call
và apply
có thể ghi đè giá trị củathis
tập hợp bằng cách gọi phương thức dấu chấm mà chúng ta đã thảo luận trong dấu đầu dòng thứ hai. Đủ đơn giản :)
Trình bày .... bind
!
bind
là anh trai của call
và apply
. Nó cũng là một phương thức được kế thừa bởi tất cả các hàm từ hàm tạo toàn cục Function
trong Javascript. Sự khác biệt giữa bind
và call
/ apply
là cả hai call
và apply
sẽ thực sự gọi hàm. bind
mặt khác, trả về một hàm mới với thisArg
và được arguments
đặt trước. Hãy lấy một ví dụ để hiểu rõ hơn về điều này:
function foo (a, b) {
console.log (this, arguments);
}
var thisArg = {myObj: "even more cool now"};
var bound = foo.bind(thisArg, 1, 2);
console.log (typeof bound); // logs `function`
console.log (bound);
/* logs `function () { native code }` */
bound(); // calling the function returned by `.bind`
// logs `{myObj: "even more cool now"}, [1, 2]`
Thấy sự khác biệt giữa ba? Nó là tinh tế, nhưng chúng được sử dụng khác nhau. Thích call
và apply
, bind
cũng sẽ vượt quá giá trị củathis
tập hợp bằng cách gọi phương thức dot.
Cũng lưu ý rằng cả hai chức năng này đều không thay đổi chức năng ban đầu. call
và apply
sẽ trả về giá trị từ các hàm được xây dựng mới trong khibind
sẽ trả về chính hàm được xây dựng mới, sẵn sàng để được gọi.
Thêm công cụ, sao chép này
Đôi khi, bạn không thích thực tế là this
thay đổi theo phạm vi, đặc biệt là phạm vi lồng nhau. Hãy xem ví dụ sau đây.
var myObj = {
hello: function () {
return "world"
},
myMethod: function () {
// copy this, variable names are case-sensitive
var that = this;
// callbacks ftw \o/
foo.bar("args", function () {
// I want to call `hello` here
this.hello(); // error
// but `this` references to `foo` damn!
// oh wait we have a backup \o/
that.hello(); // "world"
});
}
};
Trong đoạn mã trên, chúng ta thấy rằng giá trị this
thay đổi với phạm vi lồng nhau, nhưng chúng ta muốn giá trị this
từ phạm vi ban đầu. Vì vậy, chúng tôi 'sao chép' this
sang that
và sử dụng bản sao thay vì this
. Khéo léo hả?
Mục lục:
- Những gì được tổ chức
this
theo mặc định?
- Điều gì xảy ra nếu chúng ta gọi hàm là một phương thức với ký hiệu Object-dot?
- Nếu chúng ta sử dụng
new
từ khóa thì sao?
- Làm thế nào để chúng ta thao tác
this
với call
và apply
?
- Sử dụng
bind
.
- Sao chép
this
để giải quyết các vấn đề phạm vi lồng nhau.