This trong Javascript là gì
1. Mở đầuTrước khi học hiểu về thisbạn nên học trước vềscope và closure Show
Trong js, từ khóa thislà thứ rất hay nhưng cũng là thứ gây ra bao rắc rối cho nhiều người, nhất là đối với những người đi từ ngôn ngữ lập trình khác sang js. Lý do lớn nhất khiến thisgây hiểu nhầm cho bao nhiêu người chính là vì ý nghĩa từ điểncủa chính từ this Khi bạn bắt gặp từ thistrong lập trình, gần như chắc chắn bạn sẽ nghĩ tới nó chính là tham chiếu tới instancehiện tại hoặc nội hàmhiện tại, và đó cũng là lý do khiến nhiều người hiểu nhầm từ thistrong js. Có 2 chú ý khi bạn bắt gặp thismà cần phải nhớ đó là:
2. thiskhông phải là tham chiếu tới chính function.function foo(num) { console.log("foo: " + num); //keep track of how many times `foo` is called this.count++; } foo.count = 0; var i; for (i=0; i<5; i++) { foo(i); } console.log(foo.count);Nếu dịch thislà nội hàm thì nhiều người sẽ nghĩ đoạn code trên cho ra kết quả: 01 2 3 4 5 <= result of console.log(foo.count) Nhưng không phải, kết quả in ra là: 01 2 3 4 0 <= result of console.log(foo.count) Như nói ở trên, ta cần quan tâm tới việc hàm foo()được gọi ở đâu. Trong TH này foo()được gọi tại câu lệnh số 10 bằng cách gọi hàm trực tiếp(xem Function Invocation phía dưới) nên contextở đây chính là global. Vì là globalnên this.countở dòng 4 sẽ làundefineddẫn tớithis.count++trả về NaN. Câu lệnh 13 in ra giá trị 0 vìfoo.countở scope hiện tại được khai báo bằng 0. Nếu bạn muốn tham chiếu tới chính object footrong function thì sửa thành như sau: function foo(num) { console.log("foo: " + num); //keep track of how many times `foo` is called foo.count++; } foo.count = 0; var i; for (i=0; i<5; i++) { foo(i); } console.log(foo.count);Nó sẽ in ra kết quả đúng cho bạn. 3. thistrong Function InvocationXét ví dụ: var value = 500; //Global variable var obj = { value: 0, increment: function() { this.value++; var innerFunction = function() { console.log(this.value); } innerFunction(); //Function invocation pattern } } obj.increment(); //Method invocation patternGọi hàm kiểu Function invocation pattern (gọi trực tiếp bằng cách thêm dấu () ) thì từ khóa this trong hàm đó luôn là global object (window) Vì vậy đoạn code trên sẽ in ra 500 chứ không phải 1. 4. thistrong callback của hàm setTimeoutthis trong hàm ẩn danh(anonymous function) luôn là global
5. thistrong Method Invocation.thistrong Method Invocation chính là contextcủa object gọi tới method đó. var value = 500; //Global variable var obj = { value: 0, increment: function() { this.value++; console.log(this.value) } } obj.increment(); //Method invocation patternKết quả trả về là 1 Nhưng nếu là gọi trong setTimeout thì thisluôn là global: function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; var a = 100; setTimeout( obj.foo, 1000 ); // 1006. thiskhi gọi với từ khóa newfunction foo(a) { this.a = a; } var a = 10; var bar = new foo( 2 ); console.log( bar.a ); // 2Khác với Function invocation,khi khai báo bằng từ khóa newphía trước, một object sẽ được khởi tạo và trả về object đó nên contextở đây sẽ chính là object được khởi tạo. 7. thistrong eval()
Đọc thêm vền direct eval và indirect eval tại đây x = 10; (function foo() { var x = 20; (function bar(){ var x = 30; eval("test()"); // 10 var indirectEval = eval; indirectEval("test()"); // 10 var obj = { x: 40, test: test }; eval("obj.test()"); // 40 indirectEval("obj.test()"); // lỗi vì ở global ko có biến obj })(); })(); function test() { var x = 100; console.log(this.x) }Kết quả in ra là: 1010 40 error 8. thistrong các hàm đặc biệt của jsNhư nói ở trên, khi gọi hàm bằng method invocationthì thislà contextcủa object. Nhưng trừ 8 hàm đặc biệt sau:
Đối với trường hợp Function.prototype: contextsẽ là thisArg chứ không phải là object Đối với trường hợp Array.prototype:contextsẽ là thisArg nếu được truyền vào, nếu không thì là global 9. thistrong DOM event handlerXem thêm về cách gọi hàm trong event handler tạiđây thiskhi event được trigger chính là button chứa event đó. Nhưng nếu bạn khai báo một hàm trong event handler thì thissẽ là global(window trong browser) vì thisđã nằm trong hàm ẩn danh. hoặc 10. thistrong ES6arrow functionChỉ nên dùng arrow function với các hàm không phải là method của object var obj = { i: 10, b: () => console.log(this.i, this), // hoặc b: () => {console.log(this.i, this)}, c: function() { console.log(this.i, this); } } obj.b(); // prints undefined, Window {...} (or the global object) obj.c(); // prints 10, Object {...}Bản thân arrow function không tự tạo ra thisrồi truyền vào cho lệnh thực thi mà thực chất arrow function không được khai báo theo kiểu truyền thống function(){}nên sẽ không có thischo arrow function, thisđược sử dụng ở đây chính là thiscủa contextmà nơi chứa arrow functionđược gọi. Nên ở trên obj.b()mới có thislà global(Window) Cũng chính vì lý do trên mà các hàm đặc biệt như bind, callsẽ không hoạt động với arrow function var globalObject = this; var foo = (() => this); // hoặc var foo = (() => {return this}); console.log(foo() === globalObject); // true // Call as a method of an object var obj = {func: foo}; console.log(obj.func() === globalObject); // true // Attempt to set this using call console.log(foo.call(obj) === globalObject); // true // Attempt to set this using bind foo = foo.bind(obj); console.log(foo() === globalObject); // trueclass trong ES6Xét ví dụ một đoạn tutorial trong document của react: class Toggle extends React.Component { constructor(props) { super(props); console.log("1: ", this) this.state = {isToggleOn: true}; } handleClick() { console.log("2: ", this) this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); } render() { console.log("3: ", this) return (Các bước thực thi như sau:
Bạn để ý trong hàm rendercó đoạn Khi render xong và bạn click vào button thì chuyện gì xảy ra? Khi click, câu lệnh this.handleClicksẽ thực thi, nhảy vào hàm handleClick()như giải thích ở phần DOM event handlerthis ở đây sẽ là global(window)chứ không phải object của Togglenên nó sẽ báo lỗi không biết hàm setStatecủa window. Để sửa lỗi này, có một số cách như sau:
Một số ví dụ kiểm traVD1: var obj = { someData: "a string" }; function myFun() { console.log(this); } obj.staticFunction = myFun; obj.staticFunction(); //=======result==========// // this is obj // //=======================//VD2: var obj = { myMethod : function () { console.log(this); } }; var myFun = obj.myMethod; myFun(); //=========result==========// // this is global(window) // //=========================//VD3: function myFun() { console.log(this); } var obj = { myMethod : function () { eval("myFun()"); } }; obj.myMethod(); //=========result==========// // this is global(window) // //=========================//VD4: function myFun() { console.log(this); } var obj = { someData: "a string" }; myFun.call(obj); //=========result==========// // this is obj // //=========================//VD5: function Person(){ var age = 10; setTimeout(function(){ this.age++; console.log(this.age); }, 1000); } var p = Person(); //=========result==========// // NaN // //=========================// var q = new Person(); //=========result==========// // NaN // //=========================//VD6: function Person(){ this.age = 10; setTimeout(function(){ this.age++; console.log(this.age); }, 1000); } var p = Person(); //=========result==========// // 11 // //=========================//nhưng: function Person(){ this.age = 10; setTimeout(function(){ this.age++; console.log(this.age); }, 1000); } var p = new Person(); //=========result==========// // NaN // //=========================//Link tham khảo:https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work/3127440#3127440 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions https://reactjs.org/docs/handling-events.html |