鄙人对constructor和prototype的总结
在学习js面向对象过程中,我们总是对constructor和prototype充满疑惑,这两个概念是相当重要的,深入理解这两个概念对理解js的一些核心概念非常的重要。因此,在这里记录下鄙人见解,希望可以给读者带来一些帮助.如若有错,请大佬们不吝指正,小弟十分感谢!
prototype
参考高程三:无论什么时候,只要创建一个函数,就会根据特定规则为该函数创建prototype属性,这个属性指向函数的原型对象.
换言之,我们自己定义的函数在这里我称之为自定义函数吧,每个自定义函数都有一个默认的prototype属性.然后这个属性指向函数的原型对象.那这个原型对象有什么特别呢?
如果这个自定义函数被用在创建自定义对象的场景中,我们称这个函数为构造函数。构造函数中原型对象中的属性和方法可以被使用该构造函数创建出来的实例对象使用.即以构造函数方式创建出来的所有实例对象,自动拥有和共享该构造函数的原型对象中的所有属性和方法.利用这点,我们看下面的代码:
//定义构造函数Animal
function Animal(name) {
this.name = name;
}
//设置构造函数Animal原型对象上的方法
Animal.prototype.shout =function (){
console.log('wow wow');
}
//通过构造函数Animal创建实例对象a1,该过程称为实例化
var a1 = new Animal('小白');
a1.shout(); // 'wow wow'
那实例对象为什么可以继承创建该实例的构造函数的原型对象上的属性/方法呢?这里说的很绕口,我们再引入一个概念.
当用构造函数创建实例对象的时候,该实例的内部也将包含一个指针(内部属性),指向构造函数的原型对象.ECMA-262第5版中管这个指针叫[[Prototype]].我们把它称为对象的原型.
虽然在脚本中没有标准的方式访问它.但Firefox Safari Chrome在每个对象上都支持一个属性__proto__;后来ECMAScript 5 增加了一个新方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值.其实__proto__和这个方法的作用是一样的(区别一个是部分浏览器厂商实现的非标准,一个是后来推出的标准),都是获取对象的原型.文章后面一律用这个__proto__来表示对象的原型.
一定要区分好原型和原型对象哦.虽然指向的是同一个对象za,但是访问的方法不一样,叫法不一样.鄙人总结了一下:
① 构造函数.prototype(访问构造函数的原型对象)
prototype属性:只有函数才有的,它跟原型链没有关系.它的作用是构造函数new对象时,告诉新创建对象的原型是谁.
② 实例对象.__proto__(访问实例对象的原型)
__proto__属性:是所有对象(包括函数)都有的,它才叫做对象的原型,原型链就是靠它形成的.(不是ECMA标准,仅供开发者调试使用,不要用于正式开发)
构造函数.prototype === 实例对象.__proto__
我们可以画一个图帮助理解

说回刚才的问题,为什么实例对象可以继承原型创建该实例的构造函数的原型对象上的属性/方法吧!
以上面的代码来说.构造函数new形式创建实例对象的过程实际上可以分为
1. var inobj = { } //创建一个内置对象
2. 如果Animal.prototype是Object类型,则将inobj.__proto__设置为Animal.prototype,否则inobj.__proto__将初始化值(即Object.prototype) //设置了新创建对象的__proto__
3. 把inobj赋值给this,Animal.call(inobj)
4. 如果[[Call]]的返回值是Object类型,则返回这个值,否则返回inobj
那么a1就接收了这个返回值,也就是刚刚创建的inobj对象.
那么继承又是怎么实现的?这就要讲到js原型链的搜索机制了.
当对象访问某属性或者调用某个方法时:
① 首先在实例对象中搜索该属性/方法
② 如果没有找到则搜索实例对象.__proto__上的属性/方法
③ 如此一层一层沿着原型链继续向上搜索
④ 直到查找到Object.prototype(原型链的顶端),如果有就直接使用,如果没有,返回undefined或者报错
正是因为有这样的搜索机制,inobj设置了__proto__之后,将会继承原型上的属性和方法,然后继承原型的原型上的属性和方法......
JS原型链的本质在于__proto__,也就是前文所说的[[Prototype]].
constructor
简单一点说,constructor始终指向创建当前实例对象的构造函数.
根据高程三的说法,无论什么时候,只要创建了一个新函数,就会根据特定规则为该函数创建一个prototype属性,这个属性指向函数的原型对象.在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性指向prototype属性所在的函数,也就是指向构造函数.我们通过实例对象.constructor方法访问的其实就是原型对象上的constructor属性

