揭开js之constructor属性的神秘面纱
揭开 constructor
在 Javascript 语言中,constructor 属性是专门为 function 而设计的,它存在于每一个 function 的prototype 属性中。这个 constructor 保存了指向 function 的一个引用。
在定义一个函数(代码如下所示)时,
function F() {
// some code
}
JavaScript 内部会执行如下几个动作:
1.为该函数添加一个原形(即 prototype)属性
2. 为 prototype 对象额外添加一个 constructor 属性,并且该属性保存指向函数F 的一个引用
这样当我们把函数 F 作为自定义构造函数来创建对象的时候,对象实例内部会自动保存一个指向其构造函数(这里就是我们的自定义构造函数 F)的 prototype 对象的一个属性proto,
所以我们在每一个对象实例中就可以访问构造函数的 prototype 所有拥有的全部属性和方法,就好像它们是实例自己的一样。当然该实例也有一个 constructor属性了(从 prototype 那里获得的),每一个对象实例都可以通过 constrcutor 对象访问它的构造函数,请看下面代码:
var f = new F();
alert(f.constructor === F);// output true
alert(f.constructor === F.prototype.constructor);// output true
我们可以利用这个特性来完成下面的事情:
对象类型判断,如
if(f.constructor === F) {
// do sth with F
}
其实 constructor 的出现原本就是用来进行对象类型判断的,但是 constructor 属性易变,不可信赖。我们有一种更加安全可靠的判定方法:instanceof 操作符。下面代码
仍然返回 true
if(f instanceof F) {
// do sth with F
}
原型链继承,由于 constructor 存在于 prototype 对象上,因此我们可以结合
constructor 沿着原型链找到最原始的构造函数,如下面代码:
function Base() {}
// Sub1 inherited from Base through prototype chain
function Sub1(){}
Sub1.prototype = new Base();
Sub1.prototype.constructor = Sub1;
Sub1.superclass = Base.prototype;
// Sub2 inherited from Sub1 through prototype chain
function Sub2(){}
Sub2.prototype = new Sub1();
Sub2.prototype.constructor = Sub2;
Sub2.superclass = Sub1.prototype;
// Test prototype chain
alert(Sub2.prototype.constructor);// function Sub2(){}
alert(Sub2.superclass.constructor);// function Sub1(){}
alert(Sub2.superclass.constructor.superclass.constructor);// function Base(){}
上面的例子只是为了说明 constructor 在原型链中的作用,更实际一点的意义在于:一个子类对象可以获得其父类的所有属性和方法,称之为继承。
之前提到 constructor 易变,那是因为函数的 prototype 属性容易被更改,我们用时下很流行的编码方式来说明问题,请看下面的示例代码:
function F() {}
F.prototype = {
_name: 'Eric',
getName: function() {
return this._name;
}
};
初看这种方式并无问题,但是你会发现下面的代码失效了:
var f = new F();
alert(f.constructor === F); // output false
怎么回事?F 不是实例对象 f 的构造函数了吗?当然是!只不过构造函数 F 的原型被开发者重写了,这种方式将原有的 prototype 对象用一个对象的字面量{}来代替。而新建的对象{}只是 Object 的一个实例,系统(或者说浏览器)在解析的时候并不会在{}上自动添加一个 constructor 属性,因为这是 function 创建时的专属操作,仅当你声明函数的时候解析器才会做此动作。然而你会发现 constructor 并不是不存在的,下面代码可以
测试它的存在性:
alert(typeof f.constructor == 'undefined');// output false
既然存在,那这个 constructor 是从哪儿冒出来的呢?我们要回头分析这个对象字面量
{}。因为{}是创建对象的一种简写,所以{}相当于是 new Object()。那既然{}是 Object
的实例,自然而然他获得一个指向构造函数 Object()的 prototype 属性的一个引用proto,又因为 Object.prototype 上有一个指向 Object 本身的 constructor属性。所以可以看出这个constructor其实就是Object.prototype的constructor,
下面代码可以验证其结论:
alert(f.constructor === Object.prototype.constructor);//output true
alert(f.constructor === Object);// also output true
一个解决办法就是手动恢复他的 constructor,下面代码非常好地解决了这个问题:
function F() {}
F.prototype = {
constructor: F, /* reset constructor */
_name: 'Eric',
getName: function() {
return this._name;
}
};
之后一切恢复正常,constructor 重新获得的构造函数的引用,我们可以再一次测试上面的代码,这次返回 true
var f = new F();
alert(f.constructor === F); // output true this time ^^
解惑:构造函数上怎么还有一个 constructor ?它又是哪儿来的?
细心的会发现,像 JavaScript 内建的构造函数,如 Array, RegExp, String,Number, Object, Function 等等居然自己也有一个 constructor:
alert(typeof Array.constructor != ‘undefined’);// output true
经过测试发现,此物非彼物它和 prototype 上 constructor 不是同一个对象,他们是共存的:
alert(typeof Array.constructor != 'undefined');// output true
alert(typeof Array.prototype.constructor === Array); // output true
不过这件事情也是好理解的,因为 构造函数也是函数。是函数说明它就是 Function 构造函数的实例对象,自然他内部也有一个指向 Function.prototype 的内部引用proto啦。因此我们很容易得出结论,这个 constructor(构造函数上的constructor 不是 prototype 上的)其实就是 Function 构造函数的引用:
alert(Array.constructor === Function);// output true
alert(Function.constructor === Function); // output true
揭开js之constructor属性的神秘面纱的更多相关文章
- 揭开GrowingIO无埋点的神秘面纱
揭开GrowingIO无埋点的神秘面纱 早在研究用户行为分析的时候,就发现国内的GrowingIO在宣传无埋点技术,最近正好抽出时间来研究一下所谓的无埋点到底是什么样的. 我分六部分来分析一下无埋 ...
- 揭开自然拼读法(Phonics)的神秘面纱
揭开自然拼读法(Phonics)的神秘面纱 自然拼读法 (Phonics),是指看到一个单词,就可以根据英文字母在单词里的发音规律把这个单词读出来的一种方法.即从“字母发音-字母组合发音-单词-简单 ...
- JS中constructor属性
constructor属性用于对当前对象的构造函数的引用.可以用来判断对象的类型: <script> var newStr = new String("One world One ...
- 通过一个生活中的案例场景,揭开并发包底层AQS的神秘面纱
本文导读 生活中案例场景介绍 联想到 AQS 到底是什么 AQS 的设计初衷 揭秘 AQS 底层实现 最后的总结 当你在学习某一个技能的时候,是否曾有过这样的感觉,就是同一个技能点学完了之后,过了一段 ...
- 揭开C++类中虚表的“神秘面纱”
C++类中的虚表结构是C++对象模型中一个重要的知识点,这里咱们就来深入分析下虚表的在内存中的结构. C++一个类中有虚函数的话就会有一个虚表指针,其指向对应的虚表,一般一个类只会有一个虚表,每个虚表 ...
- 毫不留情地揭开 ArrayList 和 LinkedList 之间的神秘面纱
先看再点赞,给自己一点思考的时间,思考过后请毫不犹豫微信搜索[沉默王二],关注这个靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有技术大佬整理的面试题, ...
- 揭开Sass和Compass的神秘面纱
揭开Sass和Compass的神秘面纱 可能之前你像我一样,对Sass和Compass毫无所知,好一点儿的可能知道它们是用来作为CSS预处理的.那么,今天请跟我一起学习下Sass和Compass的一些 ...
- 揭开.NET消息循环的神秘面纱(GetMessage()无法取得任何消息,就会进入Idle(空闲)状态,进入睡眠状态(而不是Busy Waiting)。当消息队列不再为空的时候,程序会自动醒过来)
揭开.NET消息循环的神秘面纱(-) http://hi.baidu.com/sakiwer/item/f17dc33274a04df2a9842866 曾经在Win32平台下奋战的程序员们想必记得, ...
- 【转】再讲IQueryable<T>,揭开表达式树的神秘面纱
[转]再讲IQueryable<T>,揭开表达式树的神秘面纱 接上篇<先说IEnumerable,我们每天用的foreach你真的懂它吗?> 最近园子里定制自己的orm那是一个 ...
随机推荐
- html学习笔记:基本结构,排列清单,表格
<html> <head> 基本结构,排列清单,表格 <title></title> <!--文件标题声明--> <base> ...
- 【Linux】Linux 找回Root用户密码
Root密码破解 Linux 忘记Root密码 ? 技术学习基本原则:不作恶 一.破解步骤 1.在系统启动时进入grub选项菜单 2.在grub选项菜单按e进入编辑模式 3.编辑kernel那行添加/ ...
- MarkDown和流程图诠释你的代码
写在前面:首先感谢导师-猴哥对我的认可(求多分享点编程经验.工具.多开课),学习编程是一个痛苦和快乐的过程,希望大家共勉 本文介绍MarkDown的基本语法.使用MarkDown画简单的流程图.使用X ...
- matlab练习程序(矩形变换为圆)
最近对图像坐标的变换很感兴趣啊,这次是将一张图像变换为圆形. 变换原理就是按变换前后像素到圆心的距离按比例缩减就行了. 改变x,y方向上的系数,应该还可以变换为椭圆,不过我还没有尝试. 注意我这里相当 ...
- spark包
spark-assembly-1.5.2-hadoop2.6.0.jar http://blog.csdn.net/ronaldo4511/article/details/53035494 http: ...
- OC @protocol(声明协议 )
@protocol Study; int main(int argc, const char * argv[]) { @autoreleasepool { Student *stu = [[[Stud ...
- 每天一个linux命令:df 命令
linux中df命令的功能是用来检查linux服务器的文件系统的磁盘空间占用情况.可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息. 1.命令格式: df [选项] [文件] 2.命 ...
- Android(java)学习笔记52:成员位置的内部类的介绍
1. 内部类的定义 /* 内部类概述: 把类定义在其他类的内部,这个类就被称为内部类. 举例:在类A中定义了一个类B,类B就是内部类. 内部的访问特点: A:内部类可以直接访问外部类的成员,包括私有. ...
- Android(java)学习笔记21:Java异常处理机制
1. try....catch / try...catch...finally package cn.itcast_02; /* * 我们自己如何处理异常呢? * A:try...catch... ...
- 转载 【MySql】Update批量更新与批量更新多条记录的不同值实现方法
批量更新 mysql更新语句很简单,更新一条数据的某个字段,一般这样写: UPDATE mytable SET myfield = 'value' WHERE other_field = 'other ...