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详细结构
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__属性,以及原型式继承