原型包括三个独立但相关的访问器。这三个单词都是对单词prototype做了一些变化。

  • C.prototype用于建立由new C()创建的对象的原型

  • Object.getPrototypeOf(obj)是ES5中用来获取obj对象的原型对象的标准方法

  • obj.__proto__是获取obj对象的原型对象的非标准方法

一个例子

要理解这些访问器,我们拿一个典型的js数据类型作例子。
假设User构造函数需要通过new操作符来调用。它需要两个参数,即姓名和密码的哈希值,并将它们存储在创建的对象中。
代码如下:

function User(name,pwd){
this.name=name;
this.pwd=pwd;
}
User.prototype.toString=function(){
return '[User '+this.name+']';
}
User.prototype.checkPwd=function(pwd){
return hash(pwd)===this.pwd;
}
var u=new User('cedrusweng','$sdf99kaslf7');

根据上面的这个代码我们可以画出下面这个图,以表示各种关系。
我们每次在使用

function Fn(){

}

相当于调用下面这样

var fn=new Function([参数,]"函数体");

其中Function是一个构造函数,它的原型对象中包含我们之前讲到过的call、apply、bind等属性和方法。fn是Function的一个实例对象。
所以这里的可以得到第一部分的图

下面再就构造函数User和它的原型对象之间的关系画出一个图

User函数带有一个默认的prototype的属性,其包含一个开始几乎为空的对象。上面的例子中添加了两个方法到原型对象中。当使用new操作符创建User的实例时,产生的对象u得到了自动分配的原型对象,该原型对象被存储在User.prototype中。

最后来一张完整的图

注意:new操作符调用构造函数,会产生一个新的对象实例,这个对象是以构造函数为模板,创建一份私有的性属性和方法,所有的实例都会继承原型对象。当访问u.name时,u对象会首先在它的私有属性中进行搜索,如果有则会返回,如果没有,则会查找对象的原型对象。当访问u.checkPwd时,私有属性和方法不存在时,会返回存储在User.prototype中的方法。

原型有关的方法

首先,构造函数的prototype属性用来设置新实例的原型关系。
其次,ES5中的函数Object.getPrototypeOf()可以用于检索现有对象的原型。
如上面的例子,可以使用下面代码来检测对象u的原型对象。

Object.getPrototypeOf(u)===User.prototype;//true

最后,一些环境提供了非标准的方法检索对象的原型,即特殊的__proto__属性。这可作为在不支持ES5的Object.getPrototypeOf方法的环境中的一个兼容方法。在这些环境中可以使用下面代码完成检测

u.__proto__===User.prototype;//true

下面画一张图,表明它们之间的关系

最后的说明

js程序员往往将User描述为一个类,尽管它跟一个函数差不多。js中的类本质上是一个构造函数与一个用于类(User.prototype)实例间共享方法的原型对象的结合。
下面是一个User类的概念图。

User函数给该类提供了一个公共的构造函数,而User.prototype是实例之间共享方法的一个内部实现。User和u的变通用法都不需要直接访问原型对象。

提示

  • C.prototype属性是new C()创建的对象的原型

  • Object.getPrototypeOf(obj)是ES5中检索对象原型的标准函数

  • obj.__proto__是检索对象原型的非标准方法

  • 类是由一个构造函数和一个关联的原型组成的一种设计模式

附录一:函数声明

函数使用
var fn1=function(){};
var fn2=new Function('');
function fn3(){

}
这3种方式的函数,前面两种都是属性函数表达式,最后一种是函数声明语法。函数实际上是对象,每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。
具体可以看下图来看对应的关系图

函数表达式与函数声明的区别

看到上面都是产生了函数的实例,但解析器会率先读取函数声明,并使其在执行任何代码之前可用;函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释。
可以根据函数表达式的特点,按条件给变量赋不同的函数表达式。

function createFn(a)
var fn;
if(a){
fn=function(){console.log(1)}
}else{
fn=function(){console.log(2)}
}
return fn;
}
var fn1=createFn(true);
var fn2=createFn();
fn1();//1
fn2();//2

