继承
基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
OO语言提出了继承的思想,所以我们先来了解一下面向对象OO:
《JavaScript高级程序设计》一书中对面向对象的定义为:
面向对象(Object-Oriented)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性的方法的对象。
大神们逐渐提出了工厂模式、构造函数模式、原型模式、组合使用构造函数和原型模式、动态原型模式、寄生构造函数模式、稳妥构造函数模式等程序思想。
工厂模式:可大批量复制的函数。
构造函数模式:new出来一个新对象,但缺点是每个方法都要在实例上重新创建一遍。
原型模式:每个函数都有个prototype原型属性,它是一个指针,指向一个对象。而prototype下会自动获得constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。问题是共享。例如new出来个对象1,2。在任意对象里修改prototype都修改了原型的prototype,从而一变全变,违背了可复制使用的初衷。
组合使用构造函数和原型模式:结合了两者之长,构造函数模式定义实例属性,原型模式定义方法和共享属性。每个实例都有自己属性的副本,又都共享着对方法的引用,最大限度的节省了内存。这种模式是开发中认同度较高的创建自定义类型的方法。
动态原型模式:就是if判断了一下,原型的方法存不存在,存在即做事。
寄生构造函数模式:前述情况若开发中都不适用,可使用此种模式。思想为函数套着一个new出来的函数。外层仅封装创建的代码,内层做完事return出去,可以重写调用构造函数时返回的值。
稳妥构造函数模式:没有公共属性,适合在一些安全的环境中(禁止使用this,new),防止数据被改动时使用。
了解完以上模式,再来看继承就简单易懂的多了:
许多OO语言都支持两种继承方式:接口继承,实现继承。
接口继承只继承方法签名,实现继承则继承实际的方法。由于函数没有签名,所以在ECMAScript中就无法实现接口继承。而实现继承主要是依靠原型链来实现的。
原型链继承:
借用构造函数继承(伪造对象或经典继承):call,apply
call继承
apply继承
可以看到,call与apply继承第一个参数都是指向(这里指向的this,在实际项目中谁调用指向谁)
第二个参数call为字符串,也可以有第三四五六个参数。apply则是一个数组。
原型式继承:
function object(o){
function F(){}
F.prototype = o;
return new F();
}
object()对传入其中的对象执行了一次浅复制。
ES5新增了Object.create()方法规范化了原型式继承。
缺点是共享问题。
寄生式继承:
function createAnother(o){
var clone = object(o);
clone.sayHi = fucntion(){
alert("Hi");
}
return clone;
}
var person = {
name: "mx",
friends: ["as",1,"ddd"]
};
var a = createAnother(person);
a.sayHi(); //"Hi"
把原型式继承改造成了工厂模式,prototype在外边传入,不在函数内修改。
缺点是类似call,apply继承的方式,虽然可以有自己属性的副本,但因此就不能做到大规模的函数复用了。
寄生组合式继承:
寄生组合式继承结合call继承子类,解决了前二者的痛点,共享复用问题。在开发者眼中普遍认为是最理想的继承范式。
网上说的改变构造函数执行环境继承类似上面的原型式继承: