回到文章列表

    [JS201 筆記] 物件導向基礎與 prototype

    TWGD / 2019-01-07 / 閱讀時間約 1 分鐘

    這是 Lidemy 課程:[JS201] 進階 JavaScript:那些你一直搞不懂的地方:物件導向基礎與 prototype 課程筆記。

    如果有人不小心看到,請不要太嚴格,這只是我上課的隨筆筆記XD 若有錯誤,請不要吝嗇糾正我,謝謝~


    ES6 的物件導向範例:

    ES6 才有 class 這個關鍵字。

    class Dog {
      // 名稱開頭通常大寫
    
      // 建構子,作為初始化
      constructor(name) {
        this.name = name
      }
    
      // setter 用來設置值
      setName(name) {
        this.name = name
      }
    
      // getter 用來存取值
      getName() {
        return this.name
      }
    
      sayHello() {
        console.log('hello' + this.name)
      }
    }
    
    let cat = new Dog('cat') // cat 是 Dog 的 instance
    cat.getName()
    cat.sayHello()

    ES5 的物件導向:

    function Dog(name) {
      var myName = name
    
      return {
        getName: function () {
          return myName
        },
        sayHello: function () {
          console.log('hello' + myName)
        },
      }
    }
    
    var cat = Dog('cat')
    cat.getName()
    cat.sayHello()
    
    var cat2 = Dog('cat2')
    cat2.getName()
    cat2.sayHello()

    但是以上會有一個問題:cat.getName() !== cat2.getName()。 每創建一個 instance,就要耗費一個記憶體空間。 因此改用以下的寫法:

    // 等同於建構子的作用
    function Dog(name) {
      this.name = name
    }
    
    // 設定 method
    Dog.prototype.getName = function () {
      return this.name
    }
    
    ;(Dog.prototype,
      (sayHello = function () {
        console.log('hello' + this.name)
      }))
    
    // new 創建 Dog 的 instance
    var cat = new Dog('cat')
    console.log(cat) // Dog {name: 'cat'}
    cat.sayHello() // hello cat
    
    var cat2 = new Dog('cat2')
    console.log(cat2) // Dog {name: 'cat'}
    cat2.sayHello() // hello cat2
    
    console.log(cat.sayHello === cat2.sayHello) // true

    從 prototype 來看「原型鍊」

    與 javascript 提供的屬性 __proto__ 有關:instance 透過 __proto__ 屬性得以連結 prototype

    從以下例子理解 prototype chain 原型鍊:

    function Dog(name) {
      this.name = name
    }
    
    Dog.prototype.getName = function () {
      return this.name
    }
    
    Dog.prototype.sayHello = function () {
      console.log('hello ' + this.name)
    }
    
    var cat = new Dog('cat')
    var cat2 = new Dog('cat2')
    
    console.log(cat.__proto__ === Dog.prototype) // true
    console.log(cat.__proto__.__proto__ === Object.prototype) // true
    console.log(cat.__proto__.__proto__.__proto__ === null) // true
    
    cat.sayHello()
    /*
    prototype chain 原型鍊
    呼叫 cat.sayHello() 的時候,找 sayHello() 的順序
    1. cat 身上有沒有 sayHello
    2. cat.__proto__ 有沒有 sayHello
    3. cat.__proto__.__proto__ 有沒有 sayHello
    4. cat.__proto__.__proto__.__proto__ 有沒有 sayHello
    5. null (找到頂了)
    */
    
    // 順便看一下 Dog 的 __proto__ 是什麼
    console.log(Dog.__proto__ === Function.prototype) // true

    prototype 的其他應用:

    // 自訂 method 讓其他 string 使用
    String.prototype.first = function () {
      return this[0]
    }
    
    var a = '123'
    console.log(a.first()) // 1

    new 底層做了什麼

    先有 call() 的基本觀念:

    function test() {
      console.log(this)
    }
    
    // call 裡面傳什麼東西,this 就會是什麼
    test.call('123') // [String: '123']

    以下實作 new 的原理:

    function Dog(name) {
      this.name = name
    }
    
    Dog.prototype.getName = function () {
      return this.name
    }
    
    Dog.prototype.sayHello = function () {
      console.log('hello ' + this.name)
    }
    
    var cat = new Dog('cat')
    
    /*
    實作一個 function newDog(){...},來執行以下程式碼:
    var cat2 = newDog('cat2');
    cat2.sayHello();
    */
    
    function newDog(name) {
      var obj = {} // 產生一個 obj
      Dog.call(obj, name) // call constructor 完成初始化物件
      obj.__proto__ = Dog.prototype // 把 obj 關聯到 Dog 的 prototype
      return obj
    }

    物件導向的繼承 Inheritance

    繼承用在哪裡:若有共同的屬性可以直接繼承上層的,不用重複寫。

    • 注意 super() 的用法。
    class Dog {
      constructor(name) {
        this.name = name
      }
    
      sayHello() {
        console.log('hello ' + this.name)
      }
    }
    
    class BlackDog extends Dog {
      constructor(name) {
        // super() 呼叫上層得已初始化。強制使用 super()。傳參數進去給上層的 constructor 使用。
        super(name) // Dog 的 constructor。
        this.sayHello() // 初始化的時候就 say Hello
      }
    
      test() {
        console.log('test:', this.name)
      }
    }
    
    const black = new BlackDog('black') // hello black
    black.test() // test: black