Javascript —— 继承

本文是本人学习,张容铭著的《Javascript 设计模式》的笔记与总结。仅供学习交流。

每个类都有3个部分:

1、第一部分是构造函数内的,这是供实例化对象复制用的。

2、第二部分是构造函数外的,直接通过点语法添加,这是供类使用的,实例化对象是访问不到的。

3、第三部分是类的原型中的,实例化对象是可以通过其原型链间接地访问到,也是为供所有实例化对象所共用的。

一、子类的原型对象————类式继承

类式继承:通过子类的原型prototype对父类实例化来实现。

//类式继承
//声明父类
function SuperClass(){
    this.superValue = true;
}
//为父类添加共有方法
SuperClass.prototype.getSuperValue = function () {
    return this.superValue;
};

// 声明子类
function SubClass(){
    this.subValue = false;
}

// 继承父类
SubClass.prototype = new SuperClass ();

// 为子类添加共有方法
SubClass.prototype.getSubValue = function (){
    return this.subValue;
}

上面声明了2个类,而且第二个类的原型prototype 被赋予了第一个类的实例。
类的原型对象的作用就是为类的原型添加共有方法,但类不能直接访问这些属性个方法,必须通过原型prototype来访问。 


var instance = new SubClass();
console.log(instance.getSuperValue() ); // true
console.log(instance.getSubValue()); //false

通过instanceof 来检测某个对象是否是某个类的实例,或者说某个对象是否继承了某个类。

console.log(instance instanceof SuperClass); //true
console.log(instance instanceof SubClass); //true
console.log(SubClass instanceof SuperClass); //false

第三个false,说明 ,instanceof 是判断前面的对象是否是后面类(对象)的实例,它并不表示两者的继承。

>> SubClass 继承 SuperClass 时是通过将superClass的实例赋予给SubClass 的原型prototype,所以说 SubClass.prototype继承了SuperClass

console.log(SubClass.prototype instanceof SuperClass); //true

>> Object是所有对象的祖先
console.log(instance instanceof Object);    //true

类式继承的缺点

1、一个子类的实例更改子类原型从父类构造函数中继承来的共有属性就会直接影响到其他子类。原因:
由于子类通过其原型prototype对父类实例化,继承了父类。所以说父类中的共有属性要是引用类型,就会在子类中被所有实例共用。

示例代码:

function SuperClass () {
    this.books = ['Javascript','html','css'];
}
function SubClass () {}
SubClass.prototype = new SuperClass ();
var instance1 = new SubClass();
var instance2 = new SubClass();
console.log(instance2.books); // ['Javascript','html','css'];
instance1.books.push('设计模式');
console.log(instance2.books); // ['Javascript','html','css','设计模式'];

2、在实例化父类的时候也无法对父类构造函数内的属性进行初始化。原因:

由于子类实现的继承是靠其原型prototype对父类的实例化实现的,因此在创建父类的时候,是无法向父类传递参数的。

二、创建即继承——构造函数继承

构造函数式:通过在子类的构造函数环境中执行一次父类的构造函数来实现的。用call(this,变量);

//构造函数式继承
//声明父类

function SuperClass (id){
    //引用类型共有属性
    this.books = ['JavaScript','html','css'];
    //值类型共有属性
    this.id = id;
}
//父类声明原型方法
SuperClass.prototype.showBooks = function (){
    console.log(this.books);
}
//声明子类
function SubClass (id) {
    //继承父类
    SuperClass.call (this, id);
}

//创建第一个子类的实例
var instance1 = new SubClass (10);
var instance2 = new SubClass (11);

instance1.books.push('设计模式');
console.log(instance1.books);   // ['JavaScript','html','css','设计模式'];
console.log(instance1.id);  //10
console.log(instance2.books);   //['JavaScript','html','css'];
console.log(instance2.id);  //11

instance1.showBooks (); //TypeError

构造函数式继承的精华:
SuperClass.call (this, id);

优缺点:

call将子类的变量在父类中执行一遍,由于父类是给this绑定属性的,所以子类自然也就继承了父类的共有属性。而这种类型的继承没有涉及原型prototype,所以父的原型方法自然不会被子类继承。如果想被子类继承就必须要放在构造函数中。

三、组合继承 (类式+ 构造式)

组合继承结合了类式继承 + 构造函数继承

1、类式继承:通过子类的原型prototype对父类实例化来实现。

2、构造函数式:通过在子类的构造函数环境中执行一次父类的构造函数来实现的。用call(this,变量);

