JS继承

JS中的继承

许多OO语言都支持两种继承方式:接口继承和实现继承;

因为JS中没有类和接口的概念 , 所以JS不支持接口继承 , 只支持实现继承;

实现继承主要是依靠原型链来实现的;

1.原型链

基本思想:

利用原型让一个对象继承另一个对象的属性和方法 ;

回顾构造函数,原型和实例的关系 :

每个构造函数都有一个原型对象 ; 原型对象都包含一个指向构造函数的指针 , 而实例包含一个指向原型 ;

   function Person(name,age) {
       this.name = name;
       this.age = age;
   }
   Person.prototype.speakName = function () {
       console.log(this.name)
   }
   
   var p1 = new Person("老王",25);
   var p2 = new Person("小王",15);

那假如我们把一个实例对象,作为另外一个实例的原型对象会发生什么呢?

      //定义一个构造函数。
    function Father () {
        // 添加name属性.  默认直接赋值了。当然也可以通过构造函数传递过来
        this.name = "马云";
    }
      //给Father的原型添加giveMoney方法
    Father.prototype.giveMoney = function () {
        alert("我是Father原型中定义的方法");
    }
    //再定义一个构造函数。
    function Son () {
        //添加age属性
        this.age = 18;
    }
    //关键地方:把Son构造方法的原型替换成Father的对象。  因为原型是对象,任何对象都可以作为原型
    Son.prototype = new Father();
      //给Son的原型添加getMoney方法
    Son.prototype.getMoney = function () {
        alert("我是Son的原型中定义的方法");
    }
    //创建Son类型的对象
    var son1 = new Son();

    //发现不仅可以访问Son中定义属性和Son原型中定义的方法,也可以访问Father中定义的属性和Father原型中的方法。
    //这样就通过原型完成了类型之间的继承。 
    // Son继承了Father中的属性和方法,当然还有Father原型中的属性和方法。
    son1.giveMoney();
    son1.getMoney();
    alert("Father定义的属性:" + son1.name);
    alert("Son中定义的属性:" + son1.age);
  1. 定义Son构造函数后,我们没有再使用Son的默认原型,而是把他的默认原型更换成了Father类型对象。
  2. 这时,如果这样访问 son1.name, 则先在son1中查找name属性,没有然后去他的原型( Father对象)中找到了,所以是"马云"。
  3. 如果这样访问 son1.giveMoney(), 先在son1中找这个方法,找不到去他的原型中找,仍然找不到,则再去这个原型的原型中去找,然后在 Father的原型对象中 找到了。
  4. 从图中可以看出来,在访问属性和方法的时候,查找的顺序是这样的:对象->原型->原型的原型->...->原型链的顶端。 就像一个链条一样,这样 由原型连成的"链条",就是我们经常所说的原型链。
  5. 从上面的分析可以看出,通过原型链的形式就完成了JavaScript的继承。

2.默认原型链

原型链的顶端一定是Object这个构造函数的原型对象。

3. 测试数据类型

isPrototypeOf( 对象 ) : 这是个 原型对象 的方法,参数传入一个对象,判断参数对象是不是由这个原型派生出来的。 也就是判断这个原型是不是参数对象原型链中的一环。

4.原型链的缺陷

父类型的构造函数创建的对象,会成为子类型的原型

缺陷:

  1. 父类型中定义的实例属性 , 会成为子类型的原型属性 ; 子类型原型中的属性被所有的子类型的实例所共用;(属性共用)
  2. 原型链继承中,只有一个地方用到了父类型的构造函数 , 也就意味着只有一次传递参数的机会 , 并且参数的值在所有的子类实例里都是一样的;(参数传递困难)

5.借用构造函数弥补缺陷

如何借用?

​ 使用call或apply完成==构造函数的借调== ,目的在于借用父类型(或其他类型)的构造函数 , 完成子类型的属性添加 ;

原理

​ call和apply 可以更改 构造方法内部的 this 指向指定的对象上;

    function Father (name,age) {
        this.name = name;
        this.age = age;
    }
    //如果这样直接调用,那么father中的this只的是 window。 因为其实这样调用的: window.father("李四", 20)
    // name 和age 属性就添加到了window属性上
    Father("李四", 20);
    alert("name:" + window.name + "\nage:" + window.age);  //可以正确的输出

    //使用call方法调用,则可以改变this的指向
    function Son (name, age, sex) {
        this.sex = sex;
          //调用Father方法(看成普通方法),第一个参数传入一个对象this,则this(Son类型的对象)就成为了Father中的this
        Father.call(this, name, age);
    }
    var son = new Son("张三", 30, "男");
    alert("name:" + son.name + "\nage:" + son.age + "\nsex:" + son.sex);
      alert(son instanceof Father); //false

注意

​ 在这里并不是真正的继承 , 借用的构造函数在这里也只是作为一个普通的方法;

缺陷

​ 单独使用借用时 ; 父类型原型对象中的共享属性和方法 ; 通过借用无法获取 ;

6. 组合继承

组合函数利用了原型继承和构造函数借调继承的优点,组合在一起。成为了使用最广泛的一种继承方式。

      //定义父类型的构造函数
    function Father (name,age) {
        // 属性放在构造函数内部
        this.name = name;
        this.age = age;
        // 方法定义在原型中
        if((typeof Father.prototype.eat) != "function"){
            Father.prototype.eat = function () {
                alert(this.name + " 在吃东西");
            }
        }  
    }
      // 定义子类类型的构造函数
    function Son(name, age, sex){
          //借调父类型的构造函数,相当于把父类型中的属性添加到了未来的子类型的对象中
        Father.call(this, name, age);
        this.sex = sex;
    }
      //修改子类型的原型为父类型的对象。这样就可以继承父类型中的方法了。
    Son.prototype = new Father(    );
    var son1 = new Son("志玲", 30, "女");
    alert(son1.name);
    alert(son1.sex);
    alert(son1.age);
    son1.eat();

总结

共同的方法和属性通过原型链继承 ;

需要传入参数的属性(子类型实例中的不相同属性)通过 call或apply的方法 借调一个 构造函数来完成属性的添加 (子类属性中的同名属性会覆盖掉原型中的属性);

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

推荐阅读更多精彩内容

  • 原文链接 js的继承有6种方式,大致总结一下它们各自的优缺点,以及它们之间的关系。 1.原型链 js的继承机制不同...
    空_城__阅读 795评论 0 11
  • 原型链 继承是通过创建SuperType的实例,并将该实例赋给SubType.prototype实现的。继承的本质...
    大橙子CZ阅读 492评论 0 1
  • 时间:2017.9.12 形式:线下 议题:工作要不要继续,想回老家发展。 背景介绍:案主目前在青岛工作,男朋友在...
    宓儿_2b3e阅读 203评论 0 1
  • 今天从早上十点(俺们这嘎达冬天是北京时间十点上班)出门,和一帮同事~下农村~一直到21点多回来,被同事叫去吃了碗面...
    至简从心阅读 806评论 10 19
  • 最近,看到了一个超脑的问答:一男子想追求喜欢的女神,但不知该如何表达,于是去找情感咨询师寻问,男子说“我喜欢女神很...
    漏斗中柱阅读 265评论 0 0