[Effective JavaScript 笔记]第30条:理解prototype、getPrototypeOf和__ptoto__之间的不同
原型包括三个独立但相关的访问器。这三个单词都是对单词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
翻译的文章,版权归原作者所有,只用于交流与学习的目的。
原创文章,版权归作者所有,非商业转载请注明出处,并保留原文的完整链接。