js中实现继承的几种方式
2017-02-18 16:16
831 查看
首先我们了解,js中的继承是主要是由原型链实现的。那么什么是原型链呢?
由于每个实例中都有一个指向原型对象的指针,如果一个对象的原型对象,是另一个构造函数的实例,这个对象的原型对象就会指向另一个对象的原型对象,如此循环,就行成了原型链。
在了解原型链之后,我们还需要了解属性搜索机制,所谓的属性搜索机制,就是当我们访问对象上的一个属性时,我们如何找到这个属性值。首先,我们现在当前实例中查找该属性,如果找到了,返回该值,否则,通过__proto__找到原型对象,在原型对象中进行搜索,如果找到,返回该值,否则,继续向上进行搜索,直到找到该属性,或者在原型链中没有找到,返回undefined。
根据《javascript高级程序设计》中,可以有六种继承方式,下面我们一一来介绍:
但是这种继承方式存在一个问题,那就是引用类型属性共享。
它存在第二个问题,就是无法向父类种传参。
2.1 解决引用类型共享问题
2.2 解决传参数问题
当时,上述方法也存在一个问题,共享的方法都在构造函数中定义,无法达到函数复用的效果。
上述方法,虽然综合了原型链和借用构造函数的优点,达到了我们想要的结果,但是它存在一个问题。就是创建一次实例时,两次调用了父类构造函数。
因此,出现了寄生组合式继承。在了解之前,我们先了解一下什么是寄生式继承。
5. 寄生组合式继承
由于每个实例中都有一个指向原型对象的指针,如果一个对象的原型对象,是另一个构造函数的实例,这个对象的原型对象就会指向另一个对象的原型对象,如此循环,就行成了原型链。
在了解原型链之后,我们还需要了解属性搜索机制,所谓的属性搜索机制,就是当我们访问对象上的一个属性时,我们如何找到这个属性值。首先,我们现在当前实例中查找该属性,如果找到了,返回该值,否则,通过__proto__找到原型对象,在原型对象中进行搜索,如果找到,返回该值,否则,继续向上进行搜索,直到找到该属性,或者在原型链中没有找到,返回undefined。
根据《javascript高级程序设计》中,可以有六种继承方式,下面我们一一来介绍:
1. 原型链
// 父亲类 function Parent() { this.value = 'value'; } Parent.prototype.sayHi = function() { console.log('Hi'); } // 儿子类 function Child() { } // 改变儿子的prototype属性为父亲的实例 Child.prototype = new Parent(); var child = new Child(); // 首先现在child实例上进行查找,未找到, // 然后找到原型对象(Parent类的一个实例),在进行查找,未找到, // 在根据__proto__进行找到原型,发现sayHi方法。 // 实现了Child继承 child.sayHi();
但是这种继承方式存在一个问题,那就是引用类型属性共享。
// 父亲类 function Parent() { this.color = ['pink', 'red']; } // 儿子类 function Child() { } Child.prototype = new Parent(); var child1 = new Child(); var child2 = new Child(); // 先输出child1和child2种color的值 console.log(child1.color); // ["pink", "red"] console.log(child2.color); // ["pink", "red"] // 在child1的color数组添加white child1.color.push('white'); console.log(child1.color); // ["pink", "red", "white"] // child1上的改动,child2也会受到影响 console.log(child2.color); // ["pink", "red", "white"]
它存在第二个问题,就是无法向父类种传参。
2. 借用构造函数
在这里,我们借用call函数可以改变函数作用域的特性,在子类中调用父类构造函数,复制父类的属性。此时没调用一次子类,复制一次。此时,每个实例都有自己的属性,不共享。同时我们可以通过call函数给父类传递参数。2.1 解决引用类型共享问题
// 父亲类 function Parent(name) { this.name = name; this.color = ['pink', 'red']; } // 儿子类 function Child() { Parent.call(this); // 定义自己的属性 this.value = 'test'; } var child1 = new Child(); var child2 = new Child(); // 先输出child1和child2种color的值 console.log(child1.color); // ["pink", "red"] console.log(child2.color); // ["pink", "red"] // 在child1的color数组添加white child1.color.push('white'); console.log(child1.color); // ["pink", "red", "white"] // child1上的改动,child2并没有受到影响 console.log(child2.color); // ["pink", "red"]
2.2 解决传参数问题
// 父亲类 function Parent(name) { this.name = name; this.color = ['pink', 'red']; } // 儿子类 function Child(name) { Parent.call(this, name); // 定义自己的属性 this.value = 'test'; } var child = new Child('qq'); // 将qq传递给Parent console.log(child.name); // qq
当时,上述方法也存在一个问题,共享的方法都在构造函数中定义,无法达到函数复用的效果。
3. 组合继承
根据上述两种方式,我们可以扬长避短,将需要共享的属性使用原型链继承的方法继承,将实例特有的属性,用借用构造函数的方式继承。// 父亲类 function Parent() { this.color = ['pink', 'red']; } Parent.prototype.sayHi = function() { console.log('Hi'); } // 儿子类 function Child() { // 借用构造函数继承 Parent.call(this); // 下面可以自己定义需要的属性 } // 原型链继承 Child.prototype = new Parent(); var child1 = new Child(); var child2 = new Child(); // 每个实例特有的属性 // 先输出child1和child2种color的值 console.log(child1.color); // ["pink", "red"] console.log(child2.color); // ["pink", "red"] // 在child1的color数组添加white child1.color.push('white'); console.log(child1.color); // ["pink", "red", "white"] // child1上的改动,child2并没有受到影响 console.log(child2.color); // ["pink", "red"] // 每个实例共享的属性 child1.sayHi(); // Hi child2.sayHi(); // Hi
上述方法,虽然综合了原型链和借用构造函数的优点,达到了我们想要的结果,但是它存在一个问题。就是创建一次实例时,两次调用了父类构造函数。
// 父亲类 function Parent() { this.color = ['pink', 'red']; } Parent.prototype.sayHi = function() { console.log('Hi'); } // 儿子类 function Child() { Parent.call(this); // 第二次调用构造函数:在新对象上创建一个color属性 } Child.prototype = new Parent(); // 第一次调用构造函数Child.prototype将会得到一个color属性,屏蔽了原型中的color属性。
因此,出现了寄生组合式继承。在了解之前,我们先了解一下什么是寄生式继承。
4. 寄生式继承
同工厂模式类似,将我们需要继承的函数进行封装,然后进行某种增强,在返回对象。function Parent() { this.color = ['pink', 'red']; } function createAnother(o) { // 获得当前对象的一个克隆 var another = new Object(o); // 增强对象 o.sayHi = function() { console.log('Hi'); } // 返回对象 return another; }
5. 寄生组合式继承
// 创建只继承原型对象的函数 function inheritPrototype(parent, child) { // 创建一个原型对象副本 var prototype = new Object(parent.prototype); // 设置constructor属性 prototype.constructor = child; child.prototype = prototype; } // 父亲类 function Parent() { this.color = ['pink', 'red']; } Parent.prototype.sayHi = function() { console.log('Hi'); } // 儿子类 function Child() { Parent.call(this); } inheritPrototype(Parent, Child);
6. 原型式继承
思想:基于已有的对象创建对象。function createAnother(o) { // 创建一个临时构造函数 function F() { } // 将传入的对象作为它的原型 F.prototype = o; // 返回一个实例 return new F(); }