先看一个例子

function User(){}

var u1 = new User();
console.log(u1.prototype);// undefined 使用对象实例无法访问到prototype
console.log(User.prototype);//{},使用构造函数名访问prototype
console.log(u1.__proto__);//{},使用对象实例访问prototype的指针

这个是 __proto__ 和prototype最基本的区别:说明构造的对象无prototype属性,每一个对象相当于自带一个 __proto__指针,指向其父类,和C中的单链表类似(这里形象的比喻)

每个函数创建时默认带有一个prototype属性,其中包含一个constructor属性,和一个指向Object对象的隐藏属性 __proto__

1、constructor属性的值为该函数的对象

2、在一个函数前面加上new来调用,则会创建一个隐藏连接到该函数prototype 成员的新对象(由__proto__属性来链接),同时函数的this将会被绑定到那个新对象上。

函数总是返回一个值;如果没有指定返回值,就返回undefined;如果当做构造函数来调用,且返回值不是对象,则返回this(该新对象);如果返回值是对象,则它作为构造函数是没有意义的!

定义

继承方面,JavaScript 中的每个对象都有一个内部私有的链接指向另一个对象,这个对象就是原对象的原型。这个原型对象也有自己的原型,直到对象的原型为 null 为止(也就是没有原型)。这种一级一级的链结构就称为原型链

虽然这通常会被称作 JavaScript 的弱点之一,实际上这种原型继承的模型要比经典的继承模型还要强大。虽然在原型模型上构建一个经典模型是相当琐碎的,但如果采取其他方式实现则会更加困难。

使用不同的方法来创建对象和生成原型链

使用普通语法创建对象

var o = {a: 1};

// o这个对象继承了Object.prototype上面的所有属性
// 所以可以这样使用 o.hasOwnProperty('a').
// hasOwnProperty 是Object.prototype的自身属性。
// Object.prototype的原型为null。
// 原型链如下:
// o ---> Object.prototype ---> null var a = ["yo", "whadup", "?"]; // 数组都继承于Array.prototype (indexOf, forEach等方法都是从它继承而来).
// 原型链如下:
// a ---> Array.prototype ---> Object.prototype ---> null function f(){
return 2;
} // 函数都继承于Function.prototype(call, bind等方法都是从它继承而来):
// f ---> Function.prototype ---> Object.prototype ---> null

使用构造方法创建对象

在 JavaScript 中,构造方法其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)。

function Graph() {
this.vertexes = [];
this.edges = [];
} Graph.prototype = {
addVertex: function(v){
this.vertexes.push(v);
}
}; var g = new Graph();
// g是生成的对象,他的自身属性有'vertexes'和'edges'.
// 在g被实例化时,g.[[Prototype]]指向了Graph.prototype.

使用 Object.create 创建对象

ECMAScript 5 中引入了一个新方法:Object.create。可以调用这个方法来创建一个新对象。新对象的原型就是调用 create 方法时传入的第一个参数:

var a = {a: 1};
// a ---> Object.prototype ---> null var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (继承而来) var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype

1、原型链

// 函数声明
function Person() {}
// 函数表达式
var Man = function() {} console.log(Person.__proto__ === Function.prototype) // true
console.log(Man.__proto__ === Function.prototype) // true
console.log(Function.prototype.__proto__ === Object.prototype) // true
console.log(Object.prototype.__proto__ === null ) // true Object.prototype.a =1 ;
Function.prototype.a =10; console.log(Man.a); // 10

从上面我们可以看到原型链

2、所有构造器/函数的__proto__都指向Function.prototype

console.log(Number.__proto__ === Function.prototype) // true
console.log(Boolean.__proto__ === Function.prototype) // true
console.log(String.__proto__ === Function.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Array.__proto__ === Function.prototype) // true
console.log(RegExp.__proto__ === Function.prototype) // true
console.log(Error.__proto__ === Function.prototype) // true
console.log(Date.__proto__ === Function.prototype) // true

不过、Math,JSON是以对象形式存在的,无需new。它们的__proto__是Object.prototype

console.log(Math.__proto__ === Object.prototype) // true
console.log(JSON.__proto__ === Object.prototype) // true

3、所有的构造器都来自于Function.prototype

