Độc lạ JavaScript

Độc lạ JavaScript

Ly Nhan

2 years 5 min read

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:

  1. Nếu function được gọi như là method của object: this sẽ là instance của object gọi function này.

  2. Nếu gọi thông qua constructor ( new): this sẽ là object rỗng.

  3. Nếu gọi như là một function thông thường: this sẽ là global.

  4. 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 qua this

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)

High level experience in web design and development knowledge, producing quality work.

© Nextlint_2023 All Rights Reserved

Privacy Policy Terms of Use