js的是利用原型链实现面向对象的类行为,和oo语言中的类有这巨大的不同。
下面讲述几种继承的方式以及一些扩展。
我对js的原型继承的一些理解:
- js中只有函数存在prototype这个属性,代表原型,object中只有__proto__属性
- js中函数也是object的一种,所以函数同时存在prototype和__proto__属性,但是这是两个完全不同的属性
- js中所有的object都是继承于底层的Object.prototype
- js中所有函数的构造函数是Function
- Function.prototype是函数(这就是解决原型链循环的关键),正常函数的prototype属性都是一个object
- js中实例的__proto__总是指向它构造函数的prototype(这个也是原型链的重点)
// 父类function Animal() { this.name = 'animal'; this.type = 'animal';}Animal.prototype.say = function () { console.log(this.name);}复制代码
原型链继承, 通过子类的prototype是父类的一个实例,从而实现一条原型链,如下:(Sub代表子类,sub代表子类的一个实例,Super代表父类,super代表父类的实例)
sub.__proto__ = Sub.prototype = super, super.__proto__ = Super.prototype复制代码缺点:父类的数据对象处于子类的prototype中,prototype是所有子类实例所共有的,所以对于引用类型的数据存在一改全改的问题
function Dog() { this.name = 'dog'}Dog.prototype = new Animal();var dog = new Dog();dog.say();复制代码
dog的详细结构
为了解决上面的原型链缺点,利用构造函数的优点(组合继承)
function Cat(data) { Animal.call(this, data); // 将父类的数据类型在子类复制一份 this.name = 'cat';}Cat.prototype = new Animal();var cat = new Cat(); // 构造函数再次存储了一份父类的数据类型cat.say();复制代码
cat详细结构,可以发现有个缺点就是父类的数据类型被存储了2遍
ES5中有个api:
Object.create(object1, object2): 返回一个object,object的prototype = object1,object2代表prototype中新增或者覆盖的属性eg:var cat = Object.create(new Animal(), { name: { value: 'cat' }})复制代码
上述cat详细结构
参考红宝书(js高级程序设计):这种方式称为原型式继承function object(o) { function F(){} F.prototype = o; return new F();}复制代码
结合原型式继承和构造函数,可以有如下的继承方式
function Bird(data) { Animal.call(this, data); this.name = 'bird';}Bird.prototype = Object.create(Animal.prototype);Bird.prototype.constructor = Bird;var bird = new Bird();bird.say();复制代码
bird的详细结构:解决了原型链继承和组合继承的问题
构造函数有个特点,就是return的是一个对象的时候new的时候生成的就是这个对象,否则将按照正常的原型链生成对象。eg:
function Test() { var arr = [1, 2, 3]; this.arr = arr; return arr;}Test.prototype.say = function() { console.log(this.arr);}var test = new Test();test.say // undefined复制代码
这就是传说中的寄生继承,寄生继承返回的对象跟原型链基本保持一致,唯一的问题就是丢失了子类的构造函数吧,以及第一层prototype
function Horse() { var o = Object.create(new Animal()); o.name = 'horse'; return o;}var horse = new Horse();horse.say();复制代码
horse详细结构:
扩展题目: 如何继承数组?
// 实现数组继承var push = [].push;function myArray() { var array = Array.apply(this, arguments); array.__proto__ = myArray.prototype; return array;}myArray.prototype = Object.create(Array.prototype)myArray.prototype.constructor = myArray;myArray.prototype.push = function(value) { console.log('push:', value); push.call(this, value);}var list = new myArray(1, 2, 3);list.push(4);复制代码
因为数组的独特性所以利用了寄生继承以及object的__proto__属性,以及原型式继承