Web程序员应该知道的Javascript prototype原理
有同事问了我几个和Javascript的类继承的小问题,我在也不太理解的情况下,胡诌了一通. 回来以后有些内疚, 反省一下, 整理整理Javascript的prototype的原理, 自己清楚点, 也希望对别人也有帮助.
首先js里面没有类这回子事情, 虽然class是js的保留字, 但是到现在也没派上任何用场. 在js里面, 几乎所有的东西都是对象, 函数也是对象, 所以函数可以赋值给别的变量. 另外js对象也可以用数组的方式访问, 也就是说访问obj1.something, 也可以写成obj1['something'].
在js里虽然没有类, 但是有一个在类编程里常用的的概念, Constructor构造器, 或者叫构造函数. 语法上Constructor跟普通的js函数没有区别, 但是Constructor前面加上new关键字就可以产生新的对象.
那么”new Constructor()”这样调用时到底做了什么事情呢?
1. js运行环境首先创建一个空对象
2. 把this变量指向这个对象
3. 把__proto__指向这个构造器的prototype属性
4. 通过this把属性和方法加在这个对象上
5. 最后会把this指向的对象return出来(当然你也可以显示的return别的对象,这是情况跟我讨论的有点不一样)
这里有2个概念比较重要, 第一是Constructor的prototype属性, 它是Constructor的一个保持共享属性和方法的对象. 二通过该Constructor生成的对象的__proto__属性, 它指向了那个prototype对象. 对象的Constructor可以通过该对象的constructor属性获得.
我们定义一个简单的Constructor Demo(), 然后通过他生成一个aDemo对象, 在Chrome的console里把aDemo打出来看到的结果是这样的,这是我们能很清楚的看出这种关系.
|
01
02
03
04
05
06
|
function DemoA(){ this.title = "this is a demo constructor"; this.f1 = function(){}}var aDemo = new DemoA();console.log(aDemo); |

换一种写法,
|
01
02
03
04
05
06
|
function DemoB(){ this.title = "this is a demo constructor";}DemoB.prototype.f2 = function () {};var bDemo = new DemoB();console.log(bDemo); |

其实跟前面没多大区别, 不过这一次f2()不在创建出来的对象上, 而是在对象的__proto__指向的构造器DemoB上. 这样的好处是当使用DemoB创建很多对象时, 比较节省内存.
js的prototype搜索链 prototype Chain
js的运行环境是这样使用构造器的prototype的. 当我们对第一个对象调用其方法时, js运行环境首先看, 这个对象本身有没有这个方法, 如果有就直接调用. 如果没找到, 就查找这个对象的__proto__属性指向的对象(就是前面说的该对象的构造器的prototype属性), 如果有该方法就调用, 没有继续查找__proto__属性指向对象的__proto__属性指向的对象, 以此类推. 直到最后, 找到js所有的对象都会指向默认指向的Object(算是js的根对象吧). 所以如果我们给Object上增加个方法, 所以的js对象都能使用.
由于js有对对象函数的prototype chain搜索的特性, 我们可以利用这点来模拟类似其他语言里类(class)的效果. 如:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
function DemoA(){ this.title = "this is a demo constructor"; this.f1 = function(){}}function DemoB(){ this.title = "this is b demo constructor"; this.f2 = function(){}}DemoB.prototype = new DemoA();var bDemo = new DemoB();console.log(bDemo); |

