对象的prototype和constructor是两个重要的属性,他们老是成对出现,提到constructor的地方,不得不涉及到另一个很是重要的属性prototype,它是js中基于原型继承的一个基础。所谓的成对出现,是由于function的prototype属性指向了一个prototype对象,在prototype对象中又有一个constructor属性,这个constructor属性一样指向一个constructor对象,而这个constructor对象偏偏就是这个function函数自己。javascript
function Person(name) { this.name=name; this.showMe=function() { alert(this.name); } }; var one=new Person('JavaScript'); one.showMe();//JavaScript
不少人见到了久违的new操做符,因而就叫Person为“类”,但是又没有关键字class的出现,以为叫“类”有点勉强。因而退而求其次叫Person为类的构造函数。这些概念好像都没有错,之因此出现这样的状况,多是由于你们都学习了传统的面向对象语言(c++,c#,java等),还有一种思惟定势吧。为了让javascript也面向对象,要在javascript中找到与传统面向对象语言的影子。但是按照javascript的说法,function定义的这个Person就是一个Object(对象),并且仍是一个很特殊的对象,这个使用function定义的对象与使用new操做符生成的对象之间有一个重要的区别。
这个区别就是function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性。(这个以前没有注意过这个区别,代码测试下居然还真是)java
function Person(name) { this.name=name; this.showMe=function() { alert(this.name); } }; var one=new Person('js'); alert(one.prototype)//undefined alert(typeof Person.prototype);//object alert(Person.prototype.constructor);//function Person(name) {...};
one这个对象居然没有prototype属性。。。c++
function Person(name) { this.name=name; this.showMe=function() { alert(this.name); } }; Person.prototype.from=function() { alert('I come from prototype.'); } var one=new Person('js'); one.showMe();//js,这个结果正常 one.from();//I come from prototype.,这个结果有一点奇怪
要解释这个结果就要仔细研究一下new这个操做符了c#
var one=new Person('js');
这个语句执行的过程能够分红下面的语句:segmentfault
var one={}; Person.call(one,'js');
按照《悟透javascript》书中说的,new形式建立对象的过程实际上能够分为三步:数组
第一步是创建一个新对象(叫A吧);函数
第二步将该对象(A)内置的原型对象设置为构造函数(就是Person)prototype 属性引用的那个原型对象;学习
第三步就是将该对象(A)做为this 参数调用构造函数(就是Person),完成成员设置等初始化工做。测试
其中第二步中出现了一个新名词就是内置的原型对象__prop__,注意这个新名词跟prototype对象不是一回事,__prop__(图中标记的inobj)就指向了函数Person的prototype对象。在person的prototype对象中出现的任何属性或者函数均可以在one对象中直接使用,这个就是javascript中的原型继承了。示意图以下所示:this
每一个函数都有一个默认的prototype属性。
若是这个函数被用在建立自定义对象的场景中,咱们称这个函数为构造函数。 好比下面一个简单的例子:
// 构造函数 function Person(name) { this.name = name; } // 定义Person的原型,原型中的属性能够被自定义对象引用 Person.prototype = { getName: function() { return this.name; } } var zhang = new Person("ZhangSan"); console.log(zhang.getName()); // "ZhangSan"
做为类比,咱们考虑下JavaScript中的数据类型 - 字符串(String)、数字(Number)、数组(Array)、对象(Object)、日期(Date)等。
咱们有理由相信,在JavaScript内部这些类型都是做为构造函数来实现的;
同时对数组操做的不少方法(好比concat、join、push)应该也是在prototype属性中定义的。
实际上,JavaScript全部的固有数据类型都具备只读的prototype属性(由于若是修改了这些类型的prototype属性,则哪些预约义的方法就消失了),可是咱们能够向其中添加本身的扩展方法。
constructor始终指向建立当前对象的构造函数。
var arr = [1, 56, 34, 12];// 等价于 var foo = new Array(1, 56, 34, 12); console.log(arr.constructor === Array); // true var Foo = function() { }; // 等价于 var foo = new Function(); console.log(Foo.constructor === Function); // true // 由构造函数实例化一个obj对象 var obj = new Foo(); console.log(obj.constructor === Foo); // true // 将上面两段代码合起来,就获得下面的结论 console.log(obj.constructor.constructor === Function); // true
可是当constructor遇到prototype时,有趣的事情就发生了。 这个现象在个人这篇博客里基于原型建立对象的时候也提到过。连接描述
咱们知道每一个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数。以下例所示:
function Person(name) { this.name = name; }; Person.prototype.getName = function() { return this.name; }; var p = new Person("ZhangSan"); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true // 将上两行代码合并就获得以下结果 console.log(p.constructor.prototype.constructor === Person); // true
当时当咱们从新定义函数的prototype时(这里不是修改而是覆盖),或者成为原型重写,constructor的行为就有点奇怪了,以下示例:
function Person(name) { this.name = name; }; Person.prototype = { getName: function() { return this.name; } }; var p = new Person("ZhangSan"); console.log(p.constructor === Person); // false console.log(Person.prototype.constructor === Person); // false console.log(p.constructor.prototype.constructor === Person); // false
是由于覆盖Person.prototype时,等价于进行以下代码操做:
Person.prototype = new Object({ getName: function() { return this.name; } });
而constructor始终指向建立自身的构造函数,因此此时Person.prototype.constructor === Object,即:
function Person(name) { this.name = name; }; Person.prototype = { getName: function() { return this.name; } }; var p = new Person("ZhangSan"); console.log(p.constructor === Object); // true console.log(Person.prototype.constructor === Object); // true console.log(p.constructor.prototype.constructor === Object); // true
如何修正过来,只须要从新覆盖Person.prototype.constructor便可:
function Person(name) { this.name = name; }; Person.prototype = new Object({ getName: function() { return this.name; } }); Person.prototype.constructor = Person; var p = new Person("ZhangSan"); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true console.log(p.constructor.prototype.constructor === Person); // true
javascript中的一切皆对象,而这些对象的有一个最父层的类就是Object,经常使用的一些属性方法汇总一下,这些方法在判断上述问题以及其余方面颇有用。
Object.constructor //对象的构造函数 Object.hasOwnProperty() //检查对象属性是否被继承 Object.isPrototypeOf() //检查一个对象是不是另一个对象的原型 Object.propertyIsEnumerable() //是否能够经过for/in 循环看到属性 Object.toLocaleString() //返回对象的本地字符串表示 Object.toString() //定义一个对象的字符串表示 Object.valueOf() //制定对象的原始值
最近从图书馆把那边厚厚的《javascript高级程序设计》借过来了,看看单单事件就能将讲那么厚厚一章,打算搞一个基础知识系列~~求监督和共勉