下面的代码为例,a1.constructor----a1有constructor属性吗?没有,然后通过原型链查找到---->a1.__proto__ , 也就是Animal.prototype上的constructor属性
//定义构造函数Animal
function Animal(name) {
this.name = name;
}
//设置构造函数Animal原型对象上的方法
Animal.prototype.shout =function (){
console.log('wow wow');
}
//通过构造函数Animal创建实例对象a1,该过程称为实例化
var a1 = new Animal('小白');
console.log(a1.constructor);
//打印的是创建a1的构造函数Animal
// ƒ Animal(name) {
this.name = name;
}
但是当constructor遇到prototype时,有趣的事情就发生了。
我们知道每个函数都有一个默认的属性prototype,而这个prototype指向的原型对象上的constructor属性默认指向prototype属性所在的函数,也就是这个函数。如下例所示:
function Person(name) {
this.name = name;
};
Person.prototype.sayName = function () {
console.log(this.name);
};
var p = new Person("ZhangSan");
console.log(p.__proto__.constructor === Person); // true
console.log(Person.prototype.constructor === Person); // true
console.log(p.constructor === Person); // true
console.log(p.hasOwnProperty('constructor')); //false //检测实例上有constructor属性吗?答案是false
console.log(p.__proto__.hasOwnProperty('constructor')); //true //在原型对象上检测到有constructor属性存在
因此结合代码输出结果,结合上面的图片就不难理解constructor属性从何而来,指向何处了吧!
结论:构造函数默认有prototype属性,指向原型对象,构造函数的原型对象(实例对象的原型)默认有constructor属性,指向构造函数.
不过,这个constructor 属性易变,不可信赖!
当我们重新定义函数的prototype时(注意:和上例的区别,这里不是修改而是覆盖),
constructor的行为就有点奇怪了,如下面例子:
function Person(name) {
this.name = name;
};
Person.prototype = {
sayName: function() {
console.log(this.name);
}
};
var p = new Person("ZhangSan");
console.log(p.__proto__.constructor === Person); // false
console.log(Person.prototype.constructor === Person); // false
console.log(p.constructor === Person); // false
console.log(p.constructor === Object); // true
console.log(p.__proto__.constructor === Object); // true
console.log(Person.prototype.constructor === Object); // true
为什么呢?
原来是因为覆盖Person.prototype时,等价于进行如下代码操作:
Person.prototype = new Object({
sayName: function() {
console.log(this.name);
}
});
而constructor始终指向创建自身的构造函数,所以此时Person.prototype.constructor === Object
怎么修正这种问题呢?方法也很简单,重新覆盖Person.prototype.constructor即可
function Person(name) {
this.name = name;
};
Person.prototype = {
constructor : Person,
sayName: function() {
console.log(this.name);
}
};
var p = new Person("ZhangSan");
console.log(p.__proto__.constructor === Person); // true
console.log(Person.prototype.constructor === Person); // true
console.log(p.constructor === Person); // true
console.log(p.constructor === Object); // false
console.log(p.__proto__.constructor === Object); // false
console.log(Person.prototype.constructor === Object); // false
所以说constructor属性易变,如果是直接在原型上添加属性和方法倒是不会改变constructor,但是重写原型对象后若没有手动修正constructor属性,那么就不可靠了.
其实 constructor 的出现原本就是用来进行对象类型判断的,既然不可靠,那我们有一种更加安全可靠的判定方法:instanceof 操作符
即使上面没有修正constructor属性,下面的结果依然是true.
console.log(p instanceof Person); //true
由于时间的问题,就不继续写下去了.下面有一个示例,根据这个示例我还画了一个图,有兴趣的可以花点时间看看
//定义Animal构造函数
function Animal() {
}
//定义Dog构造函数
function Dog() {
}
var a1 = new Animal();
Dog.prototype = a1;
Dog.prototype.constructor = Dog; //手动修正constructor
var d1 = new Dog();
console.log(d1.constructor); //Dog构造函数
console.log(Dog.prototype.constructor); //Dog构造函数
console.log(d1 instanceof Dog); //true
console.log(d1 instanceof Animal); //true

