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 thissẽ đượ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, thissẽ 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. thistrong 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ư forEachvà slice, bạn sẽ biết rằng thisbiến trong trường hợp đó đề cập đến hàm Objectmà bạn đã gọi hàm đó (Lưu ý rằng trong javascript, mọi thứ đều là một Object, bao gồm cả Arrays và Functions). 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 Objectchứ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ó thisbiế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 thiskhô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 newtừ 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 newtừ khóa.
- Gọi hàm bằng
newtừ khóa sẽ ngay lập tức khởi tạo một ObjectkiểuPerson .
- Hàm tạo của cái này
Objectcó hàm tạo của nó được đặt thành Person. Ngoài ra, lưu ý rằng typeof awalsẽ trở lạiObject chỉ .
- Điều này mới
Objectsẽ đượ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 Personnguyê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
Personchính nó bây giờ được gọi; thislà 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 constructorhàm thực tế . Chúng chỉ là các chức năng bình thường, và newcó 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: callvàapply
Vì vậy, vâng, vì functioncũ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à callvà apply, và cả hai có thể được sử dụng để thao tác các giá trị thistrong 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 thistrong hàm foolà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 foodướ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 thisbất kỳ chức năng nào.
applygần giống như callchấp nhận rằng nó chỉ cần hai tham số: thisArgvà một mảng chứa các đối số được truyền cho hàm. Vì vậy, callcuộc gọi trên có thể được dịch thành applynhư thế này:
foo.apply(thisArg, [1,2,3])
Lưu ý rằng callvà applycó 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!
bindlà anh trai của callvà 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 Functiontrong Javascript. Sự khác biệt giữa bindvà call/ applylà cả hai callvà applysẽ thực sự gọi hàm. bindmặt khác, trả về một hàm mới với thisArgvà đượ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 callvà apply, bindcũ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. callvà applysẽ 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à thisthay đổ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ị thisthay đổi với phạm vi lồng nhau, nhưng chúng ta muốn giá trị thistừ phạm vi ban đầu. Vì vậy, chúng tôi 'sao chép' thissang thatvà 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
thistheo 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
thisvới callvà apply?
- Sử dụng
bind.
- Sao chép
thisđể giải quyết các vấn đề phạm vi lồng nhau.