--
前言:最近在細讀Javascript高級程序設計,對於我而言,中文版,書中不少地方翻譯的差強人意,因此用本身所理解的,嘗試解讀下。若有紕漏或錯誤,會很是感謝您的指出。文中絕大部份內容引用自《JavaScript高級程序設計第三版》。瀏覽器
道格拉斯 克勞克福德在2006年寫了一篇文章,題爲Prototypal Inhertitance in JavaScript(JavaScript中的原型式繼承)。函數
在這篇文章中,他介紹了一種實現繼承的方法,這種方法並無使用嚴格意義上的構造函數。prototype
他的想法是藉助原型能夠基於已有的對象建立新對象,同時還沒必要所以建立自定義類型。翻譯
爲了達到這個目的,他給出了以下函數。設計
function object(o) { function F(){}; F.prototype = o; return new F(); }
在object()函數內部,先建立一個臨時性的構造函數,而後將傳入的對象做爲這個構造函數的原型,最後返回了這個臨時類型的一個新實例對象。code
從本質上講,object()對傳入的對象執行了一次淺複製。對象
function object(o){ function F(){}; F.prototype = o; return new F(); } var person = { name: "Shaw", friends: ["Sharon", "Sandy", "Van"] } var person1 = object(person); /* person1 = function object(person){ function F(){}; F.prototype = person1; return new F(); }() person1 = function object({ name: "Shaw", friends: ["Sharon", "Sandy", "Van"] }){ function F(){}; F.prototype = { name: "Shaw", friends: ["Sharon", "Sandy", "Van"] } return { } } person1 = { }; {}.__proto__ = { name: "Shaw", friends: ["Sharon", "Sandy", "Van"] } */ person1.name = "Roc"; person1.friends.push("Roster"); var person2 = object(person); person2.name = "Linda"; person2.friends.push("Jobs"); console.log(person.friends); //["Sharon", "Sandy", "Van", "Roster", "Jobs"] console.log(person1.friends); //["Sharon", "Sandy", "Van", "Roster", "Jobs"] console.log(person2.friends); //["Sharon", "Sandy", "Van", "Roster", "Jobs"]
克羅克福德主張的這種原型式繼承,要求你必須有一個對象能夠做爲另外一個對象的基礎。繼承
若是有這麼一個對象的話,能夠把它傳給object()函數,而後再根據具體需求對獲得的對象加以修改便可。ip
ECMAscript5經過新增Object.create()方法規範了原型式繼承。
這個方法接收兩個參數: 一個用做新對象原型的對象和(可選的)一個爲新對象定義額外屬性的對象。原型
在傳入一個參數的狀況下,Object.create()與object()方法的行爲相同。
var person = { name: "Shaw", friends: ["Sharon", "Sandy", "Van"] } var person1 = Object.create(person); person1.name = "Roc"; person1.friends.push("Roster"); var person2 = Object.create(person); person2.name = "Linda"; person2.friends.push("Messi"); console.log(person.friends); //["Sharon", "Sandy", "Van", "Roster", "Messi"] console.log(person1.friends); //["Sharon", "Sandy", "Van", "Roster", "Messi"] console.log(person2.friends); //["Sharon", "Sandy", "Van", "Roster", "Messi"]
Object.create()方法的第二個參數與Object.defienProperties()方法的第二個參數格式相同:
每一個屬性都是經過本身的描述符定義的。
以這種方式指定的任何屬性都會覆蓋原型對象上的同名屬性。
var person = { name: "Shaw", friends: ["Sharon", "Sandy", "Selina"] } var person1 = Object.create(person, { name: { value: "Roc" } }) console.log(person1.name); //"Roc"
支持Object.create()方法的瀏覽器有IE9+, Firefox 4+, Opera 12+ 和 Chrome。
適用場景:
在沒有必要興師動衆地建立構造函數,而只想讓一個對象與另一個對象保持相似的狀況下,原型式繼承是徹底能夠勝任的。
千萬要記住,包含引用類型值的屬性始終都會共享相應的值,就像使用原型模式同樣。
寄生式(parasitic)繼承是與原型式繼承緊密相關的一種思路,而且也是由大神克勞克福推而廣之的。
寄生式繼承的思路與寄生構造函數和工廠模式相似。
建立一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來加強對象,最後再像真地是它作了全部工做同樣返回對象。
function object(o){ function F(){}; F.prototype = o; return new F(); } function createAnother(original) { var clone = object(original); //經過調用函數建立一個新對象 clone.sayHi = function(){ //以某種方式來加強這個對象 console.log("hi"); } return clone; //返回這個對象 }
在這個例子中, createAnother()函數接收了換一個參數,也就是將要做爲新對象基礎的對象。
而後,把這個對象參數(original)傳遞給object()函數, 將返回的結果賦值給clone。
再爲clone對象添加一個新方法sayHi(),最後返回clone對象。
能夠像下面這樣來使用createAnother()函數:
function object(o){ function F(){}; F.prototype = o; return new F(); } function createAnother(original) { var clone = object(original); //經過調用函數建立一個新對象 clone.sayHi = function(){ //以某種方式來加強這個對象 console.log("hi"); } return clone; //返回這個對象 } var person = { name: "Shaw", friends: ["Sandy", "Sharon", "Van"] } var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi"
這個例子中的代碼基於person返回一個新對象——anotherPerson。新對象不只具備person的全部屬性和方法,並且還有還有本身的sayHi()方法。
在主要考慮對象而不是自定義類型和構造函數的狀況下,寄生式繼承也是一種有用的模式。
前面示範繼承模式時使用的object()函數不是必需的,任何可以返回新對象的函數都適用於此模式。