包括根构造器Object及Function自身。所有构造器都继承了Function.prototype的属性及方法。如length、call、apply、bind(ES5)、Function.prototype也是唯一一个typeof XXX.prototype为 “function”的prototype。其它的构造器的prototype都是一个对象。如下

console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype) // object
console.log(typeof Number.prototype) // object
console.log(typeof Boolean.prototype) // object
console.log(typeof String.prototype) // object
console.log(typeof Array.prototype) // object
console.log(typeof RegExp.prototype) // object
console.log(typeof Error.prototype) // object
console.log(typeof Date.prototype) // object
console.log(typeof Object.prototype) // object

有一个东西需要理清楚

console.log(typeof Object.prototype) // object
console.log(Object.__proto__ === Function.prototype) // true
console.log(Function.prototype.__proto__ === Object.prototype) // true
console.log(Object.__proto__.__proto__=== Object.prototype) // true
console.log(Function.prototype.__proto__ .__proto__=== null) // true

所以最顶级的就是Object.prototype.__proto__ === null

4、上面说的“所有构造器/函数”当然包括自定义的

function Person(name) {
this.name = name
}
var p = new Person('jack')
console.log(p.__proto__.__proto__ === Person.prototype.__proto__) // true
console.log(Person.prototype.__proto__ === Object.prototype) // true

5、p是Person的实例对象,p的内部原型总是指向其构造器Person的prototype。 每个对象都有一个constructor属性,可以获取它的构造器,因此以下打印结果也是恒等的

function Person(name) {
this.name = name
}
var p = new Person('jack')
console.log(p.__proto__ === p.constructor.prototype) // true

6、看一段代码 原文地址 http://segmentfault.com/q/1010000003791600/a-1020000003793415 ,关于prototype 和 __proto__ 的区别

简介:__proto__其实就是 [[prototype]]属性原本是不可用 js 访问的,后来(据说标准里又规定可以)firefox 和 chrome 中把这个属性命名为__proto__。后来ES又添加了函数getPrototypeof,这样就可以通过这个函数来访问这个属性,所以这个__proto__现在不是标准的一部分

代码一

<script type="text/javascript">

var animal = function(){};
var dog = function(){}; animal.price = 2000;
dog.__proto__ = animal;
var tidy = new dog(); console.log(dog.__proto__ === animal) //true
console.log(dog.__proto__.__proto__ === animal.__proto__) //true
console.log(dog.__proto__.__proto__.__proto__ === Object.prototype) //true
console.log(dog.__proto__.__proto__.__proto__.__proto__ === null) //true console.log(tidy.__proto__ === dog.prototype) //true
console.log(tidy.__proto__.__proto__ === dog.prototype.__proto__) //true
console.log(tidy.__proto__.__proto__ === Object.prototype) //true
console.log(tidy.__proto__.__proto__.__proto__ === null) //true console.log(dog.price) //2000
console.log(tidy.price) // undefined </script>

代码二

<script type="text/javascript">

var animal = function(){};
var dog = function(){}; animal.price = 2000;
dog.prototype = animal; var tidy = new dog(); console.log(tidy.__proto__ === dog.prototype) //true
console.log(tidy.__proto__.__proto__ === animal.__proto__) //true
console.log(tidy.__proto__.__proto__.__proto__ === Object.prototype) //true
console.log(tidy.__proto__.__proto__.__proto__.__proto__ === null) //true console.log(dog.__proto__ === Function.prototype) //true
console.log(dog.__proto__.__proto__ === Object.prototype) //true
console.log(dog.__proto__.__proto__.__proto__ === null) //true console.log(dog.price) //undefined
console.log(tidy.price) // 2000 </script>

总结

1、所有对象,包括函数对象的原型链最终都指向了Object.prototype,而Object.prototype.__proto__===null,原型链至此结束。
2、Animal.prototype是一个普通对象。
3、Object是一个函数对象,也是Function构造的,Object.prototype是一个普通对象。
4、Object.prototype.__type__指向null。
5、Function.prototype是一个函数对象,前面说函数对象都有一个显示的prototype属性,但是Function.prototype却没有prototype属性,即Function.prototype.prototype===undefined,所有Function.prototype函数对象是一个特例,没有prototype属性。
6、Object虽是Function构造的一个函数对象,但是Object.prototype没有指向Function.prototype,即Object.prototype!==Function.prototype。

