JavaScript中的继承与原型链
先看一个例子
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中的继承与原型链的更多相关文章
- JavaScript中的继承(原型链)
一.原型链 ECMAScript中将原型链作为实现继承的主要方法,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 实例1: function SupType() { this.pro ...
- 深入理解JavaScript中的继承:原型链篇
一.何为原型链 原型是一个对象,当我调用一个对象的方法时,如果该方法没有在对象里面,就会从对象的原型去寻找.JavaScript就是通过层层的原型,形成原型链. 二.谁拥有原型 任何对象都可以有原型, ...
- JS中的继承(原型链、构造函数、组合式、class类)
1.继承 应注意区分继承和实例化,实例化是生成一个对象,这个对象具有构造函数的属性和方法:继承指的应该是利用父类生成一个新的子类构造函数,通过这个子类构造函数实例化的对象,具有子类的属性和方法,同时也 ...
- 探讨一下js中的继承和原型链
---恢复内容开始--- 每个JS对象一定对应一个原型对象,并从原型对象继承属性和方法. 也就是说 对象的__proto__属性的值就是它所对应的原型对象, 而prototype 只有函数才有的属性. ...
- JAVASCRIPT中的作用域和原型链,应该算是难点了,要好好多学学,练练
今天初六,要上班啦... JAVASCRIPT,看来是丢不了了.. http://www.dengdeng90.com/wordpress/?p=241 http://www.cnblogs.com/ ...
- 玩转JavaScript OOP[3]——彻底理解继承和原型链
概述 上一篇我们介绍了通过构造函数和原型可以实现JavaScript中的“类”,由于构造函数和函数的原型都是对象,所以JavaScript的“类”本质上也是对象.这一篇我们将介绍JavaScript中 ...
- JavaScript之继承(原型链)
JavaScript之继承(原型链) 我们知道继承是oo语言中不可缺少的一部分,对于JavaScript也是如此.一般的继承有两种方式:其一,接口继承,只继承方法的签名:其二,实现继承,继承实际的方法 ...
- 《JAVASCRIPT高级程序设计》根植于原型链的继承
继承是面向对象的语言中,一个最为津津乐道并乐此不疲的话题之一.JAVASCRIPT中的继承,主要是依靠原型链来实现的.上一篇文章介绍过,JAVASCRIPT中,每一个对象都有一个prototype属性 ...
- JavaScript高级内容笔记:原型链、继承、执行上下文、作用域链、闭包
最近在系统的学习JS深层次内容,并稍微整理了一下,作为备忘和后期复习,这里分享给大家,希望对大家有所帮助.如有错误请留言指正,tks. 了解这些问题,我先一步步来看,先从稍微浅显内容说起,然后引出这些 ...
随机推荐
- .NET:为什么需要逆变和协变
为啥需要协变和逆变? 我目前想到的理由是:逆变和协变的目的是支持多态. 一个小例子 不明白为啥输出的是false和true. using System; using System.Collection ...
- Java开发环境搭建详解
一.jdk安装与配置 jdk7于3月份刚刚发布,目前eclipse的最新版本中还没有提供对jdk7的编译支持,所以我们只下载jdk6. 下载地址:http://download.java.net/jd ...
- [ IOS ] iOS-控制器View的创建和生命周期
reference to : 1. 控制器View的创建 首先我们来看一下控制器view创建的流程图 控制器view加载.jpeg 从图中我们可以看出,在控制器view加载过程中有两个重要的方法lo ...
- MySQL中的模糊查询和通配符转义
MySQL中实现模糊查询有2种方式:一是用LIKE/NOT LIKE,二是用REGEXP/NOT REGEXP(或RLIKE/NOT RLIKE,它们是同义词). 第一种是标准的SQL模式匹配.它有2 ...
- iOS:NSDate的主要几种时间形式
NSDate:时间的获取和操作 1.获取当前时间 //获取当前日期 NSDate *date = sender.date; NSLog(@"%@",date); 2.将date转换 ...
- 第四章 mybatis批量insert
批量插入sql语句: INSERT INTO table (field1,field2,field3) VALUES ('a',"b","c"), ('a',& ...
- 访问者模式讨论篇:java的动态绑定与双分派
java的动态绑定 所谓的动态绑定就是指程执行期间(而不是在编译期间)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法.java继承体系中的覆盖就是动态绑定的,看一下如下的代码: class ...
- 2018CVPR
CVPR 2018:腾讯图像去模糊.自动人像操纵最新研究 解密运动模糊:走向实用的非特定场景图片去模糊技术 在慢速曝光或快速运动拍摄照片时,图像模糊常常困扰着照片拍摄者.优图实验室的研究人员开发了可以 ...
- 【WCF】HTTP 无法注册 URL 进程,不具有此命名空间的访问权限
背景 如题,在运行WCF宿主主机时,出现了问题. 捕获异常为:HTTP 无法注册 URL http://+:8000/WCF/.进程不具有此命名空间的访问权限(有关详细信息,请参见 http: ...
- Java基础(二):基本数据类型和变量类型
一.java基本数据类型: 变量就是申请内存来存储值.也就是说,当创建变量的时候,需要在内存中申请空间.内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据. Java 的两 ...