有些知識當時實在看不懂的話,可以先暫且放下,留在以后再看也許就能看懂了。
幾個月前,抱著《JavaScript 高級程序設計(第三版)》,啃完創(chuàng)建對象,就開始啃起了 繼承 ,然而啃完 原型鏈 就實在是看不下去了,腦子越來越亂,然后就把它扔一邊了,繼續(xù)看后面的。現(xiàn)在利用這個暑假搞懂了這個繼承,就把筆記整理一下啦。
原型鏈(Prototype Chaining)
先看一篇文章,文章作者講的非常不錯,并且還配高清套圖哦。lol…
從原文中小摘幾句
確定原型與實例的關系
有兩種方法來檢測原型與實例的關系:
instanceof :判斷該對象是否為另一個對象的實例
instanceof 內(nèi)部運算機理如下:
functioninstance_of(L, R){//L 表示左表達式,R 表示右表達式varO = R.prototype;// 取 R 的顯示原型 L = L.__proto__; // 取 L 的隱式原型while(true) {if(L ===null)returnfalse;if(O === L)// 這里重點:當 O 嚴格等于 L 時,返回 truereturntrue; L = L.__proto__; } }上面代碼摘自: JavaScript instanceof 運算符深入剖析
isPrototypeOf() :測試一個對象是否存在于另一個對象的原型鏈上
這兩個方法的不同點請參看: JavaScript isPrototypeOf vs instanceof usage
只利用原型鏈實現(xiàn)繼承
缺點:1. 引用類型值的原型屬性會被實例共享; 2. 在創(chuàng)建子類型的實例時,不能向超類型的構造函數(shù)中傳遞參數(shù)
functionFather(){this.name ="father";this.friends = ['aaa','bbb'];}functionSon(){}Son.prototype = newFather();Son.prototype.constructor = Son;vars1 =newSon();vars2 =newSon();console.log(s1.name);// fatherconsole.log(s2.name);// fathers1.name = "son";console.log(s1.name);// sonconsole.log(s2.name);// fatherconsole.log(s1.friends);// ["aaa", "bbb"]console.log(s2.friends);// ["aaa", "bbb"]s1.friends.push('ccc','ddd');console.log(s1.friends);// ["aaa", "bbb", "ccc", "ddd"]console.log(s2.friends);// ["aaa", "bbb", "ccc", "ddd"]只利用構造函數(shù)實現(xiàn)繼承
實現(xiàn)方法:在子類型構造函數(shù)的內(nèi)部調(diào)用超類型構造函數(shù)(使用 apply() 和 call() 方法)
優(yōu)點:解決了原型中引用類型屬性的問題,并且子類可以向超類中傳參
缺點:子類實例無法訪問父類(超類)原型中定義的方法,所以函數(shù)復用就無從談起了。
functionFather(name,friends){this.name = name;this.friends = friends;}Father.prototype.getName = function(){returnthis.name;};functionSon(name){// 注意: 為了確保 Father 構造函數(shù)不會重寫 Son 構造函數(shù)的屬性,請將調(diào)用 Father 構造函數(shù)的代碼放在 Son 中定義的屬性的前面。 Father.call(this,name,['aaa','bbb']);this.age =22;}vars1 =newSon('son1');vars2 =newSon('son2');console.log(s1.name);// son1console.log(s2.name);// son2s1.friends.push('ccc','ddd');console.log(s1.friends);// ["aaa", "bbb", "ccc", "ddd"]console.log(s2.friends);// ["aaa", "bbb"]// 子類實例無法訪問父類原型中的方法s1.getName(); // TypeError: s1.getName is not a functions2.getName(); // TypeError: s2.getName is not a function組合繼承(Combination Inheritance)
實現(xiàn)方法:使用原型鏈實現(xiàn)對原型屬性和方法的繼承,而通過借用構造函數(shù)來實現(xiàn)對實例屬性的繼承。
functionFather(name,friends){this.name = name;this.friends = friends;}Father.prototype.money = "100k $";Father.prototype.getName = function(){console.log(this.name);};functionSon(name,age){// 繼承父類的屬性 Father.call(this,name,['aaa','bbb']);this.age = age;}// 繼承父類原型中的屬性和方法Son.prototype = newFather();Son.prototype.constructor = Son;Son.prototype.getAge = function(){console.log(this.age);};vars1 =newSon('son1',12);s1.friends.push('ccc');console.log(s1.friends);// ["aaa", "bbb", "ccc"]console.log(s1.money);// 100k $s1.getName(); // son1s1.getAge(); // 12vars2 =newSon('son2',24);console.log(s2.friends);// ["aaa", "bbb"]console.log(s2.money);// 100k $s2.getName(); // son2s2.getAge(); // 24組合繼承避免了單方面使用原型鏈或構造函數(shù)來實現(xiàn)繼承的缺陷,融合了它們的優(yōu)點,成為 JavaScript 中最常用的繼承模式,但是它也是有缺陷的,組合繼承的缺陷會在后面專門提到。
原型式繼承(Prototypal Inheritance)
實現(xiàn)思路:借助原型基于已有的對象創(chuàng)建新對象,同時不必因此而創(chuàng)建自定義類型。
為了達到這個目的,引入了下面的函數(shù)(obj)
functionobj(o){functionF(){} F.prototype = o;returnnewF();}varperson1 = { name: "percy", friends: ['aaa','bbb']};varperson2 = obj(person1);person2.name = "zyj";person2.friends.push('ccc');console.log(person1.name);// percyconsole.log(person2.name);// zyjconsole.log(person1.friends);// ["aaa", "bbb", "ccc"]console.log(person2.friends);// ["aaa", "bbb", "ccc"]ECMAScript 5 通過新增 Object.create() 方法規(guī)范化了原型式繼承。在傳入一個參數(shù)的情況下, Object.create() 和 obj() 方法的行為相同。varperson1 = { name: "percy", friends: ['aaa','bbb']};varperson2 =Object.create(person1);person2.name = "zyj";person2.friends.push('ccc');console.log(person1.name);// percyconsole.log(person2.name);// zyjconsole.log(person1.friends);// ["aaa", "bbb", "ccc"]console.log(person2.friends);// ["aaa", "bbb", "ccc"]在沒有必要興師動眾地創(chuàng)建構造函數(shù),而只想讓一個對象與另一個對象保持類似的情況下,可以選擇使用這種繼承。
寄生式繼承(Parasitic Inheritance)
寄生式繼承是與原型式繼承緊密相關的一種思路。
實現(xiàn)思路:創(chuàng)建一個僅僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部以某種方式來增強對象,最后再返回對象。
functionobj(o){functionF(){} F.prototype = o;returnnewF();}functioncreatePerson(original){// 封裝繼承過程varclone = obj(original);// 創(chuàng)建對象 clone.showSomething = function(){// 增強對象console.log("Hello world!"); };returnclone;// 返回對象}varperson = { name: "percy"};varperson1 = createPerson(person);console.log(person1.name);// percyperson1.showSomething(); // Hello world!寄生組合式繼承(Parasitic Combination Inheritance)
先來說說我們前面的組合繼承的缺陷。 組合繼承最大的問題就是無論什么情況下,都會調(diào)用兩次父類的構造函數(shù):一次是創(chuàng)建子類的原型的時候,另一次是在調(diào)用子類構造函數(shù)的時候,在子類構造函數(shù)內(nèi)部又調(diào)用了父類的構造函數(shù)。
functionFather(name,friends){this.name = name;this.friends = friends;}Father.prototype.money = "100k $";Father.prototype.getName = function(){console.log(this.name);};functionSon(name,age){// 繼承父類的屬性 Father.call(this,name,['aaa','bbb']);// 第二次調(diào)用 Father() , 實際是在 new Son() 時才會調(diào)用this.age = age;}// 繼承父類原型中的屬性和方法Son.prototype = newFather();// 第一次調(diào)用 Father()Son.prototype.constructor = Son;第一次調(diào)用使的子類的原型成了父類的一個實例,從而子類的原型得到了父類的實例屬性;第二次調(diào)用會使得子類的實例也得到了父類的實例屬性;而子類的實例屬性默認會屏蔽掉子類原型中與其重名的屬性。所以,經(jīng)過這兩次調(diào)用, 子類原型中出現(xiàn)了多余的的屬性 ,從而引進了寄生組合式繼承來解決這個問題。
寄生組合式繼承的背后思路是: 不必為了指定子類的原型而調(diào)用父類的構造函數(shù),我們所需要的無非就是父類原型的一個副本而已 。
本質(zhì)上,就是使用寄生式繼承來繼承父類的原型,然后將結(jié)果返回給子類的原型。
functionobj(o){functionF(){} F.prototype = o;returnnewF();}functioninheritPrototype(son,father){varprototype = obj(father.prototype);// 創(chuàng)建對象 prototype.constructor = son; // 增強對象 son.prototype = prototype; // 返回對象}functionFather(name,friends){this.name = name;this.friends = friends;}Father.prototype.money = "100k $";Father.prototype.getName = function(){console.log(this.name);};functionSon(name,age){// 繼承父類的屬性 Father.call(this,name,['aaa','bbb']);this.age = age;}// 使用寄生式繼承繼承父類原型中的屬性和方法inheritPrototype(Son,Father);Son.prototype.getAge = function(){console.log(this.age);};vars1 =newSon('son1',12);s1.friends.push('ccc');console.log(s1.friends);// ["aaa", "bbb", "ccc"]console.log(s1.money);// 100k $s1.getName(); // son1s1.getAge(); // 12vars2 =newSon('son2',24);console.log(s2.friends);// ["aaa", "bbb"]console.log(s2.money);// 100k $s2.getName(); // son2s2.getAge(); // 24優(yōu)點:使子類原型避免了繼承父類中不必要的實例屬性。
開發(fā)人員普遍認為寄生組合式繼承是實現(xiàn)基于類型繼承的最理想的繼承方式。
最后
最后,強烈推薦兩篇很硬的文章
Javascript 主站蜘蛛池模板: 陆川县| 友谊县| 双鸭山市| 昭苏县| 济源市| 泰宁县| 松滋市| 大化| 云霄县| 大竹县| 宜城市| 富裕县| 岳西县| 黄山市| 通州市| 高邮市| 通渭县| 西华县| 肃宁县| 增城市| 神池县| 定陶县| 静海县| 鄱阳县| 吉木萨尔县| 永宁县| 福海县| 正定县| 宜兰市| 建宁县| 运城市| 静安区| 寻甸| 湖北省| 方正县| 洛南县| 离岛区| 永寿县| 柯坪县| 离岛区| 隆安县|