JS继承的多种方式

189 阅读3分钟

工厂模式

function cratedPerson(name) {
    var o = new Objec();
    o.name = name;
    o.getName = function() {
        console.log(this.name);
    }
    return o;
}
var person = cratedPerson('bajiu');

缺点:对象无法识别,因为所有的实例都指向一个原型

构造函数

function Person(name) {
    this.name = name;
    this.getName = function() {
        console.log(this.name);
    }
}
var person = new Person('bajiu');

优点:示例可以识别为一个特定的类型。
缺点:每次创建实例时,每个方法都会被创建一次。

构造函数模式优化

function Person(name) {
    this.name = name;
    this.getName = getName;
}
function getName() {
    console.log(this.name);
}
var person = new Person('bajiu');

优点:解决了每个方法都要被重新创建的问题
缺点:如果多个方法,就会新增多个全局方法(非常怪异)

原型模式

function Person(name) {};
Person.prototype.name = 'bajiu';
Person.prototype.getName = function() {
    console.log(this.name);
}
var person = new Person();

优点:方法不会重新创建。
缺点:

  1. 所有的属性和方法都是共享
  2. 不能初始化参数

原型模式优化1

function Person(name) {};
Person.prototype = {
    name: 'baijiu',
    sayName: function() {
        console.log(this.name);
    }
}
var person = new Person();

优点:封装性更高。
缺点:重写了原型,丢失constructor属性

原型模式优化2

function Person(name) {};
Person.prototype = {
    constructor: Person,
    name: 'baijiu',
    sayName: function() {
        console.log(this.name);
    }
}
var person = new Person();

优点:示例可以通过constructor属性周到所属构造函数。
缺点:

  1. 所有的属性和方法都是共享
  2. 不能初始化参数

组合模式

function Person(name) {
    this.name = name;
}
Person.prototype.getName = function() {
    console.log(this.name);
}
var person = new Person;

优点: 该共享的共享,该私有的私有,使用最广泛的方式
缺点:封装性不够好

动态原型模式

function Person(name) {
    this.name = name;
    if(typeof this.getName !== 'function') {
        Person.prototype.getName = function() {
            console.log(this.name);
        }
    }
}
var person = new Person();

注意:使用动态原型模式时,不能用对象字面量重写原型。
原因如下:

function Person(name) {
    this.name = name;
    if(typeof this.getName !== 'function') {
        Person.prototype = {
            constructor: Person
            getName: function() {
                console.log(this.name);
            }
        }
    }
}
var person1 = new Person('bajiu');
var person2 = new Person('kiki');
person2.getName(); // kiki
person1.getName(); // 报错

原因和new的执行步骤有关:

  1. 首先创建一个对象
  2. 将对象的原型指向 Person.prototype
  3. 然后执行Person.apply(obj)
  4. 返回对象

寄生构造函数模式

寄生式继承的思路与(寄生)构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部一某种方式增强对象,对后再想真的是他做了所有工作一样返回对象。

function Person(name) {
    var o = {};
    o.name = name;
    o.getName = function() {
        console.log(this.name);
    }
    return o;
}
var person = new Person();
console.log(person instanceof Person); // false
console.log(person instanceof Object); // true

使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,这点有构造函数模式类似。

寄生组合式继承

组合继承式JS最常用的继承模式;不过,它同样也存在不足。组合继承最大的问题就是无论什么情况下,都会调用两次父类的构造函数;一次实在创建子类原型的时候,另一次实在子类构造函数内部。寄生组合式继承就是为了降低调用父类构造函数的开销而出现的

function extend(subClass, superClass) {
    var prototype = Object.create(superClass.prototype);
    prototype.consturctor = subClass;
    subClass.prototype = prototype;
}

extend的高效率体现在他没有调用superClass构造速度,因此避免了在subClass.prototype上面创建不必要多余的属性。同时,原型链还能保持不变,因此还能正常使用instanceofisPrototypeOf()方法。

稳妥构造函数模式

function person(name) {
    var o = {};
    o.name = name;
    o.getName = function() {
        console.log(name);
    }
    return o;
}
var person = new Person('bajiu');
person.getName(); // bajiu
person.name = 'kiki';
person.getName(); // bajiu
console.log(person.name); // kiki

所谓稳妥对象,指的是没有公共属性,而且其方法不引用this对象。
与寄生构造函数模式有两点内不同:

  1. 新建的实例方法不引用this
  2. 不实用new操作符调用构造函数

稳妥对象最适合在一些安全环境中,稳妥构造函数模式也跟工厂模式一样,无法识别对象的所属的类型