# 原型鏈(Prototype Chain) 在 [JavaScript 繼承機制](https://hackmd.io/@allenliao/H18vBKOnO) 中,prototype 扮演很重要的角色,然而什麼叫做 Prototype Chain 呢? ## prototype 與 \_\_proto\_\_ :::warning 現在更好的用法是 `Object.getPrototypeOf()`,雖然有些瀏覽器還是支援 `__proto__`,但應該盡量避免使用,詳情請見 [Object.prototype.\__proto__](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) ::: 先來看看這個例子: ```javascript function Person(name) { this.name = name } Person.prototype.sayHello = function() { console.log(this.name + ': hello~') } var p1 = new Person('allen') var p2 = new Person('kaneshiro') p1.sayHello() // allen: hello~ p2.sayHello() // kaneshiro: hello~ console.log(p1.sayHello === p2.sayHello) // true ``` 我們有一個建構函式 `Person`,且它有兩個 instance `p1` 跟 `p2`,還有一個放在 `Person.prototype` 裡面的方法 `sayHello`,這樣可以讓 `p1` 跟 `p2` 共用 `sayHello` 這個方法。 但是我們明明是把 `sayHello` 放在 `Person.prototype`,為什麼 `p1` 跟 `p2` 這樣就可以用了呢? 其實當我們 `new` 了一個新的 instance 時,會發生以下幾件事: 1. 建立一個空白的 JavaScript 物件 2. 新增 `__proto__` 屬性到該新物件,這個屬性會指向建構函式的 `prototype` 物件 3. 把 `this` 綁定到該新物件,也就是建構函式中的 `this` 會指向該新物件 4. 如果函式沒有 return 任何東西,就會 return `this` 所以 `p1.__proto__` 會指向 `Person.prototype`,當 JavaScript 在 `p1` 找不到 `sayHello` 這個方法,就會透過 `p1.__proto__` 找到 `Person.prototype`,並在 `Person.prototype` 找到 `sayHello`。 假設在 `Person.prototype` 還是沒有找到,就會在往 `Person.prototype.__proto__` 去找,直到某個東西的 `__proto__` 是 `null` 為止,代表找到最上層了。這樣一直用點相連起來的鏈結,就叫做原型鏈(Prototype Chain) 寫成程式碼應該會比較有感覺: ```javascript function Person(name) { this.name = name } Person.prototype.sayHello = function() { console.log(this.name + ': hello~') } var p1 = new Person('allen') console.log(p1.__proto__ === Person.prototype) // true console.log(Person.prototype.__proto__ === Object.prototype) //true console.log(Object.prototype.__proto__) // null ``` ### hasOwnProperty 而要知道一個屬性或方法是存在 instance 自己裡面還是在他的原型鏈上,可以用 `hasOwnProperty` 這個方法來判斷 ```javascript function Person(name) { this.name = name } Person.prototype.sayHello = function() { console.log(this.name + ': hello~') } var p1 = new Person('allen') console.log(p1.hasOwnProperty('sayHello')) // false console.log(p1.__proto__.hasOwnProperty('sayHello')) // true ``` 如此可以知道 `sayHello` 是 `p1` 的原型鏈上的方法,而不是自己的方法。 我們也可以自己寫一段程式碼,來找到這個 `sayHello` 方法並執行他: ```javascript function Person(name) { this.name = name } Person.prototype.sayHello = function() { console.log(this.name + ': hello~') } var p1 = new Person('allen') function call(obj, methodName) { var methodOwner = obj // 不斷往上找,直到找到真正擁有這個 method 的人,或 methodOwner 已經是 null(到最上層了) while(methodOwner && !methodOwner.hasOwnProperty(methodName)) { methodOwner = methodOwner.__proto__ } // 找不到 method 時 methodOwner 會是 null,印出資訊並 return if (!methodOwner) return console.log('method not found') // 找到 method,用 apply 的方式來呼叫,this 的值才會正確 methodOwner[methodName].apply(obj) } call(p1, 'sayHello') // allen: hello~ call(p1, 'sayByeBye') // method not found ``` 現在可以思考看看,`Person.__proto__` 會是什麼? 來人上程式碼 ```javascript function Person(name) { this.name = name } Person.prototype.sayHello = function() { console.log(this.name + ': hello~') } var p1 = new Person('allen') console.log(Person.__proto__ === Function.prototype) // true ``` 其實 `Person` 就是 `Function` 的 instance 啦!所以 `Person.__proto__` 就是 `Function.prototype`! ### instanceof 用 `A instanceof B` 可以判斷 A 不不是 B 的 instance。 ```javascript function Person(name) { this.name = name } Person.prototype.sayHello = function() { console.log(this.name + ': hello~') } var p1 = new Person('allen') console.log(p1 instanceof Person) // true console.log(Person instanceof Object) // true console.log(Person instanceof Function) // true console.log(Person instanceof Array) // false ``` 其實原理也很簡單,只要檢查 A 的原型鏈裡有沒有 B 的 prototype 就好了,像是這樣: ```javascript function Person(name) { this.name = name } Person.prototype.sayHello = function() { console.log(this.name + ': hello~') } var p1 = new Person('allen') function instanceOf(A, B) { // 找到最上層了還是找不到 if(!A) return false // 找不到的話,就往 A 的上層繼續找 return A.__proto__ === B.prototype ? true : instanceOf(A.__proto__, B) } ``` ### constructor 每個 `prototype` 還有一個屬性叫做 `constructor`,這個屬性會指向建構函式,也就是說 `Person.prototype.constructor` 就是 `Person`。 ```javascript function Person(name) { this.name = name } Person.prototype.sayHello = function() { console.log(this.name + ': hello~') } var p1 = new Person('allen') console.log(Person.prototype.constructor === Person) // true ``` ## 參考資料 [該來理解 JavaScript 的原型鍊了](https://blog.huli.tw/2017/08/27/the-javascripts-prototype-chain/) [new operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new) [Object.prototype.\__proto__](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) ###### tags: `Lidemy-MTR05`, `Week16`
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up