这时我们通过bDemo对象, 不但能调用bDemo上的DemoB的f2()方法, 也能__proto__同对对prototype的搜索, 在bDemo上直接调用DemoA的f1()方法.
由于这种写法在语法上太啰嗦, 不容易理解, 我可以定义一个这样的函数.
|
01
02
03
|
function inherit(Child, Parent) { Child.prototype = new Parent();} |
这样我们前面的例子可以写成这样, 效果是一样的:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
function DemoA(){ this.title = "this is a demo constructor"; this.f1 = function(){}}function DemoB(){ this.title = "this is b demo constructor"; this.f2 = function(){}}inherit(DemoB, DemoA);var bDemo = new DemoB();console.log(bDemo); |
Web程序员应该知道的Javascript prototype原理的更多相关文章
- [转]Web程序员必须知道的 Console 对象里的九个方法
一.显示信息的命令 01 1: <!DOCTYPE html> 02 2: <html> 03 3: <head> 04 4: <title&g ...
- Android 程序员必须知道的 53 个知识点
1. android 单实例运行方法 我们都知道 Android 平台没有任务管理器,而内部 App 维护者一个 Activity history stack 来实现窗口显示和销毁,对于常规从快捷方式 ...
- PHP程序员应该知道的15个库
最几年,PHP已经成为最受欢迎的一种有效服务器端编程语言.据2013年发布的一份调查报告显示,PHP语言已经被安装在全球超过2.4亿个网站以及210万台Web服务器之上.PHP代表超文本预处理器,它主 ...
- 嵌入式程序员应知道的0x10个基本问题
来源:网络 嵌入式程序员应知道的0x10个基本问题 1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)#define SECONDS_PER_YEAR (60 ...
- 嵌入式程序员应知道的0x10个C语言Tips
[1].[代码] [C/C++]代码 跳至 [1] ? 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 27 ...
- (转)程序员应该知道的10个eclipse调试技巧
调试不仅可以查找到应用程序缺陷所在,还可以解决缺陷.对于Java程序员来说,他们不仅要学会如何在Eclipse里面开发像样的程序,更需要学会如何调试程序.本文介绍了Java程序员必知的10个调试技巧, ...
- Java程序员应该知道的20个有用的lib开源库
一般一个经验丰富的开发者,一般都喜欢使用开源的第三方api库来进行开发,毕竟这样能够提高开发效率,并且能够简单快速的集成到项目中去,而不用花更多的时间去在重复造一些无用的轮子,多了解一些第三方库可以提 ...
- Git / 程序员需要知道的12个Git高级命令
众所周知,Git目前已经是分布式版本控制领域的翘楚,围绕着Git形成了完整的生态圈.学习Git,首先当然是学习Git的基本工作流.相比于SVN等传统版本控制系统来说,Git是专为分布式版本控制而生的强 ...
- 成为嵌入式程序员应知道的0x10个基本问题
预处理器(Preprocessor)1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define SECONDS_PER_YEAR (60 * 60 * 2 ...
随机推荐
- e807. 设置进程监听对话框的延迟弹出
By default, the progress monitor delays for a short period before it is displayed. There are two pro ...
- JAVA组成原理及使用方法编辑环境及实现过程
JAVA组成原理一.由四方面组成:1.Java编程语言2.Java类文件格式3.Java虚拟机4.Java应用程序接口 当编辑并运行一个Java程序时,需要同时涉及到这四种方面.二.使用文字编辑软件: ...
- 详解SQLServer如何链接远程MySQL数据库
最近遇到“SQL如何链接远程MySQL”这个问题,现在问题终于解决,特把方法贴出来:(我所用的操作系统是Win7,数据库是SQL2005.) 1.在SQL SERVER服务器上安装MYSQL ODBC ...
- Linux 标准文件描述符
出于特殊目的,bash shell保留了最早的3个文件描述符0.1.2,如下所示: 1.STDIN标准输入 Shell从STDIN文件描述对应的键盘获得输入,在用户输入时处理每个字符. 范例1:cat ...
- iOS: 查看 UIView 的视图树
在想要查看的 UIView 附近打个断点,运行,直到停在断点处,在控制台键入:po [view recursiveDescription],回车. (lldb) po [self recursiveD ...
- HttpURLConnection和HttpClient的区别2(转)
1.HttpClient比HttpURLConnection功能更强大,但是做java建议用前者,安卓建议用后者 2.这两者都支持HTTPS,streaming 上传与下载,配置超时时间,IPv6, ...
- 获取 SharpSvn 执行 svn 操作的实时日志
1 获取 SharpSvn 操作日志的方式 之前一篇随笔(使用 SharpSvn 执行 svn 操作)讲到可以通过声称一个绑定到一个 SvnClient 对象的 SvnClientReport 对象. ...
- mongoose实现批量删除和多id查询的api/方法
删除一条数据:传入id Model.remove({ _id: 传入的id }); 删除多条数据,传入id数组,使用$in方法 Model.remove({ _id: { $in: ['aID', ' ...
- 彻底搞清楚Java并发 (一) 基础
多线程编程是为了让程序运行得更快,但是不是说,线程创建地越多越好,线程切换的时候上下文切换,以及受限于硬件和软件资源的限制问题 上下文切换 单核CPU同样支持多线程编程,CPU通过给每个线程分配CPU ...
- 16 go操作Mysql
mysql模块下载 mysql模块我们从github上下载,地址为:www.github.com/go-sql-driver/mysql go get "github.com/go-sql- ...