
Độc lạ JavaScript
Một câu hỏi nhanh, với đoạn code sau theo các bạn khi click vào thì kết quả sẽ là gì?
Updated
<body>
<button id="test">click me</button>
<button id="check">check</button>
<script>
function Button() {
this.clicked = false;
this.click = function () {
this.clicked = true;
};
}
const button = Button();
const ele = document.getElementById("test");
//1. what happen?
ele.addEventListener("click", button.click);
document.getElementById("check").onclick = function () {
// 2. output = ?
console.log(button.clicked === true);
};
</script>
</body>
Lần lượt các kết quả của (1) và (2) sẽ là gì ?
Update Result:
1. Khi bạn click vào button#test
điều gì sẽ xảy ra:
Javascript runtime sẽ throw error, vì ở trong function Button bạn sử dụng this
nhưng khi khởi tạo instance của Button bạn không sử dụng new
.
Trong gia javascript, giá trị của this
sẽ phụ thuộc vào cách mà function được gọi:
Nếu function được gọi như là method của object:
this
sẽ là instance của object gọi function này.Nếu gọi thông qua constructor (
new
):this
sẽ là object rỗng.Nếu gọi như là một function thông thường:
this
sẽ là global.Nếu gọi thông qua
call/apply
:this
sẽ là tham số đầu tiên được truyền vào.
Lỗi ở trên do hơi vào trường hợp số 3, this
là window và function Button cũng không return về object nào chứa method onclick nên function truyền vào trong callback sẽ là undefined => khi trigger sẽ gây ra lỗi.
Để fixed case này mình sẽ khởi tạo button bằng cách gọi function Button với từ keyword new
Khi khởi tạo function với từ khoá
new
behind the scenes Javascript sẽ khởi bạo một Object mới rỗng, và context của function này sẽ là vừa mới được tạo trước đó và có thể truy xuất được thông quathis
2.Output số 2 kết quả sẽ là false
:
Ở trên sau khi fix, this
lúc này đã được khởi tạo và chứa 2 fields:
function Button() {
this.clicked = false;
this.click = function () {
this.clicked = true;
};
}
const button = new Button();
//{ clicked: false, click: function}
ele.addEventListener("click", button.click);
Vậy khi button#test
được click theo như mình mong muốn thì field this.clicked=true
thì button.click = true
?
Nah thực tế thì là không: button.click
là một anonymous function, khi được add vào event callback và được trigger thì this
lúc này lại là instance của button#test
.
//...
this.click = function () {
this.clicked = true; //=> this lúc này sẽ là button element
};
//....
Để fix trường hợp này mình sẽ update click thành arrow function:
//...
this.click = () => {
this.clicked = true;
//=> this lúc này sẽ luôn trỏ về instance của Button
};
//....
NOTED:
Context của arrow function khi được khởi tạo sẽ không có this, thay vào đó giá trị this sẽ được trỏ về context tại thời điểm arrow function được khởi tạo, ở đây là context của Button.
References:
Secrets of the JavaScript Ninja(P.105)