javascript中最佳的继承方案
Posted liuup
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript中最佳的继承方案相关的知识,希望对你有一定的参考价值。
首先是为什么要继承?
万物皆对象,在面向对象思想的开发中,研发一个汽车是需要一个一个部件组成的,每一个部件都是一个对象,每个对象都有自己的功能和职责,而继承就是要解决对象之间避免的重复造轮子,避免做重复的工作。
比如:我已经有了一个汽车的近光灯对象,我还需要构建一个远光灯对象,让这个灯的功能变得更加强大,我当然不想在重新学习、如何买材料、制作技术等等,我只需要将制作近光灯的技术搬以致用就行了,甚至我还可以增加新的功能,给它扩展一个远光的功能,这样一来我的成本是不是就很低了呢,这样一来我的核心置关注在如何研发远光的功能,而不是重复的造轮子。
在上面的解释中我们知道为什么要继承了,继承的核心概念是什么呢,继承的核心其实就是原型链。
原型链是什么?
原型链是当前对象实例的原构造函数中的原型对象和所有被继承的实例对象的原构造函数的原型对象,一层一层向上找,一直到顶层对象Object,形成的一种关系链条,就叫做原型链。
比如:我的远光灯是当前实例对象,它继承了近光灯对象的一切功能,也就是说它同时拥有了近光灯的原型对象,这样的关联关系就是原型链。
看到这,相信你对原型链已经有了一个基本的认识,你可能又好奇,实例对象是什么,原型对象又是什么,原型对象和原型链有什么关系,等疑问。
实例对象是什么?
实例对象就是我们所说的万物皆对象,它可以是一个灯,可以是一个方向盘,也可以是一个轮胎,它是被构造函数创造出来的,其实就是定制化的一个一个对象。
构造函数是什么?
构造函数可以理解成灯的制作工厂,别名也叫做工厂函数,任何一个javascript函数都可以作为是一个构造函数,只要使用了new关键字,就可以创造出一个实例对象,而这个实例对象会把函数的原型对象和自己的原型链相关联,引用同一个内存地址,也就是说__proto__和prototype其实一个东西。
恰似它更像是一个类,类是一种用户定义类型,它是抽象的,它有属性有方法,有三大特性,封装,继承,多态。
原型对象是什么?
我们都知道函数也是一个对象,javascript引擎在解析代码时会为每个函数自动分配一个prototype空对象,这个prototype就叫做原型对象,它就是将来给我们需要继承的实例使用的继承对象。
如何实现继承?
我们都知道,在javascript中要实现继承有很多种方案,每个方案都各自有各自的优缺点,一旦使用了错误的继承方案,就会带来意想不到的困难,所以我认为这一种继承方案是比较佳的方案。
首先:我准备一个被继承类(灯)
function Lamp() { this.types = [1, 2, 3, 4]; } Lamp.prototype.open = function () { console.log("打开了近光灯"); }
继承方案一:
function LampMax() { } LampMax.prototype = new Lamp(); var lampMax1 = new LampMax(); lampMax1.open(); // 打开了近光灯 var lampMax2 = new LampMax(); lampMax2.open(); // 打开了近光灯 // 问题 lampMax1.types.push(5); console.log(lampMax1.types);// [1, 2, 3, 4, 5] console.log(lampMax2.types);// [1, 2, 3, 4, 5]
function LampMax() { Lamp.call(this); } LampMax.prototype = new Lamp(); var lampMax1 = new LampMax(); lampMax1.open(); // 打开了近光灯 var lampMax2 = new LampMax(); lampMax2.open(); // 打开了近光灯 lampMax1.types.push(5); console.log(lampMax1.types);// [1, 2, 3, 4, 5] console.log(lampMax2.types);// [1, 2, 3, 4]
function LampMax() { Lamp.call(this); } LampMax.prototype = Lamp.prototype; var lamp = new Lamp(); var lampMax = new LampMax(); // 问题 console.log(lamp.__proto__.constructor === lampMax.__proto__.constructor) // true
function LampMax() { Lamp.call(this); } for(var key in Lamp.prototype){ LampMax.prototype[key] = Lamp.prototype[key]; } LampMax.prototype.constructor = LampMax; var lamp = new Lamp(); var lampMax = new LampMax(); // 问题 console.log(lamp.__proto__.constructor === lampMax.__proto__.constructor) // false
问题说明:第一个,子类和父类的构造器相等了,第二个,使用了循环拷贝了原型对象,但是性能不太好,所以不合格。
直接终极继承方案:
function LampMax() { Lamp.call(this); } LampMax.prototype = Object.create(Lamp.prototype); LampMax.prototype.constructor = LampMax; var lampMax1 = new LampMax(); lampMax1.open(); // 打开了近光灯 var lampMax2 = new LampMax(); lampMax2.open(); // 打开了近光灯 lampMax1.types.push(5); console.log(lampMax1.types);// [1, 2, 3, 4, 5] console.log(lampMax2.types);// [1, 2, 3, 4] var lamp = new Lamp(); var lampMax = new LampMax(); console.log(lamp.__proto__.constructor === lampMax.__proto__.constructor) // false
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__,它的作用就是用于原型链链接的。
还有其他的继承方案,五花八门,但是我觉得既然继承的核心是原型链,如果跟原型链无关的继承,还能叫继承吗,对不对,所以继承不但要继承原型链,还要继承父类的构造函数,自然是离不开组合的方式来继承。
最后在放一段es6的继承:
class LampMax extends Lamp{ constructor(){ super(); } } var lampMax1 = new LampMax(); lampMax1.open(); // 打开了近光灯 var lampMax2 = new LampMax(); lampMax2.open(); // 打开了近光灯 lampMax1.types.push(5); console.log(lampMax1.types);// [1, 2, 3, 4, 5] console.log(lampMax2.types);// [1, 2, 3, 4] var lamp = new Lamp(); var lampMax = new LampMax(); console.log(lamp.__proto__.constructor === lampMax.__proto__.constructor) // false
以上是关于javascript中最佳的继承方案的主要内容,如果未能解决你的问题,请参考以下文章