[Effective JavaScript 笔记]第30条:理解prototype、getPrototypeOf和__ptoto__之间的不同的更多相关文章

  1. [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  2. [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

  3. [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  4. [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  5. [Effective JavaScript 笔记]第36条:只将实例状态存储在实例对象中

    理解原型对象与其实例之间是一对多的关系,对于实现正确的对象行为很重要.常见的错误是不小心将每个实例的数据存储到了其原型中. 示例 一个实现了树型数据结构的类可能将子节点存储在数组中. 实例状态在原型中 ...

  6. [Effective JavaScript 笔记] 第2条:理解JavaScript的浮点数

    JavaScript数值型类型只有数字 js只有一种数值型数据类型,不管是整数还是浮点数,js都把归为数字. typeof 17;   // “number” typeof 98.6; // “num ...

  7. [Effective JavaScript 笔记]第47条:绝不要在Object.prototype中增加可枚举的属性

    之前的几条都不断地重复着for...in循环,它便利好用,但又容易被原型污染.for...in循环最常见的用法是枚举字典中的元素.这里就是从侧面提出不要在共享的Object.prototype中增加可 ...

  8. [Effective JavaScript 笔记] 第12条:理解变量声明提升

    js支持词法作用域,即除了极少的例外,对变量的引用会被绑定到声明变量最近的作用域中. js不支持块级作用域,即变量定义的作用域并不是离其最近的封闭语句或代码块,而是包含它们的函数. 不了解这个会产生一 ...

  9. [Effective JavaScript 笔记]第18条:理解函数调用、方法调用及构造函数调用之间的不同

    面向对象编程中,函数.方法.类的构造函数是三种不同的概念. JS中,它们只是单个构造对象的三种不同的使用模式. 三种不同的使用模式 函数调用 function hello(username){ ret ...

随机推荐

  1. 启动tomcat报错 Could not reserve enough space for object heap的解决办法

    问题:打开eclips启动tomcat发现报出Could not reserve enough space for object heap错误. 解决办法:1.首先检查tomcat是否能正常启动.re ...

  2. angular的工具方法笔记(equals, HashKey)

    分别是angular脏值检测的工具方法equals和 类HashKey的使用方法 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transi ...

  3. Eclipse字体大小设置

    打开Eclipse,在主界面下Window->Preferences->General->Appearance->Colors and Fonts, 然后在右边展开框Basic ...

  4. web.xml的深入学习

    1.过滤器?  监听器?   listener   servlet    filter? 2.命名空间?   schema?  DTD?     xml文件的学习要好好的看下哦!

  5. openvpn的介绍和搭建过程

    本文摘自:http://www.linuxidc.com/Linux/2012-01/51702.htm,在这只是为了做个笔记使用

  6. C基础--初学指针

    一.指针简介(任何指针变量在内存中占八个字节的存储空间) 1.定义指针变量(格式):变量类型 *变量名:--定义一个指向整形变量的指针  int *p; 2.作用:指针变量就是用来存储其他变量的地址: ...

  7. SQL增加、删除、更改表中的字段名

    1. 向表中添加新的字段 ) not null 2. 删除表中的一个字段 delete table table_name column column_name 3. 修改表中的一个字段名 alter ...

  8. Activity启动模式 及 Intent Flags 与 栈 的关联分析

     http://blog.csdn.net/vipzjyno1/article/details/25463457    Android启动模式Flags栈Task   目录(?)[+] 什么是栈 栈 ...

  9. Weka算法Classifier-tree-J48源代码分析(一个)基本数据结构和算法

    大约一年,我没有照顾的博客,再次拿起笔不知从何写上,想来想去手从最近使用Weka要正确书写. Weka为一个Java基础上的机器学习工具.上手简单,并提供图形化界面.提供如分类.聚类.频繁项挖掘等工具 ...

  10. 在Main中定义student的结构体,进行年龄从大到小依次排序录入学生信息。(结构体的用法以及冒泡排序)

    using System; using System.Collections; using System.Collections.Generic; using System.Linq; using S ...