深入理解__proto__ 、constructor和prototype的关系


版权声明:本文为博主原创文章,未经博主允许不得转载。
- <!doctype html>
- <html>
- <head>
- <meta charset="utf-8">
- </head>
- <body>
- <script type="text/javascript">
- /**
- @authors Benjamin
- @date 2013-11-12 10:23:40
- 深入理解javascript 原型和原型链
- 最近不是很忙,空余时间整理最近几天看到的关于原型和原型链的文章,收获还是不小的。
- 今天十八届三中全会即将闭幕,期待新一届领导班子的改革方略,能够惠民吧
- 房价,户籍都是让80后很蛋疼的问题哈,当然,做为屌丝的我,肯定也是。
- 开篇先了解下两个很有用的属性:
- 一、__proto__属性:
- __proto__属性未来会成为ES6标准的一部分,目前,该属性在各个浏览器下的实现差别也许比较大.Firefox是最先实现的这个魔法属性(magic property)的浏览器,而且该属性在Firefox中的表现也最有望能成为标准.我们通常用的__proto__属性都是从Object.prototype上继承下来的,那到底__proto__与prototype什么关系呢?
- 1)所有构造器/函数的__proto__都指向Function.prototype,它是一个空函数(Empty function)
- 看看实例:
- */
- console.log(Number.__proto__ === Function.prototype) // true
- console.log(Boolean.__proto__ === Function.prototype) // true
- console.log(String.__proto__ === Function.prototype) // true
- console.log(Object.__proto__ === Function.prototype) // true
- console.log(Function.__proto__ === Function.prototype) // true
- console.log(Array.__proto__ === Function.prototype) // true
- console.log(RegExp.__proto__ === Function.prototype) // true
- console.log(Error.__proto__ === Function.prototype) // true
- console.log(Date.__proto__ === Function.prototype) // true
- /*
- JavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建,Math,JSON是以对象形式存在的,无需new。它们的__proto__是Object.prototype。如下
- */
- console.log("对象测试...");
- console.log(Math.__proto__ === Object.prototype);//true
- console.log(JSON.__proto__ === Object.prototype);//true
- /**
- * 上面说的“所有构造器/函数”当然包括自定义的。看下面的例子:
- */
- var Employee = function (){
- };
- function Person(){
- }
- console.log("自定义函数测试...");
- console.log(Employee.__proto__ === Function.prototype);//true
- console.log(Person.__proto__ === Function.prototype);//true
- /**
- * 由以上测试得出,所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了Function.prototype的属性及方法。如length、call、apply、bind(ES5)。另,Function.prototype也是唯一一个typeof XXX.prototype为 “function”的prototype。其它的构造器的prototype都是一个对象,下面来测试下:
- */
- console.log("类型测试中...");
- console.log(typeof Function.prototype) // function
- console.log(typeof Object.prototype) // object
- console.log(typeof Number.prototype) // object
- console.log(typeof Boolean.prototype) // object
- console.log(typeof String.prototype) // object
- console.log(typeof Array.prototype) // object
- console.log(typeof RegExp.prototype) // object
- console.log(typeof Error.prototype) // object
- console.log(typeof Date.prototype) // object
- console.log(typeof Object.prototype) // object
- /**
- * Function.prototype 是一个空函数,我们来测试下
- */
- console.log("空函数测试...");
- console.log(Function.prototype);//function(){}
- //用alert来看会更直接点
- //alert(Function.prototype);//function(){}
- /**
- * 下面我们来看看Function.prototype 的__proto__是谁呢?看下面的测试
- */
- console.log("Function.prototype的__proto__测试...");
- console.log(Function.prototype.__proto__ === Object.prototype);//true
- /**
- * 那Object.prototype的__proto__又是谁呢?看下面测试
- */
- console.log(Object.prototype.__proto__);//输出null
- /*
- 2)所有对象的__proto__都指向其构造器的prototype
- 来看看以下实例
- */
- console.log("对象的__proto__测试中...");
- //函数声明
- function Foo(){
- }
- var foo = new Foo();//对象实例化
- console.log(foo.__proto__ === Foo.prototype);//true
- //函数表达式
- var Foo = function (){}
- var foo = new Foo();//对象实例化
- console.log(foo.__proto__ === Foo.prototype);//true
- /**
- * 看完自定义函数,下面我们来看看javascript引擎内置构造器
- */
- var
- arr = ["aaa", "bbb"],
- reg = /^abc$/g,
- obj = {
- name: "张三",
- age: 20
- },
- date = new Date(),
- error = new Error("fdasfdasfd");
- console.log("内置构造器的对象测试...");
- console.log(arr.__proto__ === Array.prototype);//true
- console.log(obj.__proto__ === Object.prototype);//true
- console.log(reg.__proto__ === RegExp.prototype);//true
- console.log(date.__proto__ === Date.prototype);//true
- console.log(error.__proto__ === Error.prototype);//true
- /**
- * 二、constuctor属性
- * constructor属性始终指向创建当前对象的构造函数,看下面的实例
- */
- var
- arr = ["aaa", "bbb"],
- reg = /^abc$/g,
- obj = {
- name: "张三",
- age: 20
- },
- date = new Date(),
- error = new Error("fdasfdasfd");
- console.log("constructor指向测试...");
- console.log(arr.constructor === Array);//true
- console.log(obj.constructor === Object);//true
- console.log(reg.constructor === RegExp);//true
- console.log(date.constructor === Date);//true
- console.log(error.constructor === Error);//true
- /**
- * 我们都知道函数是对象,对象都有prototype属性,那么这个prototype的constructor属性指向谁呢?
- * 由以下实例得出结论,其constructor指也向其构造函数
- */
- console.log("prototype的constructor属性指向测试...");
- //函数声明
- function Foo(){
- }
- console.log(Foo.prototype.constructor === Foo);//true
- //函数表达式
- var Foo = function (){
- };
- console.log(Foo.prototype.constructor === Foo);
- /**
- * 那么一个函数的实例指向谁呢?
- */
- function Foo(){
- }
- var f1 = new Foo();
- console.log("测试constructor实例化指向...");
- console.log(f1.constructor === Foo);
- console.log(f1.constructor.prototype.constructor === Foo);
- /**
- * 到此我们来必须来总结下子了:
- * f1 = new Foo();
- * f1.__proto__ === Foo.prototype.__proto__ === Object.prototype
- * Foo.prototype.constructor === Foo
- * Foo.__proto__ === Function.prototype.__proto__ === Object.prototype
- * 这样看起来貌似还是比较乱,下面用一张图来总结以上问题,如下图:
- */
- /**
- * 开一个怪例子,当我们重定义prototype对象时,constructor的行为会有什么变化呢?
- */
- function Foo(name){
- this.name = name;
- }
- Foo.prototype = {
- getName :function (){
- return this.name;
- }
- };
- var f1 = new Foo();
- console.log("重定义prototype对象测试...");
- console.log(f1.constructor === Foo); //false
- console.log(Foo.prototype.constructor === Foo);//false
- console.log(f1.constructor === Object);//true
- console.log(Foo.prototype.constructor === Object);//true
- /**
- * 是什么原因导致的此问题呢?来分析哈子,其实定义Foo.prototype = {}
- * 其实等价于Foo.prototype = new Object({"getName":function(){return this.name;}})
- * 因此Foo.prototype.constuctor === Obeject 返回true
- * 那么怎么让前面的两个false 变为true,做如下操作:
- */
- Foo.prototype.constructor = Foo;
- var f1 = new Foo();
- console.log("重新指向测试...");
- console.log(f1.constructor === Foo); //true
- console.log(Foo.prototype.constructor === Foo);//true
- </script>
- </body>
- </html>
在javascript的使用过程中,constructor 和prototype这两个概念是相当重要的,深入的理解这两个概念对理解js的一些核心概念非常的重要。
我们在定义函数的时候,函数定义的时候函数本身就会默认有一个prototype的属性,而我们如果用new 运算符来生成一个对象的时候就没有prototype属性。我们来看一个例子,来说明这个
function a(c){
this.b = c;
this.d =function(){
alert(this.b);
}
}
var obj = new a('test');
alert(typeof obj.prototype);//undefine
alert(typeof a.prototype);//object
从上面的例子可以看出函数的prototype 属性又指向了一个对象,这个对象就是prototype对象,请看下图
a.prototype 包含了2个属性,一个是constructor ,另外一个是__proto__
这个constructor 就是我们的构造函数a,这个也很容易理解。
那么__proto__ 是什么呢?
这个就涉及到了原型链的概念:
每个对象都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性 时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,这个__proto__又会有自己的__proto__,于是就这样 一直找下去。
请看mozzlia 对它对它的描述
When an object is created, its __proto__
property is set to constructing function's prototype
property. For example var fred = new Employee();
will cause fred.__proto__ = Employee.prototype;
.
This is used at runtime to look up properties which are not declared in the object directly. E.g. when fred.doSomething()
is executed and fred
does not contain adoSomething
, fred.__proto__
is checked, which points to Employee.prototype
, which contains a doSomething
, i.e. fred.__proto__.doSomething()
is invoked.
Note that __proto__
is a property of the instances, whereas prototype
is a property of their constructor functions.
不管你信不信,我们来看图
在后面如果加上 alert(obj.__proto__ === a.prototype) //true
同理,在这里我们来分析出new 运算符做了那些事情
- var obj={}; 也就是说,初始化一个对象obj。
- obj.__proto__=a.prototype;
- a.call(obj);也就是说构造obj,也可以称之为初始化obj。
我们将这个例子改造一些,变得复杂一点。
function a(c){
this.b = c;
this.d =function(){
alert(this.b);
}
}
a.prototype.test = function(){
alert(this.b);
}
var obj = function (){}
obj.prototype = new a('test');
obj.prototype.test1 =function(){
alert(22222);
}
var t = new obj('test');
t.test();//alert('test');
我们来分析下这个过程
由 var t = new obj('test'); 我们可以得到 t.__proto__ = obj.prototype,但是上面指定obj.prototype =new a('test'); 可以这样来看下
obj.prototype = p, p = new a('test'); p.__proto__ = a.prototype;
那么obj.prototype.__proto__ = a.prototype,由 t.__proto__ = obj.prototype 可以得出 t.__proto__.__proto__ = a.prototype,
所以对象t先去找本身是的prototype 是否有test函数,发现没有,结果再往上级找,即 t.__proto__ ,亦即obj.prototype 寻找test函数 ,但是obj.prototype 也没有这个函数,然后再往上找。即
t.__proto__.__proto__ 找,由于t.__proto__.__proto__ = a.prototype 在 a.prototype 中找到了这个方法,输出了alert('test')
从这里可以分析得出一个结论,js中原形链的本质在于 __proto__
再看看一个列子
function a(c){
this.b = c;
this.d =function(){
alert(this.b);
}
}
var obj = new a('test');
alert(obj.constructor);//function a(){}
alert(a.prototype.constructor);//function a(){}
根据上面讲到的__proto__ 我们来分析下,首先obj是没有constructor 这个属性的,但是 obj.__proto__ = a.prototype;就从
a.prototype中寻找,而 a.prototype.constructor 是就a,所有两者的结果是一一样的.