在学习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的总结的更多相关文章

  1. 分析js中的constructor 和prototype

    在javascript的使用过程中,constructor 和prototype这两个概念是相当重要的,深入的理解这两个概念对理解js的一些核心概念非常的重要. 我们在定义函数的时候,函数定义的时候函 ...

  2. JavaScript——this、constructor、prototype

    this this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window: 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用. 我们还可以使用a ...

  3. 深入分析js中的constructor 和prototype

    在javascript的使用过程中,constructor 和prototype这两个概念是相当重要的,深入的理解这两个概念对理解js的一些核心概念非常的重要. 我们在定义函数的时候,函数定义的时候函 ...

  4. JS中的constructor与prototype

    http://www.cnblogs.com/qiantuwuliang/archive/2011/01/08/1930548.html 在学习JS的面向对象过程中,一直对constructor与pr ...

  5. 关于JS中的constructor与prototype

    ======================================================================== 在学习JS的面向对象过程中,一直对constructo ...

  6. 【JavaScript】关于JS中的constructor与prototype

    最初对js中 object.constructor 的认识: 在学习JS的面向对象过程中,一直对constructor与prototype感到很迷惑,看了一些博客与书籍,觉得自己弄明白了,现在记录如下 ...

  7. 面向对象的程序设计(二)理解各种方法和属性typeof、instanceof、constructor、prototype、__proto__、isPrototypeOf、hasOwnProperty

    //理解各种方法和属性typeof.instanceof.constructor.prototype.__proto__.isPrototypeOf.hasOwnProperty. //1.typeo ...

  8. 【转】JavaScript中的constructor与prototype

    最初对js中 object.constructor 的认识: 在学习JS的面向对象过程中,一直对constructor与prototype感到很迷惑,看了一些博客与书籍,觉得自己弄明白了,现在记录如下 ...

  9. 四,前端---constructor与prototype

    这里对于constructor 和 prototype做一个简单的介绍,旨在让大家有一个简单的了解与认识 1:定义与用法 prototype:属性使您有能力向对象添加属性和方法. constructo ...

随机推荐

  1. JAVA基础——最简单的多重循环程序

    Java 循环语句之多重循环 循环体中包含循环语句的结构称为多重循环.三种循环语句可以自身嵌套,也可以相互嵌套,最常见的就是二重循环.在二重循环中,外层循环每执行一次,内层循环要执行一圈. 如下所示: ...

  2. 微信支付(APP)

    折腾了一天,终于搞定了微信支付,总结一下.首先从服务器端获取prepareid,Andorid 端再根据这个prepareid二次签名. 服务器端: 从官网上下载DEMO,Demo中只有JsAPi,M ...

  3. cygwin安装gcc/g++

    安装cygwin如果按照默认的方式一直点下去的话,安装完了会发现没有安装gcc/g++. 这个时候可以在安装文件的目录打开命令行,并输入: setup-x86_64.exe -q -P wget -P ...

  4. 一个Ruby静态代码分析器 rubocop

    A Ruby static code analyzer, based on the community Ruby style guide. http://rubocop.readthedocs.io ...

  5. 修改system 密码

    运行cmd命令行 录入 sqlplus /nolog  无用户名登录 conn /as sysdba  连接到数据本地数据 alter user system identified by passwo ...

  6. 网络流入门—用于最大流的Dinic算法

    "网络流博大精深"-sideman语 一个基本的网络流问题 最早知道网络流的内容便是最大流问题,最大流问题很好理解: 解释一定要通俗! 如右图所示,有一个管道系统,节点{1,2,3 ...

  7. Pandas数据处理实战:福布斯全球上市企业排行榜数据整理

    手头现在有一份福布斯2016年全球上市企业2000强排行榜的数据,但原始数据并不规范,需要处理后才能进一步使用. 本文通过实例操作来介绍用pandas进行数据整理. 照例先说下我的运行环境,如下: w ...

  8. 【JAVA】配置JAVA环境变量

    系统变量新建,添加 变量名JAVA_HOME 变量值为C:\Java\jdk版本号 修改 Path为 %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;

  9. php中curl的使用(一)

    cURL 是一个利用URL语法规定来传输文件和数据的工具,PHP的curl是通过libcurl库与服务器使用各种类型的协议,如HTTP.FTP.TELNET等. PHP curl函数 curl_clo ...

  10. CJOJ 1010【NOIP2003】加分二叉树 / Luogu 1040 加分二叉树(树型动态规划)

    CJOJ 1010[NOIP2003]加分二叉树 / Luogu 1040 加分二叉树(树型动态规划) Description 设 一个 n 个节点的二叉树 tree 的中序遍历为( 1,2,3,-, ...