理解对象继承

对象继承

什么是继承

继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法

实现继承就需要完成两件事

  1. 子类可以获取父类的属性和方法
  2. 子类可以追加属性方法

怎么实现继承

对于js 来说,每个对象都可以添加属性和方法,所以我们更关注的是如何获取父类的属性和方法

根据js 的 原型链 的特性 我们可以通过原型链来获取父类的属性和方法

原型链为什么可以获取父类的属性和方法

当试图得到一个对象的某个属性时,

  1. 如果这个对象本身没有这个属性,

  2. 那么会去它的*proto*(即他的构造函数的prototype)中寻找。

  3. 如果没有,则会接着往上找,一直上溯到Object.prototype,知道null为止

也就是说所有对象都继承Object.prototype的属性,Object.prototype的原型是null,null没有任何属性和方法。

所以 只要把父类的属性和方法放在子类的构造函数的prototype 或者在之前的构造函数的prototype,就可以获取到父类的属性和方法

现有的继承方法

  1. 原型链继承
  2. 借用构造函数继承 伪造对象 或经典继承
  3. 组合继承(组合原型链继承和借用构造函数继承)(常用)
  4. 原型式继承
  5. 寄生式继承
  6. 寄生组合式继承(最理想)
  7. ES6继承 (class)(extends)

现在来实现对以下构造函数的继承

function Person(){
    this.name = 'asa' ; 
    this.age= '11'; 
    this.class = ['en','math'];
    this.sayName = function(){
        alert(this.name);
    }
}   

Person.prototype.like = 'fruit';
Person.prototype.sayHi = function(){
        alert('Hi');
    }

1. 原型链继承 -- 【原型链】

顾名思义 直接使用原型链继承。

首先创建一个对象,

1.基本方式创建对象

var a = {a:b};
var b = new Object();

2.构造函数创建对象

function Child (name) {
    this.name = name;
}

图2 为没有这一行 我们先不看继承的person

Child.prototype = new Person(); 

生成两个实例

var per1 = new Child("la");
var per2 = new Child("laa");

输出一下就发现已经获取到父类的属性和方法

console.log(per1.like); // fruit
console.log(per1.age); //11
console.log(per1.name); //la
per1.sayName();
per1.sayHi();
console.log(per2.like); // fruit
console.log(per2.age); //11
console.log(per1.name); //laa
per2.sayName();
per1.sayHi();

观察原型链

查看原型链 就相当与 a ,b 继承自Object

图1

构造函数生成的实例

看图可以知道 Child实例Per可以获取到 Child.prototype上的属性和方法

其实这就相当于与一个继承 Per 继承 Function Child的原型对象

为什么构造函数生成的对象 会继承构造函数的原型对象呢 这就是new干的事了

new会把新生成的对象的__proto__指向构造函数的原型对象

图2

但是我们要实现的继承 是对一个指定的类型继承,我们还要继承Person

查看上面的关键一行代码 就发先他就干了一件事~

Child.prototype = new Person();

如下图

橙色是记这个代码之前的原型链

由于 Child.prototype = new Person(); 所以 Child的原型就指向了Person实例的原型对象

好了这就完成了指定类的继承,这就是最基本的原型链继承

图3

这种办法最为简单但是也有很多问题,所以一般都不会单独使用哒


//问题
per1.calss.push('PE');
console.log(per1.class) // ['en','math','PE'];
console.log(per2.class) //['en','math','PE'];

问题:

  1. 引用类型的问题 【所有实例继承的引用类型都指向同一个地址 所以per1 操作的时候 Per2 的值也会改变】
  2. 继承时不能向父类中传递参数

2. 借用构造函数继承 伪造对象 或经典继承

为了解决原型链继承的问题就有了经典继承的方法

怎样解决引用类型的问题,那就是每个实例都有独立的属性,而不是去获取原型链上的属性,这样就不会相互影响了,在实例生成的时候就立刻给这个实例附上所有属性和方法

代码:

function Child2(name){
    Person.call(this,"jer",10)// 调用了 父构造函数 可以传参 提高自由度
    // Person2.call(this,"") 多个构造函数  多继承
    this.name = name;
}

这个实际上就不是把属性和方法放在原型上 ,而是直接添加到每个子实例中 添加方法就是运行一遍父构造函数,运行的时候还可以传参,就同时解决了第二个传参的问题

var per3 = new Child2('rr');
var per4 = new Child2('ss');

验证一下 就发现 Person 已经不在 实例的原型上了,因为我们只是把构造函数当做普通函数运行了一遍

console.log(per3 instanceoof Person)

验证一下 果然就不会产生两个实例的属性已经互不影响了

