Hello everyone! (bow)
For those who have studied English, it is no stranger to the sentence “What is this?”. Yes, this is here, this is there, this is everywhere. So in the realm of JavaScript programming, this pointer is an important concept. Understanding it as this 1 or this 2 will help avoid unwanted bugs when working with Javascript. Therefore in this article we go to clarify with this keyword offline.
What the hell is this
?
When you encounter this
keyword in programming languages like Java, C #, etc., you will most likely think of this as a reference to the current instance or function. This is also the reason that many people misunderstand about this
keyword in Javascript
, especially the new ones you use and use Js.
In Javascript
, this
is a keyword whose essence is the same as its name, which refers to the object currently being used or accessed. Quite similar to the definition of this in other languages, right, but in Js this
has a different value depending on the context ( context ) being used.
For example: Through Va’s wild day, I have taken my girlfriend to the most luxurious restaurant in Hanoi. (Imagine you guys! Huhi) When the food was brought out, my girlfriend asked “What is this?”. I said it was Ocean Lobster. Then she pointed to the bowl of soup and asked “What is this?”. I said it was Shark fin soup. Here, this
sometimes is Lobster, sometimes Shark Soup. The meaning of this
is always accompanied by context (context) – table where two people sit, and Food and the body language of his girlfriend. The same is true in Javascript.
Example 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">function</span> <span class="token function">greeting</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'Quan Tien'</span> <span class="token punctuation">;</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">'Hi'</span> <span class="token punctuation">,</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> name <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// console.log(this.name === global.name) // log ra true nếu chạy trên nodejs</span> <span class="token comment">// console.log(this.name === window.name) // log ra true nếu chạy trên browser</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'Vinh'</span> <span class="token punctuation">;</span> <span class="token function">greeting</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// 'Hi Vinh' ???</span> <span class="token comment">// `this` ở đây không phải là `greeting()` mà là `global/window object`</span> |
Example 2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">function</span> <span class="token function">greeting</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'Quan Tien'</span> <span class="token punctuation">;</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">sayHi</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">sayHi</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">'Hi'</span> <span class="token punctuation">,</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> name <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'Vinh'</span> <span class="token punctuation">;</span> <span class="token function">greeting</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// 'Hi Vinh' ???</span> <span class="token comment">// Hiển nhiên, `this` ở đây không phải là `sayHi()`.</span> <span class="token comment">// Hàm sayHi() được gọi trong scope của `greeting()` nhưng</span> <span class="token comment">// `this` không phải là `greeting()` mà vẫn là `global/window object`.</span> |
Through the above example, we can see that this
is really only a constraint that is created until the function is called, and what it references is determined by the call-site where the function is called. So what is the Call site
?
Call site?
The call site is where the function is called , not where it is declared. So where is the function called?
Call stack is a concept that indicates the position of the thread when the program is executing. When the function is called (call), it is stacked into piles 1 (stack). The call-stack will push the function ( push ) when it is called ( call ) and throw the function ( pop ) off the stack when the function returns .
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">function</span> <span class="token function">baz</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// call-stack là: `baz`</span> <span class="token comment">// call-site sẽ là trong global scope</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">"baz"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">bar</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// <-- call-site cho `bar`</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">bar</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// call-stack là: `baz` -> `bar`</span> <span class="token comment">// call-site là trong `baz`</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">"bar"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// <-- call-site cho `foo`</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// call-stack là: `baz` -> `bar` -> `foo`</span> <span class="token comment">// call-site trong `bar`</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">"foo"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">baz</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// <-- call-site cho `baz`</span> |
The rules apply to this
Rule 1 – New binding (Appears keywords new
):
this
is the newly created object with the new
keyword.
1 2 3 4 5 6 | <span class="token keyword">function</span> <span class="token function">foo</span> <span class="token punctuation">(</span> a <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> a <span class="token operator">=</span> a <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> bar <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">foo</span> <span class="token punctuation">(</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> bar <span class="token punctuation">.</span> a <span class="token comment">// 2</span> |
When we call a function with the keyword new
, the following will be done:
- Create a new object.
- Link this new object to another object.
this
is bound to the newly created object in step 1.- Returns
this
if the function does not return the object.
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">function</span> <span class="token function">foo</span> <span class="token punctuation">(</span> a <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> a <span class="token operator">=</span> a <span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> bar <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">foo</span> <span class="token punctuation">(</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> bar <span class="token punctuation">.</span> a <span class="token comment">// undefined</span> <span class="token comment">// Do hàm trả về một object nên this ở đây là foo,</span> <span class="token comment">// không phải là object bar vừa tạo nên kết quả là undefined</span> |
Rule 2 – Explicit binding:
this
is an object specified. Is the function called with call , apply or bind ?
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">function</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> a <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> a <span class="token punctuation">:</span> <span class="token number">2</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token comment">// this được chỉ định rõ là obj bằng từ khóa `call`</span> <span class="token keyword">var</span> bar <span class="token operator">=</span> foo <span class="token punctuation">.</span> <span class="token function">call</span> <span class="token punctuation">(</span> obj <span class="token punctuation">)</span> <span class="token punctuation">;</span> bar <span class="token punctuation">.</span> a <span class="token punctuation">;</span> <span class="token comment">//2</span> |
Distinguish call , appy and bind
- call : call the function immediately and allow pass arguments one by one.1234567891011<span class="token keyword">var</span> member <span class="token operator">=</span> <span class="token punctuation">{</span>name <span class="token punctuation">:</span> <span class="token string">'Quan Tien'</span><span class="token punctuation">}</span> <span class="token punctuation">;</span><span class="token keyword">function</span> <span class="token function">greeting</span> <span class="token punctuation">(</span> text1 <span class="token punctuation">,</span> text2 <span class="token punctuation">)</span> <span class="token punctuation">{</span>console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token string">`</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> text1 <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">, </span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> name <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">. </span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> text2 <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">.`</span></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span><span class="token punctuation">}</span>greeting <span class="token punctuation">.</span> <span class="token function">call</span> <span class="token punctuation">(</span> member <span class="token punctuation">,</span> <span class="token string">'Hello'</span> <span class="token punctuation">,</span> <span class="token string">'Nice to meet you'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span><span class="token comment">// Hello, Quan Tien. Nice to meet you.</span>
- apply : call the function immediately like a call, except that it allows pass an array with one or more elements.1234567891011<span class="token keyword">var</span> member <span class="token operator">=</span> <span class="token punctuation">{</span>name <span class="token punctuation">:</span> <span class="token string">'Quan Tien'</span><span class="token punctuation">}</span> <span class="token punctuation">;</span><span class="token keyword">function</span> <span class="token function">greeting</span> <span class="token punctuation">(</span> text1 <span class="token punctuation">,</span> text2 <span class="token punctuation">)</span> <span class="token punctuation">{</span>console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token string">`</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> text1 <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">, </span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> name <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">. </span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> text2 <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">.`</span></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span><span class="token punctuation">}</span>greeting <span class="token punctuation">.</span> <span class="token function">apply</span> <span class="token punctuation">(</span> member <span class="token punctuation">,</span> <span class="token punctuation">[</span> <span class="token string">'Hello'</span> <span class="token punctuation">,</span> <span class="token string">'Nice to meet you'</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span><span class="token comment">// Hello, Quan Tien. Nice to meet you.</span>
- bind : do not call the function right away but return a new function.123456789101112var member = {name: 'Quan Tien'};function greeting(text1, text2) {console.log(`${text1}, ${this.name}. ${text2}.`);}// `bind` trả về một function. Gán function này với sayHi.var sayHi = greeting.bind(member, 'Hello', 'Nice to meet you');sayHi(); // Hello, Quan Tien. Nice to meet you.
Rule 3 – Implicit binding:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">function</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> a <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> a <span class="token punctuation">:</span> <span class="token number">2</span> <span class="token punctuation">,</span> foo <span class="token punctuation">:</span> foo <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">var</span> bar <span class="token operator">=</span> obj <span class="token punctuation">.</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> bar <span class="token punctuation">;</span> <span class="token comment">//2</span> |
The first thing to note is that the function foo () is initialized, then referenced by an attribute in obj, we understand that the function is surrounded by an object (in this case, obj).
Call site uses obj to refer to the function, we understand this rule is obj will be referenced to this when the function foo is called, or can understand this.a here is obj.a here.
Also remember that only the last obj in the calling string will be referenced as in the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">function</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> a <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> obj2 <span class="token operator">=</span> <span class="token punctuation">{</span> a <span class="token punctuation">:</span> <span class="token number">42</span> <span class="token punctuation">,</span> foo <span class="token punctuation">:</span> foo <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">var</span> obj1 <span class="token operator">=</span> <span class="token punctuation">{</span> a <span class="token punctuation">:</span> <span class="token number">2</span> <span class="token punctuation">,</span> obj2 <span class="token punctuation">:</span> obj2 <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">var</span> bar <span class="token operator">=</span> obj1 <span class="token punctuation">.</span> obj2 <span class="token punctuation">.</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> bar <span class="token punctuation">;</span> <span class="token comment">//42 this sẽ tham chiếu đến obj2 thay vì là obj1</span> |
Rule 4 – Default Binding:
this
is window object (browser) or global object (nodejs) or undefined (use strict) .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token comment">// Xét TH 1: Dùng var</span> <span class="token keyword">function</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> a <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">2</span> <span class="token punctuation">;</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">//2</span> <span class="token comment">// `this` được trỏ tới global/window object.</span> global <span class="token operator">===</span> <span class="token keyword">this</span> <span class="token punctuation">;</span> <span class="token comment">// true</span> <span class="token comment">// Dùng var thì biến name sẽ được thêm vào properties của global/window object.</span> global <span class="token punctuation">.</span> a <span class="token operator">===</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> a <span class="token punctuation">;</span> <span class="token comment">// true</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token comment">// Xét TH 1: Dùng let</span> <span class="token keyword">function</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> a <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">let</span> a <span class="token operator">=</span> <span class="token number">2</span> <span class="token punctuation">;</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">//2</span> <span class="token comment">// `this` được trỏ tới global/window object.</span> global <span class="token operator">===</span> <span class="token keyword">this</span> <span class="token punctuation">;</span> <span class="token comment">// true</span> <span class="token comment">// Dùng `let` thì biến `name` sẽ không được thêm vào properties của `global/window object`.</span> global <span class="token punctuation">.</span> a <span class="token operator">===</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> a <span class="token punctuation">;</span> <span class="token comment">// false</span> |
1 2 3 4 5 6 7 8 9 10 11 | <span class="token comment">// Xét TH 3: strict mode</span> <span class="token keyword">function</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token string">"use strict"</span> <span class="token punctuation">;</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> a <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">2</span> <span class="token punctuation">;</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// TypeError: Cannot read property 'a' of undefined</span> <span class="token comment">// Nếu có `use strict` thì giá trị của `this` sẽ là undefined</span> |
summary
this
binding depends on the context .Call site
is where the function is called.Call stack
is a concept that indicates the position of the thread when the program is executing (execution).Bốn quy tắc
in order of precedence determine this:- Keyword
new
. Explicit binding
(binding document): this is called specific object with the call, apply and bind.Implicit binding
(binding offline): this is the object containing context.Default Binding
: this default is global / window object or undefined if strict use is used.
- Keyword