JavaScript原型与继承
JavaScript原型与继承
原型
在JavaScript中,每个函数都有一个prototype属性,这个属性是一个指针,指向该函数的原型对象。这个原型对象为所有该实例所共享。在默认情况下,原型对象包含一个属性叫做constructor,它指向prototype属性所在的函数指针。
图片和例子来自《JavaScript高级程序设计(第三版)》。
1
2
3
4
5
6
7
8
9
10
11
|
function Person () {} Person.prototype.name = 'Nicholas' ; Person.prototype.age = 29; Person.prototype.job = 'Software Enginner' ; Person.prototype.sayName = function () { alert( this .name); }; var person1 = new Person(), person2 = new Person(); |
但是改变原型时,可能会改变constructor,比如:
1
|
Dog.prototype = new Animal(); |
此时Dog.prototype.constructor指向构造函数Animal,如果有需要,可以重写constructor,比如
1
|
Dog.prototype.constructor = Dog; |
基于原型链的继承
通过让子类型的原型指向父类型的实例来实现基于原型链的继承。其本质是原型搜索机制:当访问一个实例的数据或方法时,首先在实例中寻找,实例中找不到后自然会沿着原型链寻找。这样继承之后,子类型的实例可以共享父类型的数据和方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
function SuperType (name) { this .name = name; this .age = 24; this .foo = [ 'bar1' , 'bar2' ]; } SuperType.prototype.say = function () { console.log( this .name, this .age, this .foo); }; function SubType () {} SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; var sub1 = new SubType( 'zdy1' ); var sub2 = new SubType( 'zdy2' ); sub1.age = 23; sub1.foo.push( 'bar3' ); sub1.say(); // undefined 23 ["bar1", "bar2", "bar3"] sub2.say(); // undefined 24 ["bar1", "bar2", "bar3"] |
在上面代码中,SubType通过原型继承了SuperTpe,但是同时也暴露了两个问题:
- 子类型无法为构造函数传入参数,所以SubType的name属性为undefined。
- 所有子类型共享父类型中的引用类型的数据或方法,也就是说,他们的引用类型属性都是同一个地址,修改引用类型的属性会影响所有子类型。
其中有一句需要说明一下:
1
|
SubType.prototype.constructor = SubType; |
在“SubType.prototype = new SuperType();”之后,“SubType.prototype.constructor”是指向SuperType的,需要把它更正回来。否则每一个SubType的实例的constructor都指向了SuperType,这显然是不科学的。
基于原型链的继承方式很少单独使用。
借用构造函数
在子类型构造函数中调用父类型的构造函数,叫做“借用构造函数”,也可以实现继承。它的思想是在子类型中重新调用一遍父类型的构造函数来初始化数据和方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function SuperType (name) { this .name = name; this .age = 24; this .foo = [ 'bar1' , 'bar2' ]; } SuperType.prototype.say = function () { alert( this .name); }; function SubType () { // 继承了SuperType SuperType.apply( this , arguments); } var sub = new SubType( 'zdy' ); console.log(sub) //SubType {name: "zdy", age: 24, foo: Array[2]} sub.say(); // Uncaught TypeError: Object #<SubType> has no method 'say' |
借用构造函数的模式可以在构造函数中传入参数,但子类型不能共享父类型在原型上的数据和方法。所以,它也很少单独使用。
组合继承
组合继承可谓整合了上面两种方法的特点:
- 子类型在构造函数中借用父类型的构造函数,在初始化时可以传递参数。 ——继承属性
- 子类型在原型上共享父类型数据和方法。 ——继承方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
function SuperType (name) { this .name = name; this .age = 24; this .foo = [ 'bar1' , 'bar2' ]; this .sex = 'other' ; } SuperType.prototype.say = function () { console.log( this .name, this .age, this .foo); }; function SubType (name, sex) { SuperType.apply( this , arguments); this .sex = sex; } SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; var sub1 = new SubType( 'zdy1' , 'male' ); var sub2 = new SubType( 'zdy2' , 'female' ); sub1.age = 23; sub1.foo.push( 'bar3' ); sub1.say(); // zdy1 23 ["bar1", "bar2", "bar3"] sub2.say(); // zdy2 24 ["bar1", "bar2"] console.log(sub1.sex, sub2.sex); // male female |
组合式继承也有不足之处,就是它实际调用了两次父类型的构造函数(第10行和第14行),并且在子类型的构造函数中重写(覆盖)了原型中的属性。所以改进的思路是,如何在不添加多余的、被覆盖的属性的同时,获得父类型的原型?请看最后一种继承方法。
寄生组合式继承
《JavaScript高级程序设计(第3版)》对这种继承方式给予了肯定:
开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
主要是它只调用了一次父类型的构造函数,所以避免了子类型在prototype上创建不必要的、多余的属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
function inheritPrototype (subType, superType) { function F () {} F.prototype = superType.prototype; var proto = new F(); proto.constructor = subType; subType.prototype = proto; } function SuperType (name) { this .name = name; this .colors = [ 'red' , 'blue' , 'green' ]; } SuperType.prototype.sayName = function () { alert( this .name); }; function SubType (name, age) { SuperType.call( this , name); this .age = age; } inheritPrototype(SubType, SuperType); var instance = new SubType( 'Nicholas' , 29); // SubType {name: "Nicholas", colors: Array[3], // age: 29, constructor: function, sayName: function} console.log(instance); |
1
|
|
以上代码改自《JavaScript高级程序设计(第3版)》,说说我个人对这种方法思路的理解。
在inheritPrototype函数中,创建一个临时构造函数F()并实例化,来获得父类型原型对象的一个副本。因为它只复制了父类型的原型对象,从而并没有包括父类型构造函数中的属性与方法。相当于在继承过程中,绕了一个弯来躲避再一次初始化父类型的构造函数,所以不会在子类型的原型中存在多余的父类型属性。
JavaScript原型与继承的更多相关文章
- 深入理解:JavaScript原型与继承
深入理解:JavaScript原型与继承 看过不少书籍,不少文章,对于原型与继承的说明基本上让人不明觉厉,特别是对于习惯了面向对象编程的人来说更难理解,这里我就给大家说说我的理解. 首先JavaScr ...
- JavaScript原型与继承的秘密
在GitHub上看到的关于JavaScript原型与继承的讲解,感觉很有用,为方便以后阅读,copy到自己的随笔中. 原文地址:https://github.com/dreamapplehappy/b ...
- JavaScript 原型与继承
JavaScript 原型与继承 JavaScript 中函数原型是实现继承的基础.prototype.construct.原型链以及基于原型链的继承是面向对象的重要内容 prototype 原型即 ...
- javascript原型链继承
一.关于javascript原型的基本概念: prototype属性:每个函数都一个prototype属性,这个属性指向函数的原型对象.原型对象主要用于共享实例中所包含的的属性和方法. constru ...
- JavaScript 原型与继承机制详解
引言 初识 JavaScript 对象的时候,我以为 JS 是没有继承这种说法的,虽说 JS 是一门面向对象语言,可是面向对象的一些特性在 JS 中并不存在(比如多态,不过严格来说也没有继承).这就困 ...
- 8条规则图解JavaScript原型链继承原理
原形链是JS难点之一,而且很多书都喜欢用一大堆的文字解释给你听什么什么是原型链,就算有图配上讲解,有的图也是点到为止,很难让人不产生疑惑. 我们先来看一段程序,友情提示sublimeText看更爽: ...
- 【Javascript】Javascript原型与继承
一切都是对象! 以下的四种(undefined, number, string, boolean)属于简单的值类型,不是对象.剩下的几种情况——函数.数组.对象.null.new Number(10) ...
- 【前端知识体系-JS相关】深入理解JavaScript原型(继承)和原型链
1. Javascript继承 1.1 原型链继承 function Parent() { this.name = 'zhangsan'; this.children = ['A', 'B', 'C' ...
- JavaScript原型及继承
一.浅谈原型 首先我们要知道创建对象的方法有两种: 1.通过字面量的方式直接创建 var obj = { name:'baimao', age:21 } 2.通过构造函数创建对象 function P ...
随机推荐
- Eclipse部署Web项目(图文讲解)
讲解是在linux下完成的,但对windows系统,操作也是一样的,不要被吓到了 1.下载Eclipse
- CSharp设计模式读书笔记(24):访问者模式(学习难度:★★★★☆,使用频率:★☆☆☆☆)
模式角色与结构: 示例代码: using System; using System.Collections.Generic; using System.Linq; using System.Text; ...
- cocos2d-x多分辨率和随后的自适应CCListView的bug修复
cocos2d-x多分辨率自适配及因此导致的CCListView的bug修复 cocos2d-x是一款众所周知的跨平台的游戏开发引擎.因为其跨平台的特性.多分辨率支持也自然就有其需求. 因此.在某一次 ...
- [CLR via C#]1.5 本地代码生成器:NGen.exe
原文:[CLR via C#]1.5 本地代码生成器:NGen.exe 1. NGen.exe工具,可以在一个程序安装到用户计算机时,将IL代码编译成为本地代码.由于代码在安装时已经编译好,所以CLR ...
- LSM树存储模型
----<大规模分布式存储系统:原理解析与架构实战>读书笔记 之前研究了Bitcask存储模型,今天来看看LSM存储模型,两者尽管同属于基于键值的日志型存储模型.可是Bitcask使用哈希 ...
- 将程序添加到右键菜单和图标(以记事本、UltraEdit为例)
原文:将程序添加到右键菜单(以记事本.UltraEdit为例) 如何将程序加入右键菜单,这里分别以记事本.UltraEdit为例! 以记事本程序为例: 1. 在运行中输入regedit,打开注册表,找 ...
- Windows Phone 8.1 多媒体(1):相片
原文:Windows Phone 8.1 多媒体(1):相片 Windows Phone 8.1 多媒体(1):相片 Windows Phone 8.1 多媒体(2):视频 Windows Phone ...
- NYOJ 14 场地安排(它可以被视为一个经典问题)
会场安排问题 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描写叙述 学校的小礼堂每天都会有很多活动.有时间这些活动的计划时间会发生冲突,须要选择出一些活动进行举办.小刘的工 ...
- Url.Content
Url.Content了,Url是ViewPage的一个对象,它最常用的一个方法就是Content,它的功能是返回某个文件的路径.一般情况下,在使用了ASP.NET MVC后,目录结构变得有点诡异,像 ...
- Linux下使用cat制作“内涵图”
http://blog.csdn.net/odaynot/article/details/7939869