JavaScript的原型继承
JavaScript是一门面向对象的语言。在JavaScript中有一句很经典的话,万物皆对象。既然是面向对象的,那就有面向对象的三大特征:封装、继承、多态。这里讲的是JavaScript的继承,其他两个容后再讲。
JavaScript的继承和C++的继承不大一样,C++的继承是基于类的,而JavaScript的继承是基于原型的。
现在问题来了。
原型是什么?
function Animal(name) {
this.name = name;
}
Animal.prototype.setName = function(name) {
this.name = name;
}
var animal = new Animal("wangwang");
function Animal(name) {
this.name = name;
}
var animal = new Animal("wangwang");
这时animal只有name属性。如果我们加上一句,
Animal.prototype.setName = function(name) {
this.name = name;
}
这时animal也会有setName方法。
继承本复制——从空的对象开始
方法一:构造复制
方法二:写时复制
方法三:读遍历
- 保证在读取时首先被访问到
- 如果在对象中没有指定属性,则尝试遍历对象的整个原型链,直到原型为空或或找到该属性。
原型链后面会讲。
constructor
var animal = Animal("wangwang");
function Animal(name) {
this.name = name;
return this;
}
猜猜现在animal是什么?
function Animal(name) {
if(!(this instanceof Animal)) {
return new Animal(name);
}
this.name = name;
}
这样就万无一失了。
console.log(Animal.prototype.constructor === Animal); // true
我们可以换种思维:prototype在函数初始时根本是无值的,实现上可能是下面的逻辑
// 设定__proto__是函数内置的成员,get_prototyoe()是它的方法
var __proto__ = null;
function get_prototype() {
if(!__proto__) {
__proto__ = new Object();
__proto__.constructor = this;
}
return __proto__;
}
这样的好处是避免了每声明一个函数都创建一个对象实例,节省了开销。
基于原型的继承
1. 方法一
function Animal(name) {
this.name = name;
}
function Dog(age) {
this.age = age;
}
var dog = new Dog(2);
Dog.prototype = new Animal("wangwang");
这时,dog就将有两个属性,name和age。而如果对dog使用instanceof操作符
console.log(dog instanceof Animal); // true
console.log(dog instanceof Dog); // false
这样就实现了继承,但是有个小问题
console.log(Dog.prototype.constructor === Animal); // true
console.log(Dog.prototype.constructor === Dog); // false
可以看到构造器指向的对象更改了,这样就不符合我们的目的了,我们无法判断我们new出来的实例属于谁。因此,我们可以加一句话:
Dog.prototype.constructor = Dog;
再来看一下:
console.log(dog instanceof Animal); // false
console.log(dog instanceof Dog); // true
done。这种方法是属于原型链的维护中的一环,下文将详细阐述。
2. 方法二
<pre name="code" class="javascript">function Animal(name) {
this.name = name;
}
Animal.prototype.setName = function(name) {
this.name = name;
}
function Dog(age) {
this.age = age;
}
Dog.prototype = Animal.prototype;
这样就实现了prototype的拷贝。
原型链
function Animal(name) {
this.name = name;
}
function Dog(age) {
this.age = age;
}
var animal = new Animal("wangwang");
Dog.prototype = animal;
var dog = new Dog(2);
我们可以看到,子对象的prototype指向父对象的实例,构成了构造器原型链。子实例的内部proto对象也是指向父对象的实例,构成了内部原型链。当我们需要寻找某个属性的时候,代码类似于
function getAttrFromObj(attr, obj) {
if(typeof(obj) === "object") {
var proto = obj;
while(proto) {
if(proto.hasOwnProperty(attr)) {
return proto[attr];
}
proto = proto.__proto__;
}
}
return undefined;
}
在这个例子中,我们如果在dog中查找name属性,它将在dog中的成员列表中寻找,当然,会找不到,因为现在dog的成员列表只有age这一项。接着它会顺着原型链,即.proto指向的实例继续寻找,即animal中,找到了name属性,并将之返回。假如寻找的是一个不存在的属性,在animal中寻找不到时,它会继续顺着.proto寻找,找到了空的对象,找不到之后继续顺着.proto寻找,而空的对象的.proto指向null,寻找退出。
原型链的维护
(new obj()).prototype.constructor === obj;
然后,当我们重写了原型属性之后,子对象产生的实例的constructor不是指向本身!这样就和构造器的初衷背道而驰了。
Dog.prototype = new Animal("wangwang");
Dog.prototype.constructor = Dog;
看起来没有什么问题了。但实际上,这又带来了一个新的问题,因为我们会发现,我们没法回溯原型链了,因为我们没法寻找到父对象,而内部原型链的.proto属性是无法访问的。
- __proto__是可以重写的,这意味着使用它时仍然有风险
- __proto__是spiderMonkey的特殊处理,在别的引擎(例如JScript)中是无法使用的。
function Dog(age) {
this.constructor = arguments.callee;
this.age = age;
}
Dog.prototype = new Animal("wangwang");
这样,所有子对象的实例的constructor都正确的指向该对象,而原型的constructor则指向父对象。虽然这种方法的效率比较低,因为每次构造实例都要重写constructor属性,但毫无疑问这种方法能有效解决之前的矛盾。
function getAttrFromObj(attr, obj) {
if(typeof(obj) === "object") {
do {
var proto = Object.getPrototypeOf(dog);
if(proto[attr]) {
return proto[attr];
}
}
while(proto);
}
return undefined;
}
当然,这种方法只能在支持ES5的浏览器中使用。为了向后兼容,我们还是需要考虑上一种方法的。更合适的方法是将这两种方法整合封装起来,这个相信读者们都非常擅长,这里就不献丑了。
JavaScript的原型继承的更多相关文章
- Javascript的原型继承,说清楚
一直以来对Javascript的原型.原型链.继承等东西都只是会用和了解,但没有深入去理解这门语言关于继承这方面的本质和特点.闲暇之余做的理解和总结,欢迎各位朋友一起讨论. 本文本主要从两段代码的区别 ...
- 谈谈javascript中原型继承
什么是继承?拿来主义:自己没有,别人有,把别人的拿过来使用或者让其成为自己的 如何实现继承的方式 原型继承 混入继承 经典继承 1. 混入继承 由于一个对象可以继承自任意的对象,即:o可以继承自对象o ...
- javaScript的原型继承与多态性
1.prototype 我们可以简单的把prototype看做是一个模版,新创建的自定义对象都是这个模版(prototype)的一个拷贝 (实际上不是拷贝而是链接,只不过这种链接是不可见,给人们的感觉 ...
- 关于JavaScript的原型继承与原型链
在讨论原型继承之前,先回顾一下关于创建自定义类型的方式,这里推荐将构造函数和原型模式组合使用,通过构造函数来定义实例自己的属性,再通过原型来定义公共的方法和属性. 这样一来,每个实例都有自己的实例属性 ...
- Javascript 构造函数原型继承机制
我们先聊聊Js的历史,1994年Netscape公司发布了Navigator浏览器0.9班.这是历史上第一个比较成熟的网络浏览器.轰动一时.但是,这个版本的浏览器只能用来浏览,不具备交互功能,最主要的 ...
- 理解JavaScript中的原型继承(2)
两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...
- javascript原型继承
在传统的基于Class的语言如Java.C++中,继承的本质是扩展一个已有的Class,并生成新的Subclass. 由于这类语言严格区分类和实例,继承实际上是类型的扩展.但是,JavaScript由 ...
- 深入理解JavaScript系列:史上最清晰的JavaScript的原型讲解
一说起JavaScript就要谈的几个问题,原型就是其中的一个.说了句大话,史上最清晰.本来是想按照大纲式的行文写一下,但写到后边感觉其实就一个概念,没有什么条理性,所以下面就简单按照概念解释的模式谈 ...
- 原型模式和基于原型继承的js对象系统
像同样基于原型编程的Io语言一样,javascript在原型继承方面,实现原理和Io非常类似,javascript也遵守这些原则 所有数据都是对象 要得到一个对象,不是通过实例化类,而是找到一个对象作 ...
随机推荐
- ZOJ 3607 Lazier Salesgirl 贪心
这个题比上个题简单得多,也是超过W时间会睡着,睡着就再也不会卖了,顾客按时间顺序来的,但是可能有顾客同时到(同时到如果醒着就全卖了),并且每个人只买一块面包,也是求最大的W,使得卖出面包的平均价格最高 ...
- 基于visual Studio2013解决算法导论之029二叉搜索树
题目 二叉搜索树 解决代码及点评 #include <stdio.h> #include <malloc.h> #include <stdlib.h> ty ...
- DataSource绑定DataTable.Select()显示system.data.DataRow问题解决的方法
有时候我们须要在控件中绑定DataTable中设定条件过滤后的数据,此时,在winForm环境中,一些控件不能正确绑定并显示数据内容.这是由于DataTable.Select()返回的是DataRow ...
- 如何实现android蓝牙开发 自动配对连接,并不弹出提示框
之前做一个android版的蓝牙 与血压计通讯的项目,遇到最大的难题就是自动配对. 上网查资料说是用反射createBond()和setPin(),但测试时进行配对还是会出现提示,但配对是成功了 我就 ...
- 使用gson(一)
1.数组和json的转换 package com.test.gson; import com.google.gson.Gson; public class ArrayToJson { public s ...
- Asp.Net中GridView加入鼠标滑过的高亮效果和单击行颜色改变
转载自:http://www.cnblogs.com/fly_dragon/archive/2010/09/03/1817252.html protected void GridView1_RowDa ...
- BZOJ 4152: [AMPPZ2014]The Captain( 最短路 )
先按x排序, 然后只有相邻节点的边才有用, 我们连起来, 再按y排序做相同操作...然后就dijkstra ---------------------------------------------- ...
- Python 第四篇:生成器、迭代器、装饰器、递归函数与正则表达式
一:生成器:Generator,可以理解为是一种一个函数产生一个迭代器,而迭代器里面的数据是可以通过for循环获取的,那么这个函数就是一个生成器,即生成器是有函数生成的,创建生成器使用()表示,比如g ...
- Linux进程阻塞的相关知识
1.如果驱动程序无法立即满足要求,该如何响应? 当数据不可用时,用户可能调用read:或者进程试图写入数据,但因为输出缓冲区已满,设备还未准备好接受数据.调用进程通常不会关心这类问题,程序员只会简单调 ...
- Android开发10.1:UI组件适配器AdapterView(创建ListView,Adapter接口)
@version:Android4.3 API18 @author:liuxinming 概述 AdapterView继承了ViewGroup,它的本质是容器 ...