Prototype跟Constructor关系介绍

在 JavaScript 中,每个函数对象都有名为“prototype”的属性(上面提到过Function.prototype函数对象是个例外,没有prototype属性),用于引用原型对象。此原型对象又有名为“constructor”的属性,它反过来引用函数本身。这是一种循环引用(i.e. Animal.prototype.constructor===Animal)

console.log('**************constructor****************'); 

console.log('anim.constructor===Animal:'+(anim.constructor===Animal))    ;    //true
console.log('Animal===Animal.prototype.constructor:'+(Animal===Animal.prototype.constructor)) ; //true
console.log('Animal.constructor===Function.prototype.constructor:'+(Animal.constructor===Function.prototype.constructor)); //true
console.log('Function.prototype.constructor===Function:'+(Function.prototype.constructor===Function)); //true
console.log('Function.constructor===Function.prototype.constructor:'+(Function.constructor===Function.prototype.constructor)); //true console.log('Object.prototype.constructor===Object:'+(Object.prototype.constructor===Object)); //true
console.log('Object.constructor====Function:'+(Object.constructor===Function)); //true

上面代码中用到的__proto__目前在IE6/7/8/9中都不支持。IE9中可以使用Object.getPrototypeOf(ES5)获取对象的内部原型。

var p = {}
var __proto__ = Object.getPrototypeOf(p)
console.log(__proto__ === Object.prototype) // true

性能

在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。

遍历对象的属性时,原型链上的每个属性都是可枚举的。

检测对象的属性是定义在自身上还是在原型链上,有必要使用 hasOwnProperty 方法,该方法由所有对象继承自 Object.proptotype

hasOwnProperty 是 JavaScript 中唯一一个只涉及对象自身属性而不会遍历原型链的方法。

注意:仅仅通过判断值是否为 undefined 还不足以检测一个属性是否存在,一个属性可能存在而其值恰好为 undefined

不好的实践:扩展原生对象的原型

一个经常使用的不好实践是扩展 Object.prototype 或者其他内置对象的原型。

该技术被称为 monkey patching,它破坏了对象的封装性。虽然一些流行的框架(如 Prototype.js)在使用该技术,但是该技术依然不是好的实践,附加的非标准的方法使得内置的类型混乱。

扩展内置对象原型的唯一正当理由是移植较新 JavaScript 引擎的特性,如 Array.forEach

结论

在编写使用到原型继承模型的复杂代码前理解原型继承模型十分重要。同时,还要清楚代码中原型链的长度,并在必要时结束原型链,以避免可能存在的性能问题。更进一步,除非为了兼容新 JavaScript 特性,否则永远不要扩展原生对象的原型。

参考文章

http://dmitrysoshnikov.com/ecmascript/javascript-the-core/

http://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript

http://segmentfault.com/q/1010000003791600?_ea=368532

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

