實現繼承主要是依靠原型鏈來實現的。函數
基本思想是:利用原型 讓一個引用類型繼承另外一個引用類型的屬性和方法。this
原型鏈繼承spa
有兩個構造函數 A 和 B,若是,讓A的原型對象等於B的實例,結果會是怎麼樣呢?prototype
此時 A 的原型對象將包含一個指向 B 的原型的指針,相應地,B 的原型中也可包含着指向 C 的指針。假如 C 的原型又是 D 的實例,那麼上述關係依然成立,如此層層遞進,就構成了實例與原型的鏈條。這就是原型鏈的基本概念。指針
繼承實現的本質,就是重寫原型對象,代之以一個新類型的實例。code
function A(){ //... } A.prototype.getA = function(){ //... } function B(){ //... } B.prototype = new A() //繼承了A B.prototype.getB = function(){ //... } var o = new B(); console.log(o.getA()) //在B構造函數建立的對象o中,執行A構造函數的方法
調用o.getA()會經歷三搜索步驟:對象
全部函數的默認原型都是Object的實例,所以默認原型都會包含一個內部指針,指向Object.prototype。blog
instanceof 操做符 確認實例 是否 原型鏈中出現過的構造函數的實例繼承
o instanceof Object //true o instanceof A //true o instanceof B //true
isPrototypeOf()方法ip
Object.prototype.isPrototypeOf(o) //true A.prototype.isPrototypeOf(o) //true B.prototype.isPrototypeOf(o) //true
給原型添加方法的代碼,必定要放在替換原型的語句以後。
function A(){} A.prototype.getA = function(){} function B(){} B.prototype = new A() //繼承了A //新增的方法 B.prototype.getB = function(){} //重寫超類中的方法 B.prototype.getA = function(){} var o = new B(); console.log(o.getA()) //重寫的方法會屏蔽超類中的方法
經過原型鏈實現繼承時,不能使用對象字面量建立原型方法。由於這樣會重寫原型鏈。
function A(){} A.prototype.getA = function(){} function B(){} B.prototype = new A() //繼承了A B.prototype = { getB:function(){}, getOther:function(){} } var o = new B(); console.log(o.getA()) //報錯
缺點:
構造函數繼承
基本思想是:在子類型構造函數的內部調用超類型構造函數。
function A(){ this.arr = [1,2,3] } function B(){ A.call(this) //繼承了A } var o1 = new B(); o1.arr.push(3); console.log(o1.arr) //1,2,3,4 var o2 = new B() console.log(o2.arr) //1,2,3
能夠在子類型構造函數中向超類型構造函數傳遞參數
function A(name){ this.name = name; } function B(){ A.call(this,'adhehe') //繼承了A,同時傳遞參數 this.age = 23; } var o = new B(); console.log(o.name) //adhehe console.log(o.age) //23
缺點:
組合繼承(原型鏈繼承 與 構造函數繼承 組合)
基本思想是:使用原型鏈實現對原型屬性和方法的繼承,使用構造函數實現對實例屬性的繼承。
組合繼承避免了原型鏈和構造函數的缺陷,融合了它們的優勢,是JavaScript中最經常使用的繼承模式。而且,instanceof和isPrototypeOf()也可以用於識別基於組合繼承建立的對象。
function A(name){ this.name = name; this.arr = [1,2,3]; } A.prototype.getName = function(){} function B(name,age){ A.call(this,name) //繼承屬性 this.age = age; } //繼承方法 B.prototype = new A(); B.ptototype.constructor = B; B.ptototype.getAge = function(){} var o1 = new B('adhehe',23); o1.arr.push(4) o1.arr //1,2,3,4 o1.getName() //adhehe o1.getAge() //23 var o2 = new B('Jhone',45); o2.arr //1,2,3 o2.getName() //Jhone o2.getAge() //45
缺點:不管在什麼狀況下,都會調用兩次超類型構造函數。一次是建立子類型原型的時候,一次是在子類型構造函數內部。
原型式繼承
藉助原型能夠基於已有的對象建立新對象,同時還沒必要所以建立自定義類型。
這種原型式繼承,要求必須有一個對象能夠做爲另外一個對象的基礎。
function Object(o){ function F(){} F.prototype = o; return new F(); }
var person = { name:'adhehe', colors:['red','blue','yellow'] } var o1 = object(person); o1.name = 'Greg'; o1.colors.push('pink'); var o2 = object(person); o2.name = 'Linda'; o2.colors.push('gray'); console.log(person.colors) //'red','blue','yellow','pink','gray'
ECMAScript5新增Object.create()方法規範化了原型式繼承
var person = { name:'adhehe', colors:['red','blue','yellow'] } var o1 = Object.create(person) o1.name = 'Greg'; o1.colors.push('pink'); var o2 = Object.create(person); o2.name = 'Linda'; o2.colors.push('gray'); console.log(person.colors) //'red','blue','yellow','pink','gray'
Object.create()方法的第二個參數 與 Object.defineProperties()方法的第二個參數格式相同:每一個屬性都是經過本身的描述符定義的。以這種方式指定的任何屬性都會覆蓋原型對象上的同名屬性。、
var person = { name:'adhehe', colors:['red','blue','yellow'] } var o = Object.create(person,{ name:{ value:'Greg' } }) console.log(o.name) //Greg
寄生式繼承
與寄生構造函數和工廠模式相似,即建立一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來加強對象,最後再返回對象。
function object(o){ function F(){} F.prototype = o; return new F(): } function createPreson(arg){ var clone = object(arg); clone.sayHi = function(){} return clone; } var preson = { name:'adhehe', colors:['red','blue','yellow'] } var o = createPerson(person); o.sayHi();
任何可以返回新對象的函數都適用於此模式。
寄生組合式繼承
經過借用構造函數來繼承屬性,經過原型鏈的混成形式來繼承方法。
基本思想是:沒必要爲了指定子類型的原型而調用超類型的構造函數,咱們須要的無非是超類型原型的一個副本而已。
function person(sub,sup){ var ptototype = object(sup.prototype); //建立對象 prototype.constructor = sub; //加強對象 sub.prototype = prototype; //指定對象 } //第一步,建立超類型原型的一個副本 //第二步,爲建立的副本添加constructor屬性,彌補因重寫原型而失去的默認的constructor屬性 //最後一步,將新建立的對象賦值給子類型的原型
function Sup(name){ this.name = name; this.colors = ['red','blue','yellow'] } Sup.prototype.sayName = function(){} function Sub(name,age){ Sup.call(this,name) this.age = age; } person(sub,sup); sub.prototype.sayAge = function(){}