//组合式继承
//声明父类
function SuperClass(name){
    //值类型共有属性
    this.name = name;
    //引用类型共有属性
    this.books = ['html'、'css'、'Javascript'];
}

// 父类原型共有方法

SuperClass.prototype.getName = function () {
    console.log(this.name);
};
//声明子类
function SubClass (name, time) {
    //构造函数式继承父类name属性
    SuperClass.call(this.name);
    //子类中新增共有属性
    this.time = time;
}

//类式继承 子类原型继承父类
SubClass.prototype = new SuperClass();
//子类原型方法
SubClass.prototype.getTime = function (){
    console.log(this.time);
}

如上,在子类构造函数中执行父类构造函数,在子类原型上实例化父类就是组合模式。这样融合了类式继承和构造函数继承的优点,并且过滤掉其缺点,你测试看看。

测试代码:

var instance1 = new SubClass("js book", 2014);
instance1.book.push("设计");
console.log(instance1.books);   //["html","css","Javascript","设计模式"]
instance1.getName ();   //js book
instance1.getTime ();   // 2014

var instance2 = new SubClass ("css book", 2013);
console.log(instance2.books);   //["html","css","Javascript"]
instance2.getName ();   //css book
instance2.getTime ();   //2013

优缺点

子类的实例中更改父类继承下来的引用类型属性如books,不会影响到其他实例,并且子类实例化过程中又能将参数传递到父类的构造函数中。

四、原型式继承

//原型式继承
function inheritObject (o){
    //声明一个过渡函数对象
    function F (){ }
    //过渡对象的原型继承父对象
    F.prototype = o;
    //返回过渡对象的一个实例,该实例的原型继承了父对象
    return new F();
}

这是类式继承的一个封装

测试代码:

var book = {
    name : "js book",
    alikeBook : ["css book","html book"]
}

var newBook = inheritObject (book);
newBook.name = "ajax book";
newBook.alikeBook.push("xml book");

var otherBook = inheritObject (book);
otherBook.name = "flash book";
otherBook.alikeBook.push("as book");

console.log(newBook.name);  //ajax book

console.log(newBook.alikeBook); //["css book","html book","xml book","as book"]

console.log(otherBook.name);    //flash book

console.log(otherBook.alikeBook);   //["css book","html book","xml book","as book"]

console.log(book.name);     // js book

console.log(book.alikeBook);    //["css book","html book","xml book","as book"]

优缺点

跟类式继承一样,父类对象book中的值类型的属性被复制,引用类型的属性被共同用

五、寄生式继承

// 寄生式继承
// 声明基对象
var book = {
    name : "js book",
    alikeBook : ["css book", "html book"]
}

function createBook (obj){
    //通过原型继承方式创建新对象
    var o = new inheritObject(obj);
    //拓展新对象
    o.getName = function (){
        console.log(name);
    };
    //返回拓展后的新对象
    return o;
}

优缺点

寄生式继承是对原型的第二次封装,并且在这第二次封装过程中对继承的对象进行了拓展,这样新创建的对象不仅仅有父类中的属性和方法而且还添加新的属性和方法。

六、寄生组合式继承

/**
* 寄生式继承 继承原型
* 传递参数 subClass 子类
* 传递参数 superClass 父类
**/
function inheritPrototype(subClass, superClass){
    //复制一份父类的原型副本保存在变量中
    var p = inheritObject (superClass.prototype);
    //修正因为重写子类原型导致子类的constructor 属性被修改;
    p.constructor = subClass;
    //设置子类的原型
    subClass.prototype = p;
}

测试用例

// 定义父类
function SuperClass (name){
    this.name = name;
    this.colors = ["red","blue","green"];
}
// 定义父类原型方法
SubClass.prototype.getName = function () {
    console.log(this.name);
}
// 定义子类
function SubClass (name, time){
    // 构造函数式继承
    SuperClass.call (this, name);
    // 子类新增属性
    this.time = time;
}

//寄生式继承父类原型
inheriPrototype(SubClass, SuperClass);
//子类新增原型方法
SubClass.prototype.getTime = function (){
    console.log(this.time);
};
// 创建两个测试方法
var instance1 = new SubClass("js book", 2014);
var instance2 = new SubClass("css book", 2013);

测试代码

instance1.colors.push("black");
console.log(instance1.colors);  //["red","blue","green","black"]
console.log(instance2.colors);  //["red","blue","green"]
instance2.getName ();   //css book
instance2.getTime ();   //2013

