方式一:原型链的直接继承
重写原型对象,代之以一个新类型的实例
Idol.prototype.lastName = 'cai'
function Idol () {
this.name = 'kunkun'
this.hobby = ['rap', 'dance', 'basketball']
}
var idol = new Idol()
Fan.prototype = idol
function Fan () {}
var fan1 = new Fan()
var fan2 = new Fan()
console.log(fan1) // {},原型上的属性是看不到的
console.log(fan1.hobby) // ['rap', 'dance', 'basketball']
console.log(fan1.name) // 'kunkun' 我只想继承哥哥的爱好。 但是哥哥的名字也继承过来了。继承了过多的东西了。
fan2.hobby.push('sing')
console.log(fan1.hobby) // [ 'rap', 'dance', 'basketball', 'sing' ] fan2的爱好也被添加到fan1了,修改引用中的值,会相互影响
直接修改原型链的弊端:
1、原型链过多继承了没用的属性,某些属性(继承的属性)是看不到的;
2、修改原型链中引用类型的值,会相互影响;
方式二:借用构造函数
使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类
function Idol () {
this.name = 'kunkun'
this.hobby = ['rap', 'dance', 'basketball']
}
function Fan () {
Idol.call(this)
this.age = 18
}
var fan = new Fan()
console.log(fan.hobby) // ['rap', 'dance', 'basketball']
console.log(fan.name) // 'kunkun' 我只想继承哥哥的爱好。 但是哥哥的名字也继承过来了。继承了过多的东西了。
借用构造函数的弊端:
1、不能继承借用构造函数的原型;
2、每次构造都要多走一个函数,走Fan 还要走Idol
3、call 改变 this 指向,借用别人的函数,实现自己的功能。只能在你的需求完全涵盖别人的时候才能使用,如果不想要name这个属性,就不能使用这种方法(同样是继承了过多的东西)
方式三:组合式继承
用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承。
Idol.prototype.lastName = 'Xu'
function Idol () {
this.name = 'kunkun'
this.hobby = ['rap', 'dance', 'basketball']
}
function Fan () {
Idol.call(this)
this.age = 18
}
Fan..prototype = Idol.prototype
var fan = new Fan()
console.log(fan.lastName) // 'Xu'
方式四:原型式继承
利用一个空对象作为中介,并指定它的原型,这样可以通过原型链继承他的属性和方法。
// 方法一:
var obj = {
name: 'cxk',
age: 18,
friends: ['wyf']
}
// 原型式继承函数
function createObj1(o) {
var newObj = {}
// 为某个对象设置原型
Object.setPrototypeOf(newObj, o)
return newObj
}
var info = createObj1(obj)
console.log(info.name) // 'cxk'
// 方法二:
function createObj2(o) {
function Fn() {}
Fn.prototype = o
return new Fn()
}
var info = createObj2(obj)
console.log(info.__proto__) // { name: 'cxk', age: 18, friends: [ 'wyf' ] }
console.log(info.name) // 'cxk'
// 方法三:
新创建一个对象,并将某个对象作为新创建的对象的原型
var info = Object.create(obj)
console.log(info)
console.log(info.__proto__) // { name: 'cxk', age: 18, friends: [ 'wyf' ] }
console.log(info.name) // 'cxk'
弊端:
原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
无法传递参数
方式五:寄生式继承
寄生式继承的思路是结合原型式继承和工厂模式的一种方式; 即创建一个封装继承过程的函数, 该函数在内部以某种方式来增强对象,最后再将这个对象返回。
function objectCopy(obj) {
function Fun() { };
Fun.prototype = obj;
return new Fun();
}
var idolObj = {
dancing: function() {
console.log('dancing')
}
}
// 工厂函数
function createFan(name) {
var fan = objectCopy(idolObj)
// 以下为增强对象属性
fan.name = name
fan.studying = function() {
console.log('singing')
}
return fan
}
var fanObj1 = createFan('wyf')
var fanObj2 = createFan('cxk')
console.log(fanObj1) // { name: 'wyf', studying: [Function (anonymous)] }
console.log(fanObj2) // { name: 'cxk', studying: [Function (anonymous)] }
方式六:寄生组合式继承
结合借用构造函数传递参数和寄生模式实现继承,寄生组合式继承是在原型式继承的基础上有改变。
function inherit(Target, Origin) {
function F() {}
F.prototype = Origin.prototype
Target.prototype = new F()
// 原型上的constructor是不可遍历的
// Target.prototype.constructor = Target;
Object.defineProperty(Target.prototype, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value: Target
})
}
Idol.prototype.lastName = "cai"
function Idol() {
}
function Fan() {
}
inherit(Fan, Idol);
var fan = new Fan();
var idol = new Idol();
Fan.prototype.sex = "male";
console.log(fan.lastName); // 'cai'
console.log(fan.sex); // 'male'
console.log(idol.sex); // undefined, 既修改了自己的原型也不会影响到Idol的原型