2019-07-05

继承

子类的原型对象--继承

// 类式继承
// 声明父类
function SupperClass(){
    this.superValue = true
}
// 为父类添加公有方法
SupperClass.prototype.getSuperValue = function(){
    return this.superValue;
}
// 声明类
function SubClass(){
    this.subValue = false;
}
// 继承父类
SubClass.prototype = new SuperClass()
// 为子类添加公有方法
SubClass.prototype.getValue = function(){
    return this.subValue
}
var instance = new SubClass()
instance.getSuperValue()    // true
instance.getSubValue()      // false

这里声明类两个类, 而且第二个类的原型prototype被赋予了第一个类的实例
类的原型对象的作用就是为类的原型添加共有方法,但类不能直接访问这些属性和方法,必须通过原型prototype来访问而我们实例化一个父类的时候,新创建的对象复制了父类的构造函数内的属性与方法并且将原形proro指向了父类的原型对象,这样就拥有了父类的原型对象上的属性与方法,并且这个新创建的对象可直接访问到父类原型对象的属性与方法.如果我们将这个新创建的对象赋值给子类的原型, 那么子类的原型就可以访问到父类的原型属性和方法.

我们可以通过instanceof来检测某个对象是否是某个类的实例,或者说某个对象是否继承了某个类.这样就可以判断对象与类之间的继承关系了.
instanceof是通过判断对象的prototype链来确定这个对象是否是某个类的实例,而不关心对象与类的自身结构

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

instanceof 是判断前面的对象是否是后面的类(对象)的实例, 它并不表示两者的继承.
我们在实现subClass继承superClass时是通过将superClass的实例赋值给subClass的原型prorotype,所以SubClass.prototype继承了superClass
console.log(SubClass.prototype instanceof SuperClass) //true
所创建的所有对象都是Object的实例
console.log(instance instanceof Object) // true
这种类继承还有2个缺点.
一: 由于子类通过原型prototype对父类实例化,继承了父类.所以说父类中的共有属性要是引用类型,就会在子类中被所有实例共有, 因此一个子类的实例更改子类原型从父类构造函数中继承的共有属性就会影响到其它子类.

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

instance1 改变了instance2的值
二:由于子类实现的继承是靠原型prototype对父类的实例化实现的, 因此在创建父类的时候,是无法向父类传递参数的, 因为在实例化父类的时候也无法对父类否找函数内的属性进行初始化.

构造函数继承

// 构造函数式继承
// 声明父类
function SuperClass(id){
    // 引用类型共有属性
    this.books = ['js', 'css', 'html'];
    // 值类型共有属性
    this.id = id;
}
// 父类声明原型方法
SupperClass.prototype.showBooks = function(){
    console.log(this.books)
}
// 声明子类
function subClass(id){
    // 继承父类
    SuperClass.call(this, id)
}
// 创建第一个子类的实例
var instance1 = new subClass(10)
// 创建第二个子类的实例
var instance2 = new subClass(1);
instance1.books.push('设计模式')
console.log(instance1.books) // ['js', 'css', 'html', '设计模式']
console.log(instance1.id) // 10
console.log(instance1.books) // ['js', 'css', 'html']
console.log(instance1.id) // 1
instance1.showBooks() // TypeErroe

SuperClass.call(this, id),call这个方法可以更改函数的作用环境,因此在子类中,对superClass调用这个方法就是将子类汇总的变量在父类中执行了一遍,由于父类中是给this绑定属性的,因此子类自然也就继承了父类的共有属性.由于这种类型的继承没有涉及原型prototype,所以父类的原型方法自然不会倍子类继承, 而如果想要被子类继承就必须放在构造函数中,这样创建出的每个实例都会单独拥有一份而不是共用,这样就违背了代码复用的原则.综合这两种模式的优点,后来就有了组合继承

组合继承

类式继承是用过子类的原型prototype对父类实例化来实现的,构造函数式继承是通过在子类的构造函数作用环境中执行一次父类的构造函数来实现的,所以只要在继承中同时做到这两点即可.

// 组合式继承
// 声明父类
function SuperClass(name){
    // 值类型共有属性
    this.name = name;
    // 应用类型共有属性
    this.books = ['js', 'css', 'html'];
}
// 父类原型共有方法
SuperClass.prototype.getName = function(){console.log(this.name)}
// 声明子类
function SubClass(name, time){
    // 构造函数式继承父类name属性
    SuperClass.call(this, name)
    // 子类中新增共有属性
    this.time = time;
}
// 类式继承 子类原型继承父类
SubClass.prototype = new SupperClass();
// 子类原型方法
SubClass.prototype.getTime = function(){console.log(this.time)}

在子类构造函数中执行父类构造函数,在子类原型上实例化父类就是组合模式,这样就融合了类式继承的优点

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

这种继承模式在使用构造函数继承时执行了一遍父类的构造函数, 而在实现子类原型的类式继承时又调用了一遍父类构造函数.因此父类构造函数调用了两遍.

洁净的继承者--原型式继承

借助原型prototype可以根据已有的对象创建一个新的对象, 同时不必创建新的自定义对象类型

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

它是对类式继承的一个封装,过渡对象就相当于类式继承中的子类,只不过在原型中作为一个过渡对象出现的, 目的是为了创建要返回的新的实例化对象,由于F过渡类的构造函数中无内容, 开销比较小, 使用起来也比较方便.

