先看一个例子

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. 学习node js 之微信公众帐号接口开发 准备工作之三

    app.js文件介绍,因为也是初学,以下的内容是个人的理解,有些不正确的地方请评论中指证:以注解的形式说明. //依赖组件[模块]导入 var express = require('express') ...

  2. [翻译] EnterTheMatrix

    Enter The Matrix https://github.com/mpospese/EnterTheMatrix The sample application to accompany my c ...

  3. js自动补全实例

    var oInputField ,oPopDiv , oColorsUl,aColors; //初始化变量 function initVars(modelId,divId,ulId){ oInputF ...

  4. Android图片加载框架最全解析(三),深入探究Glide的缓存机制

    在本系列的上一篇文章中,我带着大家一起阅读了一遍Glide的源码,初步了解了这个强大的图片加载框架的基本执行流程. 不过,上一篇文章只能说是比较粗略地阅读了Glide整个执行流程方面的源码,搞明白了G ...

  5. python植入后门backdoor程序的方法?

    后门程序 from gevent.backdoor import BackdoorServer server = BackdoorServer((), banner="Hello from ...

  6. Objective-C:MRC手动释放对象内存举例(引用计数器)

    手机内存下的类的设计练习: 设计Book类, 1.三个成员变量:    title(书名)author(作者).price(价格) 2.不使用@property,自己完成存取方法(set方法,get方 ...

  7. OpenCV学习(22) opencv中使用kmeans算法

    kmeans算法的原理参考:http://www.cnblogs.com/mikewolf2002/p/3368118.html 下面学习一下opencv中kmeans函数的使用.      首先我们 ...

  8. IntPtr 转 string

    假设有 intPtr pBuffer 方法一: 直接使用Marshal.PtrToStringAnsi方法: string ss = Marshal.PtrToStringAnsi(pBuffer); ...

  9. MySql的入侵测试以及防范

    在做了之前的SQL SERVER之后,便很想尝试一下MYSQL的入侵测试已经防范,与大家一起分享. 总的来说,我一直在用的是MYSQL,对MYSQL比较熟悉,相比较而言,感觉MYSQL更安全,这只是我 ...

  10. Inception in CNN

    之前也写过GoogLeNet的笔记.但那个时候对Inception有些似懂非懂,这周又一次看了一遍,觉得有了新的体会,特地又一次写一篇博客与它再续前缘. 本文属于论文笔记性质.特此声明. Networ ...