三者暧昧关系简单整理

  在javascript中,prototype、constructor以及__proto__之间有着“著名”的剪不断理还乱的三角关系,楼主就着自己对它们的浅显认识,来粗略地理理以备忘,有不对之处还望斧正。

  楼主的一句话理解就是:某个对象的constructor属性返回该对象构造函数,其__proto__属性是个对象,值和其构造函数的prototype属性值一致。

  先来说说prototype。prototype的解释是“原型”,js的所有函数都有一个prototype属性,其属性值是个对象,原型对象初始值是空的(其实还有constructor属性和__proto__属性,只是不能被枚举,可以参考最下面留言),代码验证:

function Person() {
  this.name = 'hanzichi';
  this.age = 10;
}

var num = 0;
for(var i in Person.prototype)
  num++;

console.log(num); 

  prorotype的主要作用简单来说就是“便于方法或者属性的重用”,可以利用prototype添加扩展属性和方法,举个简单的例子:

function Person() {
  this.name = 'hanzichi';
  this.age = 10;
}

Person.prototype.show = function() {
  console.log(this.name);
};

Person.prototype.sex = 'male';

var a = new Person();
console.log(a.sex); // male
a.show(); // hanzichi
console.log(a.__proto__ === Person.prototype); // true 对象a的__proto__值取自构造函数Person的prototype值
console.log(a.constructor === Person); // true  对象a的构造函数是Person
console.log(Person.prototype.constructor === Person); // true 

  如上,所有用Person函数构造的对象都可以使用sex值,调用show方法。

  那么问题来了,以上代码Person函数的prototype值是什么?我们尝试打印(console.log(Person.prototype)):

  我们发现Person.prototype值确实是个对象,本身有两个属性(show和sex),这是自定义的,还有两个属性(constructor和__proto__ 可以通过hasOwnProperty验证),一个是constructor,值为函数本身,另一个就是__proto__(据说ie不支持 楼主没测试),其值为父函数的prototype属性值。因为Person继承自Object,故其__proto__值为Object的prototype属性值,也就是说Person继承了Object本身的所有方法,我们可以展开来细看:

  说完prototype,我们再来看看对象实例有哪些属性,我们也将它打印出来(console.log(a)):

  我们看到实例对象a除了本身自带属性外,也有个属性__proto__

  我们通过a.show()调用了show方法,但是show方法并不显示在对象本身属性里(可通过hasOwnProperty验证),为何能用?又是__proto__!所有的实例对象都有个__proto__属性,我们看到它的值跟Person.prototype一致,也就是说实例a继承了Person类的属性方法。本身的属性里找不到show方法,自动去__proto__中寻找。

  再说说constructor,其值返回该对象的构造函数:

console.log('string'.constructor);  // function String() { [native code] }
console.log(new String('string').constructor);  // function String() { [native code] }
console.log(/hello/.constructor); // function RegExp() { [native code] }
console.log([1, 2, 3].constructor); // function Array() { [native code] }
function A() {};
var a = new A();
console.log(a.constructor); // function A() {} 

  我们依旧看最前面的代码,a.constructor返回的是a的构造函数,也就是Person,其实实例对象a本身并没有constructor属性,但是a中的__proto__拥有constructor属性,没错,a本身没有,就会从它的__proto__属性中寻找constructor方法,如果还没有,就继续从__proto__属性的__proto__属性中寻找... 这样就构成了一个原型链。

  我们似乎已经习惯了用new的方式来构造对象,其实new方式的核心实现要分为三个步骤,如下:

function Person() {
  this.name = 'hanzichi';
  this.age = 10;
}

Person.prototype.show = function() {
  console.log(this.name);
};

Person.prototype.sex = 'male';

var a = {};  // 1
a.__proto__ = Person.prototype;  // 2
Person.call(a); // 3