var book = {
    name: 'js book';
    alikeBook: ['css', 'js']
}
var newBook = inheritObject(book);
newBook.name = 'ajax book';
newBook.alikeBook.push('as book')
var otherBook = inheritObject(book);
newBook.name = 'flash book';
newBook.alikeBook.push('xml book')
console.log(newBook.name); // ajax book
console.log(newBook.alikeBook); // ['css'. 'js', 'as book', 'xml book']
console.log(otherBook.name); // flash book
console.log(otherBook.alikeBook); // ['css'. 'js', 'as book', 'xml book']
console.log(book.name); // js book
console.log(book.alikeBook); // ['css'. 'js', 'as book', 'xml book']

寄生式继承

function inheritObject(o){
    function F(){}
    F.prototype = o;
    return new F();
}
// 寄生式继承
// 声明基对象
var book = {
    name: "js book",
    alikeBook: ["css", "js", "html"]
};
function createBook(obj){
    // 通过原型继承方式创建新对象
    var o = new inheritObject(obj)
    o.getName = function(){
        console.log(name)
    };
    // 返回拓展后的新对象
    return o;
}

寄生式继承就是对原型继承的第二次封装,并且在这第二次封装中对继承的对象进行拓展,这样创建的对象不仅仅有父类中的属性和方法而且还添加新的属性和方法
寄生大概值得就是像寄生虫一样寄托于某个对象内部生长。当然寄生式继承这种增强新创建对象的继承思想也是寄托于原型继承模式

终极继承者--寄生组合式继承

组合式继承将类式继承同构造函数继承组合使用,但是这种方式有一个问题,就是子类不是父类的实例,而子类的原型是父类的实例,所以才有了寄生组合式继承
寄生式寄生式继承, 寄生式继承依托于原型继承, 原型继承又与类继承相像,另一种继承模式式构造函数继承, 子类不是父类实例的问题是由于类是继承引起的

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

组合式继承中,通过构造函数继承的属性和方法是没问题的, 所以这里我们组要理解通过寄生式继承重新继承父类的原型。我们需要继承的仅仅是父类的原型,不再需要调用父类的构造函数,换句话说,在构造函数继承中我们已经调用了父类的构造函数,因此我们需要的就是父类的原型对象的一个副本,而这个副本我们通过原型继承便可得到,但是这么直接赋值给子类会有问题,因为父类原型对象复制得到的复制对象p中的constructor指向的不是subClass子类对象,因此寄生式继承中要对复制对象p做一次增强,修复其constructor属性指向不正确的问题,最后将得到的复制对象p复制给子类的原型,这样子类的原型就继承了父类的原型并且没有执行父类的构造函数

// 定义父类
function SuperClass(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
// 定义父类原型方法
SuperClass.prototype.getName = function (){
    console.log(this.name);
};
// 定义子类
function SubClass(name, time){
    // 构造函数式继承
    SuperClass.call(this, name)
    // 子类新增属性
    this.time = time;
}
// 寄生式继承父类原型
inheritPrototype(SubClass, SuperClass);
// 子类新增原型方法
SubClass.prototype.getTime = function(){
    console.log(this.time);
};
// 创建两个测试方法
var instance1 = new SubClass("js book", 2014)
var instance2 = new SubClass("css book", 2015)
console.log(instance1.colors); // ["red", "blue", "green"];
console.log(instance2.colors); // ["red", "blue", "green"];
instance2.getName(); // 'css book'
instance2.getTime(); // '2015'

多继承

在JS中继承是依赖于原型prototype链实现的, 只有一条原型链, 所以理论不能继承多个父类

extend

// 单继承 属性赋值
var extend = function(target, source){
    // 遍历源对象的属性
    for(var property in source){
        // 将源对象中的属性复制到目标对象中
        targer[property] = source[property]
    }
    // 返回目标对象
    return target;
}
var book = {
    name: 'js',
    alike: ['css', 'js', 'html']
}
var anotherBook = {
    color: 'red'
}
extend(anotherBook, book)
console.log(anotherBook.name) // js
console.log(anotherBook.alike) // ['css', 'js', 'html']
anotherBook.alike.push('ajax');
anotherBook.name = '设计模式';
console.log(anotherBook.name); //设计模式
console.log(anotherBook.alike); // ['css', 'js', 'html', 'ajax']
console.log(book.name); // js
console.log(book.alike); // ['css', 'js', 'html', 'ajax']

多继承

// 多继承 属性复制
var mix = fuction(){
    var i = 1,
        len = arguments.length,
        target = arguments[0],
        arg;
    // 遍历被继承的对象
    for (; i < len; i++){
        // 缓存当前对象
        arg = argument[i];
        // 遍历被缓存的对象中的属性
        for (var property in arg){
            // 将被缓存对象中的属性复制到新目标中
            target[property] = arg[property]
        }
    }
    return target;
}

绑定到Object上

Object.prototype.mix = function(){
    var i = 0,
        len = arguments.length,
        arg;
    // 遍历被继承的对象
    for (; i < len; i++){
        // 缓存当前对象
        arg = argument[i];
        // 遍历被缓存的对象中的属性
        for (var property in arg){
            // 将被缓存对象中的属性复制到新目标中
            this[property] = arg[property]
        }
    }
}
otherBook.mix(book1, book2);

多态

多态是同一个方法多用调用方式

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

推荐阅读更多精彩内容