es5创建对象与继承

创建对象7种方法

继承的六种实现方式

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();
11         const c = new Child();
12         c.addColor('gray');
13 
14         const other = new Child();
15         console.log(other.colors); //['blue', 'green', 'gray']

问题:父类实例如果有引用类型成员,所有子类都共享这些成员。请看下面的代码:

function Animal() {
    this.friends = ['dog', 'cat'];
}

function Dog() {}
Dog.prototype = new Animal();

const dog1 = new Dog();
const dog2 = new Dog();

dog1.friends.push('rabbit');
console.log(dog1.friends); // ['dog', 'cat', 'rabbit']
console.log(dog2.friends); // ['dog', 'cat', 'rabbit']  // dog2 也被修改了 

 

2. 构造函数继承

将子类构造函数的this显示绑定到父类身上。这样子类的构造函数身上就显示具有父类身上的属性。。

 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),会在内存中被创建多次,浪费内存空间。
父类的原型方法无法被继承,代码如下:

function Parent() {
    this.name = 'Parent';
}

Parent.prototype.sayName = function() {
    console.log(this.name);
};

function Child() {
    Parent.call(this); // 继承父类属性
}

const child = new Child();
child.sayName(); // Uncaught TypeError: child.sayName is not a function

 

3.组合继承(原型链继承+构造函数继承)

是 JavaScript 中最常用的继承方式之一。它结合了 原型链继承构造函数继承 的优点,使子类可以:

  • 通过构造函数继承父类的实例属性(避免共享引用类型)。

  • 通过原型链继承父类的方法(保证方法共享,节省内存)。

 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 = Child
14         const c = new Child();
15         c.addColor('gray');
16 
17         const other = new Child();
18         console.log(other.colors); //['blue', 'green']

 缺点: 需要调用两次构造函数,性能上还可以进一步优化

 

4.寄生组合继承(最完美的方式)

  • 实例:通过 Parent.call(this) 继承父类构造函数中的属性,避免了父类构造函数被重复调用。每个实例都有自己的属性,避免了原型链继承中共享引用类型属性的问题。

  • 共享方法:通过 Object.create(Parent.prototype)继承 父类原型上的方法,保证了子类共享方法,不会重复定义。

 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)

posted @ 2018-05-31 09:27  我是格鲁特  阅读(163)  评论(0)    收藏  举报