console.log(a.sex); // male
a.show(); // hanzichi 

  以上代码一目了然。先初始化一个空对象,然后空对象继承构造函数的prototype值,最后call构造函数初始化。

  再来看一段稍微复杂一点的代码:

function Person() {
  this.name = 'hanzichi';
  this.age = 10;
}

Person.prototype.show = function() {
  console.log(this.name);
};

Person.prototype.sex = 'male';

function Child() {};
Child.prototype = new Person();

var a = new Child();
console.log(a);

  

  这是一种简单的继承代码,先不管代码对错,我们看看代码执行中发生了什么。

  Person函数前面已经分析了,我们又构造了一个Child函数,我们把一个实例化的Person对象(new Person())赋值给了Child的prototype属性,也就是说Child继承了Person的所有方法属性,可以可以尝试打印Child.prototype看看(其值其实也就是上图中的a.__proto__),这样Child的实例就能使用Person的属性方法了。而以上实例对象a如果要调用show函数需经过两个的__proto__原型链传递:

学以致用

  试着来做道题看看有没有理解:

function t1(name) {
  if(name) this.name  = name;
}

function t2(name) {
  this.name = name;
}

function t3(name) {
  this.name = name || "test";
}

t1.prototype.name = "hanzichi";
t2.prototype.name = "hanzichi";
t2.prototype.name = "hanzichi";

console.log(new t1().name, new t2().name, new t3().name); 

  答案:hanzichi undefined test

  其实也就是本身有name属性就用,没有就从原型链中寻找。2和3的话都是本身已经拥有,而1是本身没有name属性。ps:没有传入实参而在函数中使用形参的话会被解释成undefined。

  恩,再看一道:

function Person() {
  this.name = 'hanzichi';
  this.age = 10;
}

Person.prototype.sex = 'female';

var a = new Person();
console.log(a.sex);

Person.prototype.sex = 'male';
console.log(a.sex);

Person.prototype = {
  sex: 'female'
};

console.log(a.sex);

  答案:female male male

  为什么会这样?

  一开始Person.prototype指向一个对象,如上图1所示指向对象1,而初始化一个实例后,该实例的__proto__属性同时指向了Person.prototype,即指向了Person.prototype指向的对象1,如上图2,这时a.sex就会返回对象1中sex的值,而因为Person.prototype和a.__proto__引用同一个对象,所以都能改变该对象的值,如下代码也可以同时验证:

function Person() {
  this.name = 'hanzichi';
  this.age = 10;
}

Person.prototype.sex = 'female';

var a = new Person();

a.__proto__.sex = 'male';

console.log(Person.prototype.sex);  // male
console.log(a.sex); // male

  而Person.prototype = {...}后Person.prototype引用了一个新的对象,如上图3操作后Person.prototype引用了对象2,但是实例a还是引用在原来的对象1上。

总结

  javascript中每个对象除了本身的属性外,还有一个__proto__属性,继承了父对象的方法和属性(形成原型链);而每个函数有个prototype属性,该属性值是个对象,该对象函数自定义的一些属性方法外,还有两个属性,constructor(其值一般为函数本身)和__proto__(其值继承自父对象)。

  其实楼主对于以上了解的也很浅显,欢迎指导拍砖~

