原由是被學妹問起原型鏈,發現只是曾經記得,一些概念不敢咬的那麼準了,因而決定溫故一下,力求能知新~~es6
從如下幾個方面着手數組
1.對象字面量app
var o1 = {name: 'o1'}
var o2 = new Object({name: 'o2'})
複製代碼
2.經過構造函數函數
var M = function(name){
this.name = name
}
var o3 = new M('o3')
複製代碼
3.Object.create優化
var o4 = Object.create(p)
複製代碼
記憶老是有規律的,如高中時期學的三角函數,須要背公式不少,強行去背所有的公式是容易混亂的。不過若是把核心的幾點背牢,其他的公式只須要稍加推導便可。關於原型鏈也是同樣,有幾點在最開始就記住的話,後面就不會亂了。原型鏈中關鍵概念:構造函數 實例 constructor __ proto__ prototype, 首先要記住他們的關係this
上面3點請先牢記,後面所總結的完整繼承和這有緊密的關聯spa
其實 構造函數 實例 constructor __ proto__ prototype 的關係已經在上面的例子和3點介紹中介紹完了。不妨再回顧一下prototype
構造函數即普通函數,只不過前邊有 new 關鍵字code
經過 new 加 構造函數 ,生成的對象即爲實例。對象
以上面生成o3實例爲例子
o3.proto === M.prototype //true o3.prototype //undefined o3.proto === M.prototype //true
o3實例自己並沒有constructor,不過會藉助原型鏈向上查找,即,
o3.constructor === M.prototype.constructor // true o3.constructor === M //true
小結 理清這幾個關鍵詞的關係後,原型鏈就明朗不少了
instanceof 的原理是什麼呢? 先來看一下使用
[] instanceof Array // true
複製代碼
即左邊是對象,右邊是類型,instanceof 就是要判斷右邊類型的prototype,是否在左邊實例的原型鏈上,以下例子所示
[].__proto__ === Array.prototype //true
Array.prototype.__proto__ === Object.prototype //true
Object.prototype__proto__ //undefined
複製代碼
那麼依據這個思想來實現一下instanceof吧,必定會印象更加深入
function myInstanceof2(left, right){
if(left === null || left === undefined){
return false
}
if(right.prototype === left) {
return true
}
left = left.__proto__
return myInstanceof2(left, right)
}
console.log(myInstanceof2([], Array))
複製代碼
new的過程發生了什麼?
生成空對象
這個空對象的__proto__賦值爲構造函數的prototype
綁定this指向
返回這個對象
// 構造函數 function M(name){ this.name = name } // 原生new var obj = new M('123')
// 模擬實現 function create() { // 生成空對象 let obj = {} // 拿到傳進來參數的第一項,並改變參數類數組 let Con = [].shift.call(arguments) // 對空對象的原型指向賦值 obj.proto = Con.prototype // 綁定this (對應下面使用來講明:Con是參數第一項M,arguments是參數['123'],就是 M方法執行,參數是'123',執行這個函數的this是obj) let result = Con.apply(obj, arguments) return result instanceof Object ? result : obj }
var testObj = create(M, '123') console.log('testObj', testObj)
一步一步來,從簡到繁,更能直觀發現繼承的原理與缺點
構造方法方式 核心 Parent1.call(this)
// 構造方法方式 function Parent1(){ this.name = 'Parent1' } Parent1.prototype.say = function () { alert('say') } function Child1(){ Parent1.call(this) this.type = 'type' }
var c1 = new Child1() c1.say() //報錯
缺點: 只能繼承父類構造函數內部屬性,沒法繼承父類構造函數原型對象上屬性
思考: 爲何 call 實現了繼承,call本質是什麼?
只借助原型繼承 核心 Child2.prototype = new Parent2()
// 原型 function Parent2(){ this.name = 'Parent2' this.arr = [1,2] } Parent2.prototype.say = function () { alert('say') } function Child2(){ // Parent2.call(this) this.type = 'type' } Child2.prototype = new Parent2()
var c21 = new Child2() var c22 = new Child2()
c21.say() c21.arr.push('9') console.log('c21.arr : ', c21.arr) console.log('c22.arr : ', c22.arr)
缺點: c21.arr 與c22.arr對應的是同一個引用
思考:爲何這麼寫是同一個引用?
把上面兩個繼承方式的優勢合併起來,缺點都拋棄掉
function Parent3(){
this.name = 'Parent3'
this.arr = [1,2]
}
Parent3.prototype.say = function () {
alert('say')
}
function Child3(){
Parent3.call(this)
this.type = 'type'
}
Child3.prototype = new Parent3()
var c31 = new Child3()
var c32 = new Child3()
c31.say()
c31.arr.push('9')
console.log('c31.arr : ', c31.arr)
console.log('c31.arr : ', c32.arr)
複製代碼
思考: 這麼寫就沒有問題了嗎?
答 : 生成一個實例要執行 Parent3.call(this) , new Child3(),也就是Parent3執行了兩遍。
組合繼承2 改變上例子 的
Child3.prototype = new Parent3()
爲
Child3.prototype = Parent3.prototype
複製代碼
缺點 : 很明顯,沒法定義子類構造函數原型私有的方法
組合繼承優化3 再次改變上例子 的
Child3.prototype = Parent3.prototype
爲
Child3.prototype = Object.create(Parent3.prototype)
複製代碼
問題就都解決了。 由於Object.create的原理是:生成一個對象,這個對象的__proto__, 指向所傳的參數。
思考 :是否還有疏漏?一時想不起來的話,能夠看下這幾個結果
console.log(c31 instanceof Child3)
console.log(c31 instanceof Parent3)
console.log(c31.constructor === Child3)
console.log(c31.constructor === Parent3)
複製代碼
因此回想起文章開頭所說的那幾個須要牢記的點,就須要從新賦值一會兒類構造函數的constructor: Child3.prototype.constructor = Child3,完整版以下
function Parent3(){
this.name = 'Parent3'
this.arr = [1,2]
}
Parent3.prototype.say = function () {
alert('say')
}
function Child3(){
Parent3.call(this)
this.type = 'type'
}
Child3.prototype = Object.create(Parent3.prototype)
Child3.prototype.constructor = Child3
var c31 = new Child3()
var c32 = new Child3()
c31.say()
c31.arr.push('9')
console.log('c31.arr : ', c31.arr)
console.log('c31.arr : ', c32.arr)
console.log('c31 instanceof Child3 : ', c31 instanceof Child3)
console.log('c31 instanceof Parent3 : ', c31 instanceof Parent3)
console.log('c31.constructor === Child3 : ', c31.constructor === Child3)
console.log('c31.constructor === Parent3 : ', c31.constructor === Parent3)
複製代碼
es6的繼承後續填補~~~