有同事问了我几个和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原理的更多相关文章

  1. [转]Web程序员必须知道的 Console 对象里的九个方法

    一.显示信息的命令 01 1: <!DOCTYPE html> 02  2: <html> 03  3: <head> 04  4:     <title&g ...

  2. Android 程序员必须知道的 53 个知识点

    1. android 单实例运行方法 我们都知道 Android 平台没有任务管理器,而内部 App 维护者一个 Activity history stack 来实现窗口显示和销毁,对于常规从快捷方式 ...

  3. PHP程序员应该知道的15个库

    最几年,PHP已经成为最受欢迎的一种有效服务器端编程语言.据2013年发布的一份调查报告显示,PHP语言已经被安装在全球超过2.4亿个网站以及210万台Web服务器之上.PHP代表超文本预处理器,它主 ...

  4. 嵌入式程序员应知道的0x10个基本问题

     来源:网络 嵌入式程序员应知道的0x10个基本问题 1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)#define SECONDS_PER_YEAR (60 ...

  5. 嵌入式程序员应知道的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 ...

  6. (转)程序员应该知道的10个eclipse调试技巧

    调试不仅可以查找到应用程序缺陷所在,还可以解决缺陷.对于Java程序员来说,他们不仅要学会如何在Eclipse里面开发像样的程序,更需要学会如何调试程序.本文介绍了Java程序员必知的10个调试技巧, ...

  7. Java程序员应该知道的20个有用的lib开源库

    一般一个经验丰富的开发者,一般都喜欢使用开源的第三方api库来进行开发,毕竟这样能够提高开发效率,并且能够简单快速的集成到项目中去,而不用花更多的时间去在重复造一些无用的轮子,多了解一些第三方库可以提 ...

  8. Git / 程序员需要知道的12个Git高级命令

    众所周知,Git目前已经是分布式版本控制领域的翘楚,围绕着Git形成了完整的生态圈.学习Git,首先当然是学习Git的基本工作流.相比于SVN等传统版本控制系统来说,Git是专为分布式版本控制而生的强 ...

  9. 成为嵌入式程序员应知道的0x10个基本问题

    预处理器(Preprocessor)1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define SECONDS_PER_YEAR (60 * 60 * 2 ...

随机推荐

  1. Mybatis表关联多对一

    在上章的 一对多 中,我们已经学习如何在 Mybatis 中关联多表,但在实际项目中也是经常使用 多对一 的情况,这些查询是如何处理的呢,在这一节中我们来学习它.多表映射的多对一关系要用到 mybit ...

  2. android 解决输入法键盘遮盖布局问题

    /**     * @param root 最外层布局,需要调整的布局     * @param scrollToView 被键盘遮挡的scrollToView,滚动root,使scrollToVie ...

  3. MySQL库和表的管理

    MySQL数据库服务配置好后,系统会有4个默认的数据库. information_schema:虚拟对象,其对象都保存在内存中performance_schema:服务器性能指标库mysql:记录用户 ...

  4. weblogic学习笔记:域创建+应用部署

    参考地址:http://docs.oracle.com/cd/E13222_01/wls/docs92/index.html 文档基于版本:weblogic92 域(domain)是weblogic服 ...

  5. PHP 开发者该知道的 5 个 Composer 小技巧

    From: https://segmentfault.com/a/1190000000355928 Composer是新一代的PHP依赖管理工具.其介绍和基本用法可以看这篇<Composer P ...

  6. CCSprite: fade 效果切换图片

    //CCSprite+Animation.h #import "CCSprite.h" @interface CCSprite (Animation) + (void)fadeWi ...

  7. Android Material Design控件学习(二)——NavigationView的学习和使用

    前言 上次我们学习了TabLayout的用法,今天我们继续学习MaterialDesign(简称MD)控件--NavigationView. 正如其名,NavigationView,导航View.一般 ...

  8. Node.js之操作Mongodb

    // mongoose 链接 var mongoose = require('mongoose'); var db = mongoose.createConnection('mongodb://127 ...

  9. ABBYY FineReader利用模式提高OCR质量

    提高OCR质量,除了可以使用ABBYY FineReader 12OCR文字识别软件解决纸质文档的复杂结构未出现在电子文档中,或者未正确检测到区域的问题(详见如何提高ABBYY FineReader ...

  10. java okhttp发送post请求

    java的httpclient和okhttp请求网络,构造一个基本的post get请求,都比py的requests步骤多很多,也比py的自带包urllib麻烦些. 先封装成get post工具类,工 ...