javascript中prototype、constructor以及__proto__之间的三角关系的更多相关文章

  1. 在 JavaScript 中 prototype 和 __proto__ 有什么区别

    本文主要讲三个 问题 prototype 和 proto function 和 object new 到底发生了什么 prototype 和 proto 首先我们说下在 JS 中,常常让我们感到困惑的 ...

  2. Javascript中Function,Object,Prototypes,__proto__等概念详解

    http://anykoro.sinaapp.com/2012/01/31/javascript%E4%B8%ADfunctionobjectprototypes__proto__%E7%AD%89% ...

  3. Javascript中prototype属性详解 (存)

    Javascript中prototype属性详解   在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不 ...

  4. (转载)详解Javascript中prototype属性(推荐)

    在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不存在类(Class)的概念的,javascript中不 ...

  5. 深入浅析JavaScript中的constructor

    constructor 属性返回对创建此对象的数组函数的引用.本文给大家介绍JavaScript中的constructor ,需要的朋友参考下吧 定义和用法 constructor 属性返回对创建此对 ...

  6. 【转】JavaScript中的constructor与prototype

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

  7. 彻底搞清javascript中this, constructor, prototype

    说起这三个属性,肯定有一些同学和我一样,初学js时非常困惑,头大,一脸的迷茫.今天就来给大家彻底解决这些担心受怕的问题. 先看this this定义: this就是函数赖以执行的对象. 分析这句话: ...

  8. javascript中prototype与__proto__

    1.prototype:构造函数独有的属性: __proto__:每个对象都有一个名为__proto__的属性: 注意:每个构造函数(自带与自创)都有一个prototype的属性,构造函数的proto ...

  9. Javascript中的原型链,__proto__和prototype等问题总结

    1.js中除了原始数据类型 都是对象. 包括函数也是对象,可能类似于C++函数对象把 应该是通过解释器 进行()操作符重载或其他操作, 用的时候把它当函数用就行 但是实际上本质是一个对象 原型也是一个 ...

随机推荐

  1. Effective Java 54 Use native methods judiciously

    Java Native Interface(JNI) allows Java applications to call native methods, which are special method ...

  2. Windows Server 2008 下ASP程序连接ORACLE数据库驱动错误

    今天开发那边升级.改造系统过程中,在测试服务器碰到关于ASP程序连接ORACLE数据库的小问题,虽然是小问题,但是整起来真要命啊,花了不少时间,主要是ASP程序啊,这种上古神器,哥还是当年毕业的时候弄 ...

  3. Command Network

    Command Network Time Limit: 1000MSMemory Limit: 131072K Total Submissions: 11970Accepted: 3482 Descr ...

  4. Java性能调优笔记

    Java性能调优笔记 调优步骤:衡量系统现状.设定调优目标.寻找性能瓶颈.性能调优.衡量是否到达目标(如果未到达目标,需重新寻找性能瓶颈).性能调优结束. 寻找性能瓶颈 性能瓶颈的表象:资源消耗过多. ...

  5. HTML5性能优化

    HTML5性能优化 在看完这两章内容之后,我意犹未尽,于是乎从网上搜索关键字“Java Web高性能”,在IBM社区找到两篇不错的文章,而让人更意外的是我发现那两篇文章的内容跟<高性能HTML5 ...

  6. 写一个ActionFilter检测WebApi接口请求和响应

    我们一般用日志记录每次Action的请求和响应,方便接口出错后排查,不过如果每个Action方法内都写操作日志太麻烦,而且客户端传递了错误JSON或XML,没法对应强类型参数,请求没法进入方法内, 把 ...

  7. js统计字符串中各种字符情况

    问题描述:在一个字符串中,统计出大写字母.小写字母.数字和其他字符各数.这个算法以前在学习java的时候,老师说过,而且说了四种算法.在孔乙己的世界里,茴香豆的"茴"字有四种写法嘛 ...

  8. Normalize.css的使用(重置表)

    本文译自Normalize.css官网: http://nicolasgallagher.com/about-normalize-css/ Normalize.css 只是一个很小的CSS文件,但它在 ...

  9. ACdream OJ 1099 瑶瑶的第K大 --分治+IO优化

    这题其实就是一个求数组中第K大数的问题,用快速排序的思想可以解决.结果一路超时..原来要加输入输出优化,具体优化见代码. 顺便把求数组中第K大数和求数组中第K小数的求法给出来. 代码: /* * th ...

  10. 导航(NavanavigationController)push和pop

    //跳转到指定的控制器 for (UIViewController *Vc in self.navigationController.viewControllers) { if ([Vc isKind ...