per3.calss.push('PE');
console.log(per3.class) // ['en','math','PE'];
console.log(per4.class) //['en','math'];

原型链 与上面没有继承父类的原型链长得一模一样 ,但是每个实例上都添加了方法和属性,这就产生了新的问题。这样就会发现

问题1
我们看着两个函数,根据上面的代码运行后 就生产了两个作用一致代码一致并且不会变动的函数,这就会造成冗余了
其实原型链那种方式 这两个函数也是有冗余的,但是那种方法可以把这个函数放在超类的原型上,所以实际没有这种问题

per3.sayName();
per4.sayName();

问题2
Person 不在原型链上 所以Person的原型对象上的方法也就不能被子类实例所使用啦

per3.sayHi(); // 会报错 没有该函数
per4.sayHi();

问题 :

  1. 冗余问题
  2. 继承时不能向父类中传递参数

由于以上问题,我们一般也不单独使用借用构造函数哒~

3.最常用的方法 -- 组合继承

那么,既然两种方法互补,那么合成大法来了---组合继承

想一下 把sayName这种公用的方法,或者公用的属性,放在原型链上,用原型链方式继承

class这种独有的属性,借用构造函数继承,就很完美了~

代码:

借用构造函数继承属性 此时 Person调用一次

function Child3(name){
    Person.call(this,name); 
}

组合原型继承方法 new的时候又调用一次

Child3.prototype = new Person(); 

var per5 = new Child3("xiaomi");
var per6 = new Child3("xiaoming");

输出一下就发现已经获取到父类的属性和方法

console.log(per1.like); // fruit
console.log(per1.age); //11
per1.sayName();
console.log(per2.like); // fruit
console.log(per2.age); //11
per2.sayName();

验证一下 这两个实例的属性也是互不影响了

per3.calss.push('PE');
console.log(per3.class) // ['en','math','PE'];
console.log(per4.class) //['en','math'];

而且可以获取 原型链上的方法 可以有公共的方法

per5.sayHi();
per6.sayHi();

组合继承很好的避免了原型链和借用构造函数的缺陷,融合了他们的优点

问题

不过组合继承也有一个小小的问题,那就是超类构造函数会运行两次

以下几个有时间再好好看,现在还不太理解,暂时放着 //todo

4.原型式继承

5.寄生式继承

6.寄生组合式继承(最理想)

7.ES6继承 (class)(extends)

es6 出了新的语法糖class之间通过使用extends关键字,这比通过修改原型链实现继承,要方便清晰许多

新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。实际上还是运用上面的组合继承的方法。通过原型链继承方法,借用了构造函数实现专有属性。

代码:

先来看class
class 我的理解是 class 更像是构造函数的原型对象,他可以找到构造函数,并可以存放属性和方法

class Point{
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
    toString(){
      console.log(this.x);   
    }
}

es5 实现 你就发现 class 实际就是一个构造函数 和 原型上的方法组成~ 不过就是更简单清晰了

function Point(x,y){
    this.x = x;
    this.y = y;
}

Point.prototype.toString(){
    console.log(this.x);   
}

再来看extends

class Colorpoint extends Point {
    //这个就是默认方法  使用new 生成实例时  会调用这个方法, 会把Point加在原型链上
    //如果未定义 会自动添加 
    constructor(x,y,color){
        
        //子类必须在constructor方法中调用super方法,否则新建实例时会报错
        //这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工,如果不调用super方法,子类就得不到this对象。
        super(x,y); //调用父类构造函数(Point.prototype.constructor.call(this,x,y))
        this.color = color
        
        //隐式返回 this
        // 如果显示返回对象 就是该对象
    }
    toString(){
        //通过 super调用父类的方法
        return this.color + ' ' + super.toString(); 
    }
}

差异

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this

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

推荐阅读更多精彩内容

  •   面向对象(Object-Oriented,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意...
    霜天晓阅读 2,089评论 0 6
  • 博客内容:什么是面向对象为什么要面向对象面向对象编程的特性和原则理解对象属性创建对象继承 什么是面向对象 面向对象...
    _Dot912阅读 1,394评论 3 12
  • JavaScript面向对象程序设计 本文会碰到的知识点:原型、原型链、函数对象、普通对象、继承 读完本文,可以学...
    moyi_gg阅读 757评论 0 2
  • 面向对象的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。ECMAScr...
    DHFE阅读 956评论 0 4
  • 也许不同时期背景,不同文化,不同人种,不同性别的爱情观是不同的,但爱的真谛是一样的。一段跨越半个多世纪的爱情史诗,...
    淑淑女子阅读 578评论 0 1