javascript之继承
主要是参考了《JavaScript高级程序设计(第三版)》这本书,根据自己的理解,做了下面的记录
继承是面向对象(OO)语言里面的概念,有俩种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。由于函数没有签名,在javascript里面无法实现接口继承,只支持实现继承。
原型链继承
构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
所有函数的默认原型都是Object的实例
那如果让原型对象等于另一个类型的实例会怎么样呢?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型也包含着指向另一个构造函数的指针。
具体代码:
1 function SuperType() { 2 this.proterty = true 3 } 4 SuperType.prototype.getSuperValue = function () { 5 return this.proterty 6 } 7 function SubType() { 8 this.subproperty = false 9 } 10 //继承了SuperType 11 SubType.prototype = new SuperType() 12 SuperType.prototype.getSubValue = function () { 13 return this.subproperty 14 } 15 var instance = new SubType() 16 instance.getSuperValue() //true
判断原型和实例的关系
使用instance或者isPrototypeOf
1 alert(instance instanceof SubType) // true 2 alert(SubType.prototype.isPrototypeOf(instance)) // true
缺点:
(一)、只要某个实例、构造函数、原型对象修改了属性或方法,会对原型链上的其他对象实例造成影响
(二)、在创建了类型的实例时,没有办法在不影响谁有对象实例的情况下,给超类型的构造函数传递参数
借用构造函数
1 function SuberType() { 2 this.colors = ["red","blur"] 3 } 4 function SubType() { 5 //继承了SuberType 6 //使用了call改变了this的作用域,用apply也可以 7 SuberType.call(this) 8 } 9 var instance = new SubType() 10 instance.colors.push("black") 11 alert(instance.colors) //red,blur,black
13 var instalce2 = new SuberType() 14 alert(instalce2.colors) // red,blur
优点:可以在子类型构造函数中向超类型构造函数传递参数
1 function SuberType(name) { 2 this.name = name 3 } 4 function SubType() { 5 //继承了SuberType,还传递了参数 6 SuberType.call(this,"zhao") 7 this.age = "27" 8 } 9 var instance = new SubType() 10 alert(instance.name) //zhao 11 alert(instance.age) //27
缺点:
方法都在构造函数中定义,函数无法复用。而且在超类型的原型中定义的方法,子类型是不可见的
组合继承
原型链和构造函数的技术组合到一起。思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
1 function SuberType(name) { 2 this.name = name 3 this.colors = ["red","blue"] 4 } 5 SuberType.prototype.sayName = function () { 6 alert(this.name) 7 } 8 function SubType(name,age) { 9 //继承属性 10 SuberType.call(this, name) // 第一次调用SuberType() 11 this.age = age 12 } 13 //继承方法 14 SubType.prototype = new SuberType() //第二次调用SuberType() 15 SubType.prototype.sayAge = function () { 16 alert(this.age) 17 } 18 var instance1 = new SubType("zhao", 27) 19 instance1.colors.push("black") 20 alert(instance1.colors) // red,blue,black 21 instance1.sayName() // zhao 22 instance1.sayAge() //27 23 24 var instance2 = new SubType("w", 28) 25 alert(instance2.colors) // red,blue 26 instance2.sayName() // w 27 instance2.sayAge() // 28
优点:实例有各自的属性,和相同的方法
缺点:会俩次调用超类型的构造函数
原型式继承
借助原型可以基于已有的对象创建新对象,不必因此自定义类型
1 function object(o) { 2 function F() {} 3 F.prototype = o 4 return new F()6 }
上面这个函数的本质是:object()对传入的对象执行了一次浅复制
1 var person = { 2 name: 'zhao', 3 friends: ['zhao1','zhao2','zhao3'] 4 } 5 var anotherPerson = object(person) 6 anotherPerson.name = 'ss' 7 anotherPerson.friends.push('Bob') 8 9 var yetAnotherPerson = object(person) 10 yetAnotherPerson.name = 'rr' 11 yetAnotherPerson.friends.push('Bar') 12 alert(person.friends) //zhao1,zhao2,zhao3,Bob,Bar
ECMAScript5通过新增Object.create()方法规范了原型式继承,这个方法接收俩个参数,一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象
在传入一个参数的情况下,Object.create()和object()一样
1 var person = { 2 name: 'zhao', 3 friends: ['zhao1','zhao2','zhao3'] 4 } 5 var anotherPerson = Object.create(person) 6 anotherPerson.name = 'ss' 7 anotherPerson.friends.push('Bob') 8 9 var yetAnotherPerson = Object.create(person) 10 yetAnotherPerson.name = 'rr' 11 yetAnotherPerson.friends.push('Bar') 12 alert(person.friends) //zhao1,zhao2,zhao3,Bob,Bar
Object.create()的第二个参数会覆盖原型对象上的同名属性
1 var person = { 2 name: 'zhao', 3 friends: ['zhao1','zhao2','zhao3'] 4 } 5 var anotherPerson = Object.create(person) 6 anotherPerson.name = 'ss' 7 anotherPerson.friends.push('Bob') 8 9 var yetAnotherPerson = Object.create(person) 10 yetAnotherPerson.name = 'rr' 11 yetAnotherPerson.friends.push('Bar') 12 alert(person.friends) //zhao1,zhao2,zhao3,Bob,Bar
寄生式继承
思路:创建一个仅用与封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象
1 <script> 2 function object(o) { 3 function F() {} 4 F.prototype = o 5 return new F() 6 } 7 8 function createAnother(original) { 9 var clone = object(original) // 通过调用函数创建一个新对象 10 clone.sayHi = function () { // 以某种方式来增强这个对象 11 alert("hi") 12 } 13 return clone //返回这个对象 14 } 15 var person = { 16 name: "zhaobao", 17 friends: ["zhaobao1","zhaobao2","zhaobao3"] 18 } 19 var anotherPerson = createAnother(person) 20 anotherPerson.sayHi() // hi 21 </script>
寄生组合式继承
这个主要是为了解决组合继承的缺点
思路:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
不必为了指定子类型的原型而调用超类型的构造函数,我们需要的只是超类型原型的一个副本。
本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
基本模式
1 function inheritPrototype(subType, superType) { 2 var prototype = object(superType) //创建对象 3 prototype.constructor = subType // 增强对象 4 subType.prototype = prototype //指定对象 5 }
例子
1 function inheritPrototype(subType, superType) { 2 var prototype = object(superType) //创建对象 3 prototype.constructor = subType // 增强对象 4 subType.prototype = prototype //指定对象 5 } 6 function SuperType(name) { 7 this.name = name 8 this.colors = ["red","blue"] 9 } 10 SuperType.prototype.sayName = function () { 11 alert(this.name) 12 } 13 function SubType(name,age) { 14 SuperType.call(this,name) 15 this.age = age 16 } 17 inheritPrototype(SubType,SuperType) 18 SuperType.prototype.sayAge = function () { 19 alert(this.age) 20 }
这个例子的高效率提现在它只调用了一次SuperType构造函数,并且因此避免了在SubType.prototype上面创建不必要的、多余的属性。与此同时,原型链还能保持不变,因此,还能正常使用instanceof和isPrototypeOf()
这是最理想的继承方式