JavaScript中的继承与原型链的更多相关文章

  1. JavaScript中的继承(原型链)

    一.原型链 ECMAScript中将原型链作为实现继承的主要方法,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 实例1: function SupType() { this.pro ...

  2. 深入理解JavaScript中的继承:原型链篇

    一.何为原型链 原型是一个对象,当我调用一个对象的方法时,如果该方法没有在对象里面,就会从对象的原型去寻找.JavaScript就是通过层层的原型,形成原型链. 二.谁拥有原型 任何对象都可以有原型, ...

  3. JS中的继承(原型链、构造函数、组合式、class类)

    1.继承 应注意区分继承和实例化,实例化是生成一个对象,这个对象具有构造函数的属性和方法:继承指的应该是利用父类生成一个新的子类构造函数,通过这个子类构造函数实例化的对象,具有子类的属性和方法,同时也 ...

  4. 探讨一下js中的继承和原型链

    ---恢复内容开始--- 每个JS对象一定对应一个原型对象,并从原型对象继承属性和方法. 也就是说 对象的__proto__属性的值就是它所对应的原型对象, 而prototype 只有函数才有的属性. ...

  5. JAVASCRIPT中的作用域和原型链,应该算是难点了,要好好多学学,练练

    今天初六,要上班啦... JAVASCRIPT,看来是丢不了了.. http://www.dengdeng90.com/wordpress/?p=241 http://www.cnblogs.com/ ...

  6. 玩转JavaScript OOP[3]——彻底理解继承和原型链

    概述 上一篇我们介绍了通过构造函数和原型可以实现JavaScript中的“类”,由于构造函数和函数的原型都是对象,所以JavaScript的“类”本质上也是对象.这一篇我们将介绍JavaScript中 ...

  7. JavaScript之继承(原型链)

    JavaScript之继承(原型链) 我们知道继承是oo语言中不可缺少的一部分,对于JavaScript也是如此.一般的继承有两种方式:其一,接口继承,只继承方法的签名:其二,实现继承,继承实际的方法 ...

  8. 《JAVASCRIPT高级程序设计》根植于原型链的继承

    继承是面向对象的语言中,一个最为津津乐道并乐此不疲的话题之一.JAVASCRIPT中的继承,主要是依靠原型链来实现的.上一篇文章介绍过,JAVASCRIPT中,每一个对象都有一个prototype属性 ...

  9. JavaScript高级内容笔记:原型链、继承、执行上下文、作用域链、闭包

    最近在系统的学习JS深层次内容,并稍微整理了一下,作为备忘和后期复习,这里分享给大家,希望对大家有所帮助.如有错误请留言指正,tks. 了解这些问题,我先一步步来看,先从稍微浅显内容说起,然后引出这些 ...

随机推荐

  1. 80x86 CPU 的工作模式

    8086/8088微处理器只有一种工作模式:实地址模式. 32为的80x86微处理器有3种工作模式:实地址模式.保护模式和虚拟8086模式.   实地址模式 对于8086/8088微处理器,实模式是它 ...

  2. 常见的Hadoop十大应用误解

    常见的Hadoop十大应用误解 1.        (误解) Hadoop什么都可以做 (正解) 当一个新技术出来时,我们都会去思考它在各个不同产业的应用,而对于平台的新技术来说,我们思考之后常会出现 ...

  3. zookeeper 伪分布式安装

    1 下载zookeeper安装包 下载地址 http://apache.fayea.com/zookeeper/ 我下载的是zookeeper-3.4.6.tar.gz 2 解压缩 将zookeepe ...

  4. js 中时间格式化的几种方法

    1.项目中时间返回值,很过时候为毫秒值,我们需要转换成 能够看懂的时间的格式: 例如: yyyy-MM-dd HH:mm:ss 2.处理方法(处理方法有多种,可以传值到前端处理,也可以后台可以好之后再 ...

  5. Expectation Maximization-EM(期望最大化)-算法以及源码

    在统计计算中,最大期望(EM)算法是在概率(probabilistic)模型中寻找参数最大似然估计的算法,其中概率模型依赖于无法观测的隐藏变量(Latent Variable).最大期望经常用在机器学 ...

  6. 【BZOJ】【2286】【SDOI2011】消耗战

    虚树+树形DP Orz ZYF……果然好神…… 建虚树先按dfn排序,再用一个单调栈来维护当前这条[链],往里加边……说实话还没弄懂- - 留个坑吧…… RE的原因:这条链往出退的时候没写top--; ...

  7. C语言项目:学生成绩管理系统

    C语言项目:学生成绩管理系统    1.数据结构:学生信息:学号.姓名.年龄.性别.3课成绩    2.功能:   (1)增加学生记录    (2)  删除学生记录    (3)  查找学生信息(学号 ...

  8. opencv2.4中SVD分解的几种调用方法

    原帖地址: http://blog.sina.com.cn/s/blog_6109b5d00101ag7a.html       在摄影测量和计算机视觉中,考虑最优解问题时,经常要用到SVD分解.奇异 ...

  9. go语言之进阶篇方法面向过程和对象函数的区别

    1.方法 (method) 在面向对象编程中,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些函数,这种带有接收者的函数,我们称为方法(method). 本质上,一个方法则是一个和特 ...

  10. MSSQL工作中常用的小技巧

    大概看了一下有接近二十天自己没有写博客了,一来是因为国庆之前公司工作总会比较繁杂一点,国庆自己也需要休息,二来是因为学习一些新的东西,公司写了一天SQL回家看了看以前的笔记,感觉还挺不错,贴出来供大家 ...