七、多继承

一些面向对象语言中支持多继承,在javascript中也能实现,但是有些局限性。只有一条原型链,理论上不能继承多个父类。而通过 extend 方法可以做到。

// 单继承 属性复制

var extend = function (target, source){
    //遍历源对象中的属性
    for (var property in source){
        //将源对象中的属性复制到目标对象中
        target[property] = source[property];
    }
    //返回目标对象
    return target;
}

extend方法是一个浅复制的过程,他只能复制值类型的属性,对于引用类型的属性是不行的。

单继承测试代码

var book =  {
    name : 'javascript 设计模式',
    alike : ['css','html','javascript']
}
var anotherBook = {
    color : 'blue'
}
extend(anotherBook, book);
console.log(anotherBook.name);  //Javascript 设计模式
console.log(anotherBook.alike);     // ['css','html','javascript']

anotherBook.alike.push('ajax');
anotherBook.name = '设计模式';
console.log(anotherBook.name);  // 设计模式
console.log(anotherBook.alike);     //['css','html','javascript','ajax']
console.log(book.name);   //Javascript 设计模式
console.log(book.alike);   // ['css','html','javascript']

多继承

//多继承 属性复制
var mix = function (){
    var i = 1,  // 从第二个参数起为被继承的对象
        len = arguments.length, //获取参数长度
        target = arguments[0],  ///第一个对象为目标对象
        arg;    //缓存参数对象
    // 遍历被继承对象
    for(; i<len; i++){
        // 缓存当前对象
        arg = arguments[i];
        //遍历被继承对象中的属性
        for (var property in arg){
            //将被继承对象中的属性复制到目标对象中
            target[property] =arg[property];
        }
    }
    // 返回目标对象
    return target;
}

//将mix绑定到原生对象Object上,所有的对象都可以拥有这个方法。
Object.prototype.mix = function (){
    var i = 0,  //从第一个参数起为被继承的对象
        len = arguments.length, // 获取参数长度
        arg;
    //遍历被继承的对象
    for(; i<len; i++){
        // 缓存当前对象
        arg = arguments[i];
        //遍历被继承对象中的属性
        for (var property in arg){
            //将被继承对象中的属性复制到目标对象中
            this[property] = arg[property];
        }
    }
}

// 调用
var book1 = {name:'书一',publish:'广州'};
var book2 = {cover:'http://www.baidu.com',title:'sdfdf'};
var otherBook = {content:'df'};
otherBook.mix(book1,book2);
console.log(JSON.stringify(otherBook)); //{"content":"df","name":"书一","publish":"广州","cover":"http://www.baidu.com","title":"sdfdf"}

八、多种调用方式————多态

//场景: 一个参数返回平方, 两个参数相乘,三个参数相加再相乘,三个以上相加
// 多态
function total () {
    // 获取参数
    var arg = arguments,
        //获取参数长度
        len = arg.length;
        switch(len){
            //如果没有参数
            case 0:
                return 0;
            case 1:
                return Math.pow(10,arg[0]);
            case 2:
                return arg[0] * arg[1];
            case 3:
                return (arg[0] + arg[1])*arg[3];
            defult:
                var a=0;
                for(var i=0; i<len ;i++){
                    a += arg[i];
                }
                return a;
        }
    
}

根据不同数量的参数有不同的处理

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

推荐阅读更多精彩内容

  • 我是谁,我来自哪,我是谁的谁 想必大家一定在学习或者开发过程常常被JS独有的原型继承拨过不少脑弦吧,为何不迎问题而...
    俗三疯阅读 315评论 0 2
  • JavaScript有多种继承模式,总结起来用到的方法有:原型链的传递、构造函数的借用、对象的复制。 对于原型链和...
    minxuan阅读 6,937评论 5 29
  • 例子 我们生成两个构造函数,后面的例子都是让‘’猫‘’继承‘’动物‘’的所有属性和方法。 动物(为了更好的理解各种...
    流光号船长阅读 317评论 0 1
  • 之前的JavaScript继承一文中已经介绍了继承,但那篇只能算简介。本篇结合原型链详细介绍一下JavaScrip...
    张歆琳阅读 2,579评论 0 8
  • 稍有所想不得不敲打纪录下来,想到工作,想到生活,想到友谊,想到了我还有用。更是想到现在如重生般的自己! ...
    贤之阅读 299评论 0 0