es5创建对象与继承
继承的六种实现方式
1.原型链继承:可以让子类共享父类的方法,其关键实现就是让一个原型对象指向另一个类型的实例
1 function Parent(){ 2 this.colors = ['blue','green']; 3 } 4 Parent.prototype.addColor = function(c){ 5 this.colors.push(c); 6 } 7 function Child(){ 8 } 9 Child.prototype = new Parent(); 10 11 const c = new Child(); 12 c.addColor('gray'); 13 14 const other = new Child(); 15 console.log(other.colors); //['blue', 'green', 'gray']
问题:父类中如果有引用类型的成员,所有子类都共享这些成员。(上面代码第15行)
2. 借用构造函数:在子类构造函数中调用父类的构造函数,并把父类函数的上下文指定为子类本身,以此解决引用类型成员共享问题。
1 function Parent(){ 2 this.colors = ['blue','green']; 3 this.addColor = function(c){ 4 this.colors.push(c); 5 } 6 } 7 function Child(){ 8 Parent.call(this); 9 } 10 const c = new Child(); 11 c.addColor('gray'); 12 13 const other = new Child(); 14 console.log(other.colors); //['blue', 'green']
问题:同一个逻辑的方法(例如上面第3行的addColor),会在内存中被创建多次,浪费内存空间。
3.组合继承(原型链继承+构造函数继承),可解决两种继承的缺点,是es5时代最常用的继承实现方法,思路就是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
1 function Parent(){ 2 this.colors = ['blue','green']; 3 } 4 Parent.prototype.addColor = function(c){ 5 this.colors.push(c); 6 } 7 function Child(){ 8 Parent.call(this); 9 } 10 // 继承方法 11 Child.prototype = new Parent() 12 //上面整个原型对象都被覆盖了,而new Parent的实例是没有构造函数成员的 13 Child.prototype.constructor = Parent 14 const c = new Child(); 15 c.addColor('gray'); 16 17 const other = new Child(); 18 console.log(other.colors); //['blue', 'green']
缺点: 需要调用两次构造函数
4. 原型式继承:借助原型可以基于已有的对象字面量创建新对象,同时还不必因此创建自定义类型。
1 function obj(o){ 2 function F(){}; 3 F.prototype = o; 4 return new F(); 5 } 6 const person = { 7 name: 'Jiang', 8 friends: ['Shelby', 'Bob'] 9 } 10 const zhangsan = obj(person);
在es6中,直接使用Object.create()方法代替上面的obj函数。
缺点: 子类型会共享父类型的引用成员
5. 寄生式继承: 在原型式继承的基础上,给新创建的对象增加了成员(属性或方法)
1 function obj(o){ 2 const newO = Object.create(o); 3 newO.say = function(){ 4 console.log(`my name is ${this.name}`); 5 } 6 return newO; 7 } 8 const person = { 9 name: 'Jiang', 10 friends: ['Shelby', 'Bob'] 11 } 12 const zhangsan = obj(person); 13 zhangsan.say(); // my name is Jiang
缺点: 子类型会共享父类型的引用成员
6. 寄生组合式继承:不必为了指定子类型的原型而调用父类的构造函数,需要的无非就是父类原型的一个副本,在实现上使用寄生式继承来继承父类的原型,再将结果指定给子类型的原型
1 function Parent(name) { 2 this.name = name 3 this.colors = [‘red’, ‘blue’, ‘green’] 4 } 5 Parent.prototype.sayName = function () { 6 console.log(this.name) 7 } 8 function Child(name, job) { 9 // 继承属性 10 Parent.call(this, name) 12 this.job = job 13 } 14 // 继承 15 Child.prototype = Object.create(Parent.prototype) 16 // 修复constructor 17 Child.prototype.constructor = Child 18 var instance = new Child(‘Jiang’, ‘student’) 19 instance.sayName()
ES6新增了一个Object.setPrototypeOf
,可以直接创建关联,而且不用手动添加constructor属性,使用该方法时,第15步-17步可简写为Object.setPrototypeOf(Child.prototype, Parent.prototype)