js 对象继承

/*
    约定
*/

function Fun(){
    //私有属性
    var val = 1;                             //私有基本属性
    var arr = [1];                         //私有引入属性
    function fun(){};                       //私有函数(引用属性)

    //实例属性
    this.val = 1;                           //实例基本属性
    this.arr = [1];                       //实例引用属性
    this.fun = function(){};                 //实例函数(引用属性);
};

//原型属性
Fun.prototype.val = "jack";               //原型基本属性
Fun.prototype.arr = [1];                     //原型引用属性
Fun.prototype.fun = function(){};           //原型函数(引用属性)

js变量可分为两部分,基本类型和引用类型。

基本类型:基本类型比较简单,包括Undefined,Null,Boolean,Number,String,基本类型就是简单的数据段;
引用类型:引用类型值可能由多个值构成,引用类型保存在内存中,而js是不能直接访问内存的,所以对于引用类型,操作的不是实际的对象,而是对对象的引用。

一、简单原型链

function  Super(){
  this.val = 1;
  this.arr = [1];
};

function Sub(){

};

Sub.prototype = new Super();

var Sub1 = new Sub();
var Sub2 = new Sun();

Sub1.val = 5;
Sub1.arr.push(2);

alert(Sub1.val);  //5
alert(Sub2.val);  //1

alert(Sub1.arr);  //1,2
alert(Sub2.arr);  //1,2

//可以看到,修改sub1.arr后sub2.arr也变了,因为来自原型对象的引用属性是所有实例共享的。

如果不懂,可查看js基本类型与引用类型详解

二、借用构造函数

function gou(val){
    console.log(typeof val);
    this.val = val;
    this.arr = [1];

    this.fun = function(){

    };
};

function fun(val){
    gou.call(this,val);
};

var fun1 = new fun(1);
var fun2 = new fun(2);
fun1.arr.push(2);

console.log(fun1.val);    //1
console.log(fun2.val);    //2

console.log(fun1.arr);    //1,2
console.log(fun2.arr);    //1

alert(sub1.fun === sub2.fun);   // false

借父类的构造函数来增强子类实例,等于是把父类的实例属性复制了一份给子类实例装上了(完全没有用到原型)
缺点:
无法实现函数复用,每个子类实例都持有一个新的fun函数,太多了就会影响性能,内存爆炸。。
P.S.好吧,刚修复了共享引用属性的问题,又出现了这个新问题。

三、组合继承(最常用)

function zuhe(){
    this.val = 1;
    this.arr = [1];
};

zuhe.prototype.fun1 = function(){};
zuhe.prototype.fun2 = function(){};

function newSub(){
    //通过call函数继承父类的基本数据和引用数据。
    zuhe.call(this);    //核心
};

newSub.prototype = new zuhe();
var zh1 = new newSub();
var zh2 = new newSub();

zh1.val = 2;
zh1.arr.push(2);

console.log(zh1);    //{val:2,arr:[1,2]}
console.log(zh2);    //{val:1,arr:[1]}

console.log(zh1.fun1 === zh2.fun1);  //true

把实例函数都放在原型对象上,以实现函数复用。同时还要保留借用构造函数方式的优点

四、寄生组合继承

组合继承是js最常用的继承模式,组合继承最大的问题就是无论在什么情况下,都会调用两次构造函数:一次是在创建子类型原型时,另一次是在子类型构造函数内部。

function SuperType(name){
 this.name = name;
 this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
 alert(this.name);
}
 
function SubType(name, age){
 SuperType.call(this, name);  //第二次调用SuperType()
  
 this.age = age;
}
SubType.prototype = new SuperType();  //第一次调用SuperType()
SubType.prototype.sayAge = function(){
 alert(this.age);
}

在第一次调用SuperType构造函数时,SubType.prototype会得到两个属性: name和colors; 他们都是SuperType的实例属性,只不过现在位于SubType的原型中。
当调用SubType构造函数时,又会调用一次SuperType构造函数,这一次又在新对象上创建了实例属性name和colors。
于是这两个属性就屏蔽了原型中的两个同名属性。
寄生组合式继承就是为了解决这一问题。

function inheritPrototype (subType,superType){
    var protoType = Object.create(superType.prototype);     //创建对象
    protoType.constructor = subType;                        //增强对象
    subType.prototype = protoType;                          //指定对象
};

