JavaScript原型和继承的理解

一、_proto_和prototype的关系

只有function对象才有prototype
生成function对象的时候,会生成prototype属性,并将function对象赋值给prototype.constructor。

后面的例子都以Base作为父类

//父类
function Base(){
    this.x = 0;
    this.y = 0;
}

Base.prototype.constructor === Base//true

使用 new function对象,可以生成一个新的对象。new做法是新建一个obj对象o1,并且让o1的__proto__指向了Base.prototype对象。并且使用call 进行强转作用环境。从而实现了实例的创建。

new的时实际上执行的是:

var o1 = new Object();
o1.[[Prototype]] = Base.prototype;//__proto__指向prototype
Base.call(o1);

所以

new Base().__proto__ === Base.prototype

构造函数生成了一个原型,new可以基于这个生成的原型和构造函数生成实例
在这里插入图片描述

总结起来的话,prototype是一个在构造函数中,用来共享数据结构的模板。
__proto__是prototype在对象中的具体表现

二、继承的实现

2.1 构造函数到底声明了什么

一个构造函数其实定义了两部分数据结构。以Base为例:

  • 自身的x,y
  • 用来共享的prototype。

2.2 如何来实现继承

继承其实就是想办法继承这两部分数据

首先还是以Base为例定义被继承的父类

//父类
function Base(){
    this.x = 0;
    this.y = 0;
}

Base.prototype.getAdd = function(){
    return this.x+this.y
}

2.2.1 原型链继承

将父类的实例直接复制到子类的prototype里面,依靠原型链来访问父类,这样自身属性和原型链都放到了原型链上

//父类
function Base(){
    this.x = 0;
    this.y = 0;
}

Base.prototype.className = "Base"


function PrototypeChildren(){
	
}
PrototypeChildren.prototype = new Base()
var pc1 = new PrototypeChildren()
var pc2= new PrototypeChildren()

需要注意的是通过子类可以读取原型链上的属性,但是无法对原型链上的属性赋值

pc1.className = "pc1"
console.log(pc2.className)//Base
console.log(pc1.className)//pc1

直接赋值会在子对象上动态生成一个新的属性,不会修改父类的prototype。

在这里插入图片描述

如果要修改原型链上的属性需要修过父类的prototype

PrototypeChildren.prototype.className = "Children"
console.log(pc2.className)//Children
console.log(pc1.className)//pc1

2.2.2 构造函数继承

使用call方法,把构造函数中的属性复制到新对象里面,继承了构造函数里面的数据结构,只继承了构造函数内声明的属性,没有继承原型中的属性
function ConChildren(){
Base.call(this)
}

var cc = new ConChildren()

这样只继承了属性,但是原型链并没有继承

在这里插入图片描述
我们要把原型链修改成为可以用下面的组合法

在这里插入图片描述

2.2.3 组合式继承

通过构造函数和原型链相结合的方式实现继承

function SubTypeChildren(){
	Base.call(this)
}

// 空函数F:
function F() {
}

// 把F的原型指向Base.prototype:
F.prototype = Base.prototype;

SubTypeChildren.prototype = new F()

var sc = new SubTypeChildren()

这里面没有直接使用,对Base.prototype进行了保护,避免修改SubTypeChildren.prototype时直接修改了Base.prototype放到了,同时将Base.prototype放到了SubTypeChildren的原型链上

SubTypeChildren.prototype = Base.prototype

但是这样SubTypeChildren.prototype.constructor指向了Base

在这里插入图片描述

可以手动改回来

SubTypeChildren.prototype.constructor = SubTypeChildren

空函数F写法很繁琐,看起来很奇怪,ES5之后可以用Object.create代替。Object.create规范了组合继承的写法

function SubTypeChildren(){
	Base.call(this)
}
SubTypeChildren.prototype = Object.create(Base.prototype);
SubTypeChildren.prototype.constructor = SubTypeChildren

Object.create(Base.prototype)和 new Base()的区别。Object.create(Base.prototype)生成一个对象只包含了Base的原型而没有生成构造函数中的属性,避免了多次调用构造函数生成重复属性

[外链图片转存失败(img-07H40chl-1568877115313)(8739E479D04847E8A8C6BBFB4FFDC834)]

2.3 多继承

可以使用Object.assign混入结合Object.create实现多继承

function MyClass() {
     SuperClass.call(this);
     OtherSuperClass.call(this);
}

// 继承一个类
MyClass.prototype = Object.create(SuperClass.prototype);
// 混合其它
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// 重新指定constructor
MyClass.prototype.constructor = MyClass;

MyClass.prototype.myMethod = function() {
     // do a thing
};

2.4 class和extends继承

2.4.1 class

新的关键字class从ES6开始正式被引入到JavaScript中。class是生成类的语法糖,目的就是让定义类更简单。
旧的写法

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

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
}

新的class写法

class Student {
    constructor(name) {
        this.name = name;
    }

    hello() {
        alert('Hello, ' + this.name + '!');
    }
}

2.4.2 extends继承

现在,原型继承的中间对象,原型对象的构造函数等等都不需要考虑了,直接通过extends来实现:

class PrimaryStudent extends Student {
    constructor(name, grade) {
        super(name); // 记得用super调用父类的构造方法!
        this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade);
    }
}

对比组合继承和extends继承

[外链图片转存失败(img-wbBMZ7uS-1568877115315)(AABEABF2CCFF4EB6B7A9535DA046E521)]

优势:

  • extends实现的继承语义更清晰
  • 生成的对象的原型链接更简洁易于理解
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值