有个注意点就是Object.prototype是由Object构造函数实例化出来的,同时Object.prototype是Object构造函数的原型对象,那么Object.prototype的原型__proto__不就是它自己吗?这样就无限循环了,所以系统默认把它的原型__proto__设置为null,好有个终点嘛!然后还有一个注意点就是,由于创建对象的过程都是通过new创建一个inobj,因此在js中,万物皆对象,所有的内置或自定义对象都继承自Object对象,几乎所有的对象都可以使用Object.prototype上面的属性和方法.
画的也不是很完整,constructor没画上去,感觉那样线就太多太乱了.不过细心钻研一下还是能懂的,如若有错,请不吝指正哈!
鄙人对constructor和prototype的总结的更多相关文章
- 分析js中的constructor 和prototype
在javascript的使用过程中,constructor 和prototype这两个概念是相当重要的,深入的理解这两个概念对理解js的一些核心概念非常的重要. 我们在定义函数的时候,函数定义的时候函 ...
- JavaScript——this、constructor、prototype
this this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window: 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用. 我们还可以使用a ...
- 深入分析js中的constructor 和prototype
在javascript的使用过程中,constructor 和prototype这两个概念是相当重要的,深入的理解这两个概念对理解js的一些核心概念非常的重要. 我们在定义函数的时候,函数定义的时候函 ...
- JS中的constructor与prototype
http://www.cnblogs.com/qiantuwuliang/archive/2011/01/08/1930548.html 在学习JS的面向对象过程中,一直对constructor与pr ...
- 关于JS中的constructor与prototype
======================================================================== 在学习JS的面向对象过程中,一直对constructo ...
- 【JavaScript】关于JS中的constructor与prototype
最初对js中 object.constructor 的认识: 在学习JS的面向对象过程中,一直对constructor与prototype感到很迷惑,看了一些博客与书籍,觉得自己弄明白了,现在记录如下 ...
- 面向对象的程序设计(二)理解各种方法和属性typeof、instanceof、constructor、prototype、__proto__、isPrototypeOf、hasOwnProperty
//理解各种方法和属性typeof.instanceof.constructor.prototype.__proto__.isPrototypeOf.hasOwnProperty. //1.typeo ...
- 【转】JavaScript中的constructor与prototype
最初对js中 object.constructor 的认识: 在学习JS的面向对象过程中,一直对constructor与prototype感到很迷惑,看了一些博客与书籍,觉得自己弄明白了,现在记录如下 ...
- 四,前端---constructor与prototype
这里对于constructor 和 prototype做一个简单的介绍,旨在让大家有一个简单的了解与认识 1:定义与用法 prototype:属性使您有能力向对象添加属性和方法. constructo ...
随机推荐
- 【Android Developers Training】 105. 显示一个位置地址
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 10.解决VUEX刷新的时候出现数据消失
通常,我们在使用vue编写页面时,会需要使用vuex在组件间传递(或者说共同响应)同一个数据的变化.例如:用户的登录信息. 下面,我们使用传递用户登录信息的例子来一步步解决这个问题. 首先,我们的第一 ...
- ng-cordova(插件库)
ng-cordova 环境配置 1.执行以下命令 bower install ngCordova 2.引用文件(在引用cordova.js之前引用) <script src="lib/ ...
- H5仿微信界面教程(一)
前言 先来张图,仿微信界面,界面如下,并不完全一模一样,只能说有些类似,希望大家见谅. 1 用到的知识点 jQuery WeUI 是WeUI的一个jQuery实现版本,除了实现了官方插件之外,它还提供 ...
- gulp实用配置(2)——中小项目
上一篇的gulp配置很简单,主要就是为了demo的查看和调试,这一篇则会相对详细一些,包括压缩合并打时间戳等. 在互联网环境比较好的城市,需要多人协作的,大一点的项目应该都用上了模块化(这里主要指co ...
- 谈谈关于PHP的代码安全相关的一些致命知识
谈谈关于PHP的代码安全相关的一些致命知识 目标 本教程讲解如何防御最常见的安全威胁:SQL 注入.操纵 GET 和 POST 变量.缓冲区溢出攻击.跨站点脚本攻击.浏览器内的数据操纵和远程表单提交. ...
- jpg、jpeg、png... 的区别
对于做设计这一行的人来说,这几个图片格式是最常用的,也是最常见的,几乎每一天都要与他们打交道. 刚刚入门的新人通常不知道在什么地方如何使用他们或者说如何更有效的使用他们. 那他们到底是有什么区别?(一 ...
- 防止js全局变量污染方法总结
javaScript 可以随意定义保存所有应用资源的全局变量.但全局变量可以削弱程序灵活性,增大了模块之间的耦合性.在多人协作时,如果定义过多的全局变量 有可能造成全局变量冲突,也就是全局变量污染问题 ...
- python多线程爬虫设计及实现示例
爬虫的基本步骤分为:获取,解析,存储.假设这里获取和存储为io密集型(访问网络和数据存储),解析为cpu密集型.那么在设计多线程爬虫时主要有两种方案:第一种方案是一个线程完成三个步骤,然后运行多个线程 ...
- JavaScript一个简易枚举类型实现扑克牌
<script type="text/javascript"> /** * 这个函数创建一个新的枚举类型,实参对象表示类的每个实例的名字和值 * 返回值是一个构造函数, ...