Bootstrap

JavaScript原型链与继承

原型的概念

在js中,对象可以分为函数对象普通对象两种,其中普通对象只有隐式原型__proto__,函数对象既有隐式原型__proto__,还有显示原型prototype属性。像是我们经常使用的一些如Object,Function等等,都是js内置的函数。隐式原型__proto__具有constructor和__proto__两个属性,constructor用于记录实例是由哪个构造函数创建的,__proto__指向它的构造函数的显示原型的值。显示原型prototype中,则只有一个constructor属性,原型对象的constructor属性指向构造函数本身。

原型链的概念

实例对象在查找属性时,如果查找不到,就会沿着__proto__去与对象关联的原型上查找,如果还查找不到,就去找原型的原型,直至查到最顶层。

原型链的问题

  1. 当原型链中包含引用类型值的原型时,该引用类型值会被所有实例共享;
  2. 在创建子类型时,不能向超类型的构造函数(也就是父级)中传递参数。

继承

原型链继承

function Father() {
    this.FatherRole = "dad";
}
Father.prototype.getFatherRole = function() {
    return this.FatherRole;
}
function Son() {
    this.SonRole = "son";
}
Son.prototype = new Father(); 
Son.prototype.getSonRole = function() {
    return this.SonRole;
}
var a = new Son();
console.log(a.getFatherRole()); // dad

 这个案例中,我们通过Son.prototype = new Father();将Son()函数的显示原型指向了Father()函数,从而使得用Son()函数构造的函数,可以使用 Father()的显示原型上的方法。 

缺点:不仅子类型无法向父类型中传参,而且但凡一个子类型对父类型做出改动,那么所有子类型会一起变动。

借用构造函数继承

本质是在子类型构造函数的内部调用超类型构造函数(父级).

function Father(){
	this.todolists = ["eat","sleep","work"];
}
function Son(){
	Father.call(this);
}
var a = new Son();
a.todolists.push("study");
console.log(a.todolists);//"eat","sleep","work","study"
var b = new Son();
console.log(b.todolists);//"eat","sleep","work" 

通过Father.call(this);创建子类实例时调用Father构造函数,于是Son的每个实例都会将Father中的属性复制一份。这样做的好处是保证了原型链中引用类型值的独立性,不再被所有实例所共享,并且子类型创建时也能向父类型传递参数。但是这样做只能继承父类型的实例,无法像原型链继承那样顺着父类型的原型链向上查找。

组合继承

实际上就是原型链继承+借用构造函数继承。

function Father(a){
	this.message = a;
}
function Son(a){
	Father.call(this,a);
}
Son.prototype = new Father();

 组合继承的优点是同时使用了原型链继承和借用构造函数继承,既能顺着父类型的原型链向上查找,也能避免某一个子类型对父类型的修改造成不可逆的后果。缺点是父类被调用了两次,消耗严重。

原型式继承

利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型。

在object()函数内部, 先创建一个临时性的构造函数, 然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例.

function obj(o) {     //传递一个字面量函数
    function F() {}   //创建一个构造函数
    F.prototype = o;  //把字面量函数赋值给构造函数的原型
    return new F();   //最终返回出实例化的构造函数
}
var fun = { //字面量对象
    name : 'aaa',
};
var fun = obj(fun);  //传递一个字面量函数
alert(fun.name);
fun.name = 'bbb';
alert(fun.name);
var fun2 = obj(fun); //传递一个字面量函数
alert(fun2.name);    //引用类型共享了

 缺点:

  1. 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
  2. 无法传递参数。

寄生式继承

寄生式继承就是用一个函数来包装一个对象,然后返回函数的调用,这样函数就变成了可以随意添加属性的实例或对象。核心是在原型式继承的基础上,增强对象,返回构造函数。

function object(o){
    function fun(){}
    fun.prototype = o;
    return new fun();
}
function inherit(person){
    var clone = object(person);
    clone.smile = function(){
        console.log('hhh')
    }
    return clone;
}
var me = {
    name:"maobuhui"
}
var a = inherit(me);
a.smile();//hhh

寄生组合式继承

寄生组合式继承,就是通过构造函数继承属性,通过原型链的混成方式来继承方法。

function object(o){
    function fun(){}
    fun.prototype = o;
    return new fun();
}
function inherit(a,b){
    var prototype = object(b.prototype);
    prototype.constructor = a;
    a.prototype = prototype; 
}
function Father(name){
    this.name = name;
    this.todolists = ['eat','work']
}
Father.prototype.who = function(){
    console.log(this.name);
}
function Son(name, age){
    Father.call(this,name);
    this.age = age;
}
inherit(Son,Father);
Son.prototype.howOld = function(){
    console.log(this.age);
}

混入方式继承

混入方式继承是一个子类继承多个父类,使用了Object.assign()函数。它的作用就是可以把多个对象的属性和方法拷贝到目标对象中,若是存在同名属性的话,后面的会覆盖前面。(属于浅拷贝)

function a(){
    OneClass.call(this);
    OtherClass.call(this);
}
a.prototype = Object.create(OneClass.prototype);
Object.assigin(a.prototype,OtherClass.prototype);
a.prototype.constructor = a;

;