YGben

漫谈JS继承


继承父类的方法,对象就有了这个方法吗?

  • 对继承的理解
  • 原型
  • 原型链

对继承的理解

一说到继承,我就想起上学老师的一句话:继承,子类继承父类的属性和方法。这句话没错,但是很浅,怎么实现继承的?为什么子类就有了父类的属性和方法?
面向对象的编程中,才有了类和对象,有了继承的概念。继承,这一很好理解的概念,但也容易误导人的理解。社会语言体系中继承即拥有,继承财产,即拥有财产。编程语言中继承并不拥有,继承父类的方法,对象就有了这个方法吗?以前没想过,现在想不是的,只不过子类可以用父类的方法。

比如:
OC中 所有类的实例都由类对象生成,类对象会把实例的isa的值修改成自己的地址,每个实例的isa都指向该实例的类对象。

那既然JS中也有继承的概念,JS中如何实现这一机制的呢?阮一峰大神的一篇Javascript继承机制的设计思想 通俗达理。

继承主要还是对象间关系建立。那ECMAScript中对象是什么?如何创建对象?

  • ECMAScript 没有类的概念,对于对象的理解也与其他语言不同。对象:无序属性的集合,其属性可以包含基本值,对象或者函数。这里说继承的对象就是某个特定引用类型的实例,大部分还是Object 的实例。
  • JS对象可以用构造函数new出来,构造函数也是普通函数。

prototype

JS高级程序编程中第六章看一遍有一遍的收获。
prototype是什么? __proto__是什么?

第六章中有相关的介绍,如下:

我们创建的每个函数有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。

可以理解为prototype属性指向函数的原型对象。即原型属性指向原型对象。

当调用构造函数创建一个新实例后,该实例的内部讲包含一个指针(内部属性),指向构造函数的原型对象。

而这个所谓的内部属性就是__proto__,称为隐式原型,这个属性在Firfox,Safari,Chrome中可见,IE不可见。

最后还是一张图说明一切:
此处输入图片的描述

  • proto 是所有对象内部的隐式属性,指向它的构造方法的原型对象。函数的构造方法自然是Function,所以foo的proto指向了Function prototype。

  • prototype属于构造函数,指向构造函数的原型对象

  • 上图很好的说明了什么是原型链,一层一层,直到OBject的原型属性指向null

  • 狠狠的抓住原型对象这一关键点,连接建立在实例和构造函数的原型对象之间,而不是存在实例和构造函数之间。上图中 f1,f2 显然并没有和foo()构建联系。


实践

1
2
3
4
5
6
7
8
9
10
11
function message(person,time){
this.person = person;
this.time = time;
}
message.prototype.tell = function(){
alert(this.person+this.time)
}
var instance= new message("特朗普","2017/11/08");
instance.tell();

这样写就很清楚,prototype属性 属于 构造函数message。而实例之所以能访问到tell()方法,因为instance的内部属性porto指向了构造函数的prototype。

上个小代码就是原型链和构造方法结合,原型链中实现对原型属性和方法继承,是被所有实例有共享的。构造方法实现对实例属性的继承,是某一实例的

—- 尬尬的分割线0 —

在项目中遇到浮点数的加减乘除处理,有时候结果不是想要的。就需要给Number添加方法处理,就在prototype上添加。

1
2
3
Number.prototype.mul = function (arg) {
return accMul(arg, this);
}

—- 尬尬的分割线1 —

理解了JS继承中基石——prototype,最后实现继承的就需要两句代码来建立对象间这种叫“继承”的关系。

1
2
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;

开始为了理解第二句话到底有什么作用,写了如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function message(person,time){
this.person = person;
this.time = time;
}
message.prototype.tell = function(){
alert(this.person+this.time)
}
function politics(person,time,place){
message.call(this,person,time);
this.place = place;
}
politics.prototype = new message();
politics.prototype.say = function(){
alert(this.person+this.time+this.place)
}
politics.prototype.constructor = politics;
var instance= new politics("特朗普访华","2017/11/8","北京");
instance.say();
  • 第13行 一定写在12行的下面,否则politics的原型被message取代,自然没有say方法,会报错 instance.say is not a function
  • 第9行 调用call()方法,执行message方法,通过调用构造方法来实现继承。其实我感觉相当于OC语言的super调用。
  • 第16行 这句话我研究一阵到底什么用,效果没什么用。politics是通过new方法来创建的,按理constructor就应该指向politics,但是因为第12行的代码破坏了心理惯性,导致instance.constructor指向了message。所以也是为了一致性和防止以后用到对象的construction属性出错蒙蔽,就写上这样一行代码。

参考传送门

感谢
知乎问答js中proto和prototype的区别和关系?
IBM社区资料 开始使用面向对象的 JavaScript 代码
阮一峰的Javascript继承机制的设计思想