function SuperType(name){
    this.name = name;
    this.colors = ["red","blue","green"];
};

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

function SubType(name,age){
    SuperType.call(this,name);

    this.age = age;
};

inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function(){
    alert(this.age);
};

var instance = new SubType("jack",28);
instance.sayName();         //jack
instance.sayAge();          //28

## 方法二
function beget(obj){   // 生孩子函数 beget:龙beget龙,凤beget凤。
    var F = function(){};
    F.prototype = obj;
    return new F();
}
function Super(){
    // 只在此处声明基本属性和引用属性
    this.val = 1;
    this.arr = [1];
}
//  在此处声明函数
Super.prototype.fun1 = function(){};
Super.prototype.fun2 = function(){};
//Super.prototype.fun3...
function Sub(){
    Super.call(this);   // 核心
    // ...
}
var proto = beget(Super.prototype); // 核心
proto.constructor = Sub;            // 核心
Sub.prototype = proto;              // 核心
 
var sub = new Sub();
alert(sub.val);
alert(sub.arr);

五、原型式

function beget(obj){
    var F = function(){};
    F.prototype = obj;
    return new F();
};

function Super (){
    this.val = 1;
    this.arr = [1];
};

//拿到父类对象
var sup = new Super ();
//生孩子
var sub = beget(sup);
//增强
sub.attr1 = 1;
sub.attr2 = 2;
console.log(sub);
console.log(sub.attr1);
console.log(sub.arr);

优点:
从已有对象衍生新对象,不需要创建自定义类型(更像是对象复制,而不是继承。。)
缺点:
原型引用属性会被所有实例共享,因为是用整个父类对象来充当了子类原型对象,所以这个缺陷无可避免无法实现代码复用(新对象是现取的,属性是现添的,都没用函数封装,怎么复用)
P.S.这东西和继承有很大关系吗?为什么尼古拉斯把它也列为实现继承的一种方式?关系不大,但有一定关系

六、寄生式

寄生式是一种模式,并不是只能用来继承

function beget(obj){   // 生孩子函数 beget:龙beget龙,凤beget凤。
    var F = function(){};
    F.prototype = obj;
    return new F();
}
function Super(){
    this.val = 1;
    this.arr = [1];
}
function getSubObject(obj){
    // 创建新对象
    var clone = beget(obj); // 核心
    // 增强
    clone.attr1 = 1;
    clone.attr2 = 2;
    //clone.attr3...
 
    return clone;
}
 
var sub = getSubObject(new Super());
alert(sub.val);     // 1
alert(sub.arr);     // 1
alert(sub.attr1);   // 1

注意:beget函数并不是必须的,换言之,创建新对象 -> 增强 -> 返回该对象,这样的过程叫寄生式继承,新对象是如何创建的并不重要(用beget生的,new出来的,字面量现做的。。都可以)
优点:
还是不需要创建自定义类型
缺点:
无法实现函数复用(没用到原型,当然不行)
P.S.剧情解析:有缺陷的寄生式继承 + 不完美的组合继承 = 完美的寄生组合式继承,不妨回去找找看哪里用到了寄生

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

推荐阅读更多精彩内容

  • 内容来自《JavaScript高级程序设计》第三版第6章第3节 原型链 ECMAScript中描述了 原型链的概念...
    angelwgh阅读 217评论 0 0
  • 博客内容:什么是面向对象为什么要面向对象面向对象编程的特性和原则理解对象属性创建对象继承 什么是面向对象 面向对象...
    _Dot912阅读 1,367评论 3 12
  • 1.继承(接口继承和实现继承) 继承是 OO 语言中的一个最为人津津乐道的概念。许多 OO 语言都支持两种继承方式...
    believedream阅读 883评论 0 3
  • 本章内容 理解对象属性 理解并创建对象 理解继承 面向对象语言有一个标志,那就是它们都有类的概念,而通过类可以创建...
    闷油瓶小张阅读 808评论 0 1
  • 搬进新房已经好几天了,每次看到宽敞的房间以及齐全的设施,我不止一次地怀疑:难道这宽敞明亮的房间真的自己住的吗?这里...
    西瓜甜甜啦阅读 637评论 28 30