浅谈ES5的类和继承

说起类(class)这个概念,对于那些熟悉oo语言的程序员们必然是熟的和葡萄干似的,但是在JS中,类是个很有意思的概念。ES5中,JS主要使用原型和原型链来实现类和继承,而在ES6中则提供了class和extends语法糖来实现类和继承

// ES5原型方式实现类
function SuperTypeES5() {

}

// SuperTypeES5的"类属性"
SuperTypeES5.prototype.sayName = function () {
    this.name = 'Allen';
    this.age = 22;
    console.log(this.name);
}


// ES6中class方式实现类
class SuperTypeES6 {
    constructor(name, age) {
        this.name = name || 'Allen';
        this.age = age || 22;
    }
}

ES6使用class关键字对于学习过oo语言的开发人员是很好理解的,所以本篇文章主要介绍下ES5中关于原型的类和继承的几种模式。

创建对象

先介绍ES5中几种创建对象的方式

  • 工厂模式:把给对象赋值的工作抽象封装起来,然后在返回这个对象。就比如你把一个干干净净的对象交给一个工厂,然后工厂给你加工(增强对象),加工完后再还给你。
function Person(name, age) {
    let o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function () {
        console.log(this.age);
    }

    return o;
}

let p1 = new Person('job', 12);

工厂模式是一种比较简单,容易理解的模式,但是在对象识别的问题上有致命的缺陷。因为每个对象都是返回的都是o,所以每个对象只能被识别为Object而不能识别为Person。而构造函数模式就解决了这个问题。

  • 构造函数模式
function PersonConstructorModel(name, age) {
    this.name = name;
    this.age = age;
    this.sayName = function () {
        console.log(this.name)
    };
}

let p2 = new PersonConstructorModel('Allen', 22);

构造函数模式就很好的解决了对象识别的问题。但是有个问题,对于sayName这个公共方法,通过构造函数模式创建对象会在每一个对象上都会定义一次。因为函数在JS中是应用类型,所以每个函数都会占内存。这样与我们抽象的初衷就背道而驰了。所以我们使用了原型模式。

  • 原型模式(还有一种比较简洁的原型模式,使用那种方式需要重新指定原型的constructor的指向,但核心思想没什么差别,在这里就不赘述了)
function PersonPrototypeModel() {

}

PersonPrototypeModel.prototype.name = 'Allen';
PersonPrototypeModel.prototype.age = 22;
PersonPrototypeModel.prototype.friends = ['Tom', 'Jerry'];
PersonPrototypeModel.prototype.sayName = function () {
    console.log(this.name);
};

let p3 = new PersonPrototypeModel();

通过这样的方式就可以借助原型链的思想来实现对一类对象的属性的抽象了。每一个PersonPrototypeModel实例化的对象都共享name = 'Allen',age=22,friends = ['Tom', 'Jerry']以及sayName四个属性。但这种模式也有个很严重的问题,就是所有对象共享的friends是一个引用类型。所以你在某个对象上操作friends时也会导致其他同类对象friends的改变。所以为了解决这个问题,就衍生出了组合构造函数和原型的模式(使用最广泛,认可度最高的一种模式)。

  • 组合构造函数和原型模式
function PersonPrototypeAndCons() {
  // 为每一个对象都实例化自己的friedns
  this.friends = ['Tom', 'Jerry'];
}

PersonPrototypeAndCons.prototype.name = 'Allen';
PersonPrototypeAndCons.prototype.age = '22';
PersonPrototypeAndCons.prototype.sayName = function () {
    console.log(this.name);
};
let p4 = new PersonPrototypeAndCons();

这种模式的核心思想就是引用类型放在构造函数里,然后通过this来为每一个实例化对象在自己内部实例化一个引用类型的对象(本例中就是friends)。这样就既解决了封装的问题,又解决了每个对象共享引用类型的问题。

说完对象的几种构造模式我们再讲讲ES5中常用的几种继承方法。我所接触到的继承方法大概有6种:原型链继承,构造函数继承,组合继承,原型式继承,寄生继承,寄生组合式继承。对于这六种方法之间的优缺点有太多介绍的文章了,本人才疏学浅就不过多的介绍了,我就讲讲觉得比较有意思的几个继承方法吧。

  • 组合继承(一种集成了原型链继承和构造函数继承优点的方法)
// 组合继承
function CombineInheritSuper(name) {
    this.firends = ['rj', 'wy', 'qyf'];
    this.name = name;
}

CombineInheritSuper.prototype.sayName = function () {
    console.log(this.name, 'zuhe');
}

function CombineInheritSub(name, age) {
    CombineInheritSuper.call(this, name);
    this.age = age;
}

CombineInheritSub.prototype = new CombineInheritSuper();
let p2 = new CombineInheritSub('Allen', 22);

p2.sayName();

其实组合继承的方式就把子类的原型指向一个父类的实例对象,然后通过子类→子类原型(父类实例)→父类原型的方法来继承父类的属性。然后为了解决子类实例化对象共享父类实例上对象的问题(原型模式建立对象也有这种问题),所以在子类中调用父类的构造函数,这样子类的friends就会屏蔽父类实例化对象(子类原型)上的friends

  • 组合寄生继承
// 寄生组合继承
// 核心思想:使用不包含实例属性的原型副本
function inhert(superType, subType) {
    let clone = Object.create(superType.prototype);
    subType.prototype = clone;
    subType.constructor = SubType;
}

function SuperType(name, age) {
    this.name = name;
    this.age = age;
    this.friends = ['rj', 'wl', 'qyf'];
}

SuperType.prototype.sayName = function () {
    console.log(this.name);
}

function SubType(name, age, hobby) {
    SuperType.apply(this, arguments);
    this.hobby = hobby;
}

inhert(SuperType, SubType);

let p = new SubType('Allen', '22', 'base');
console.log(p.name, p.age, p.hobby);
console.log(p.friends);
console.log(SubType.prototype);
p.sayName();

其实组合寄生继承和组合构造函数和原型链的继承方式思想差不多,只不过这种方式少掉用了一次父类的构造函数,也就少实例化了一个父类对象,从而减少了内存占用。这也是一种比较理想的继承方式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容