JavaScript中的原型继承基础学习教程


Posted in Javascript onMay 06, 2016

大多数编程语言中,都有类和对象,一个类可以继承其他类。
在JavaScript中,继承是基于原型的(prototype-based),这意味着JavaScript中没有类,取而代之的是一个对象继承另一个对象。:)

1. 继承, the proto
在JavaScript中,当一个对象rabbit继承另一了对象animal时,这意味着rabbit对象中将会有一个特殊的属性:rabbit.__proto__ = animal;
当访问rabbit对象时,如果解释器在rabbit中不能找到属性,那么它会顺着__proto__链往上在animal对象中寻找
栗子中的__proto__属性仅在Chrome和FireFox中可以访问,请看一个栗子:

var animal = { eats: true }
var rabbit = { jumps: true }

rabbit.__proto__ = animal // inherit

alert(rabbit.eats) // true

eats属性是从animal对象中访问的。
如果在rabbit对象中已经发现了属性,那么就不会去检查proto属性啦。
再来一个栗子,当子类中也有eats属性时,父类中的就不会访问了。

var animal = { eats: true }
var fedUpRabbit = { eats: false}

fedUpRabbit.__proto__ = animal 

alert(fedUpRabbit.eats) // false

你也可以在animal中添加一个函数,那么在rabbit中也可以访问了。

var animal = {
 eat: function() {
  alert( "I'm full" )
  this.full = true
 }
}


var rabbit = {
 jump: function() { /* something */ }
}

rabbit.__proto__ = animal

(1)rabbit.eat():
rabbit.eat()函数以如下两步执行:
首先,解释器查找rabbit.eat,rabbit中没有eat函数,那么它就顺着rabbit.__proto__往上找,在animal中找到了。
函数以this = rabbit运行。this值与__proto__属性完全无关。
因此,this.full = true在rabbit中:
看看这里我们有什么新发现,一个对象调用了父类函数,但是this还是指向对象本身,这就是继承。
被__proto__引用的对象称作是原型(prototype),animal是rabbit的原型(译者注:这就是rabbit的__proto__属性引用了animal 的prototype属性)
(2)读时查找,不是写时
当读取一个对象时,比如this.prop,解释器会在它的原型中查找属性。
当设置一个属性值时,比如this.prop = value,那么就没有理由去查找了,这个属性(prop)会被直接添加到这个对象中(这里是this)。delete obj.prop也类似,它只删除对象本身的属性,原型中的属性保持原封不动。
(3)关于proto
如果你在阅读指南,这里我们叫的__proto__,在指南中表示为[[Prototype]]。双方括号是很重要的,因为有另一个属性叫做prototype。

2. Object.create, Object.getPrototypeOf
__proto__是一个非标准的属性,由Chrome/FireFox提供访问,在其他的浏览器中保持不可见。
所有的现代浏览器除了Opera(IE > 9)支持两个标准的函数来处理原型问题:

Object.ceate(prop[,props])

用给定了proto创建一个空对象:

var animal = { eats: true }

rabbit = Object.create(animal)

alert(rabbit.eats) // true

上面代码创建了一个空rabbit对象,并且原型设置为animal
rabbit对象创建好以后,我们可以往里添加属性了:

var animal = { eats: true }

rabbit = Object.create(animal)
rabbit.jumps = true

Object.creat函数的第二个参数props是可选的,它允许像新对象设置属性。这里就省略了,因为我们关系的继承。
(1)Object.getPrototypeOf(obj)
返回obj.__proto__的值。这个函数是标准的,可以在不能直接访问__proto__属性的浏览器中使用了。

var animal = {
 eats: true
}

rabbit = Object.create(animal)

alert( Object.getPrototypeOf(rabbit) === animal ) // true

现代浏览器允许读取__proto__属性值,但是不能设置。

3. The prototype
有一些好的跨浏览器的方式设置__proto__属性,这将会使用构造器函数(constructor functions)。记住!任何函数创建一个对象都是通过new关键字的。
一个栗子:

function Rabbit(name) {
 this.name = name
}

var rabbit = new Rabbit('John')

alert(rabbit.name) // John

new操作将原型的属性设置到rabbit对象的的__proto__属性中了。
让我们来看看它的原理,例如,new Rabbit 对象,而Rabbit是继承animal 的。

var animal = { eats: true }

function Rabbit(name) {
 this.name = name
}

Rabbit.prototype = animal

var rabbit = new Rabbit('John')

alert( rabbit.eats ) // true, because rabbit.__proto__ == animal

Rabbit.prototype = animal 字面量意味着:对所有由new Rabbit创建的对象设__proto__ = animal

4. 跨浏览器 Object.create(proto)
Object.create(prop)函数功能的强大的,因为它允许从给定的对象直接继承。它可以由如下代码模拟:

function inherit(proto) {
 function F() {}
 F.prototype = proto
 return new F
}

inherit(animal) 与Object.create(animal)是完全等同的,返回一个空的对象,并且object.__proto__ = animal。
一个栗子:

var animal = { eats: true }

var rabbit = inherit(animal)

alert(rabbit.eats) // true
alert(rabbit.hasOwnProperty('eats')) // false, from prototype

来看一下它的原理是什么:

function inherit(proto) {
 function F() {}   // (1)
 F.prototype = proto // (2)
 return new F()   // (3)
}

(1) 创建了一个新函数,函数没有向this设置任何属性,以此`new F` 会创建一个空对象。
(2) `F.prototype`被设置为proto
(3) `new` F创建了一个空对象,对象的`__proto__ = F.prototype`
(4) Bingo! 我们得到了一个继承`proto`的空对象
这个函数广泛适用于各种库和框架之中。
你的函数接受了一个带有options 的对象

/* options contains menu settings: width, height etc */
function Menu(options) {
 // ...
}
你想设置某些options
function Menu(options) {
 options.width = options.width || 300 // set default value
 // ...
}

。。。但是改变参数值可能会产生一些错误的结果,因为options可能会在外部代码中使用。一个解决办法就是克隆options对象,复制所有的属性到一个新的对象中,在新对象中修改,
怎样用继承来解决这个问题呢? P.S. options可以添加设设置,但是不能被删除。
Solution
你可以继承options,并且在它的子类的中修改或者添加新的属性。

function inherit(proto) {
 function F() {}
 F.prototype = proto
 return new F
}

function Menu(options) {
 var opts = inherit(options)
 opts.width = opts.width || 300
 // ...
}

所有的操作只在子对象中有效,当Menu方法结束时,外部代码仍然可以使用没有修改的过的options对象。delete操作在这里非常重要,如果width是一个prototype中的属性,delete opts.width不会产生任何作用

5. hasOwnProperty
所有的对象都有hasOwnProperty函数,它可以用来检测一个属性是否对象自身还是属于原型
一个栗子:

function Rabbit(name) {
 this.name = name
}

Rabbit.prototype = { eats: true }

var rabbit = new Rabbit('John')

alert( rabbit.hasOwnProperty('eats') ) // false, in prototype

alert( rabbit.hasOwnProperty('name') ) // true, in object

6. Looping with/without inherited properties
for..in循环输出一个对象的所有属性,包括自身的和原型的。

function Rabbit(name) {
 this.name = name
}

Rabbit.prototype = { eats: true }

var rabbit = new Rabbit('John')

for(var p in rabbit) {
 alert (p + " = " + rabbit[p]) // outputs both "name" and "eats"
}

用hasOwnProperty可以过滤得到属于对象自己的属性:

function Rabbit(name) {
 this.name = name
}

Rabbit.prototype = { eats: true }

var rabbit = new Rabbit('John')

for(var p in rabbit) {
 if (!rabbit.hasOwnProperty(p)) continue // filter out "eats"
 alert (p + " = " + rabbit[p]) // outputs only "name"
}

7. Summary
JavaScript是通过一个特殊的属性proto来实现继承的
当访问一个对象的属性时,如果解释器不能在对象中找到,它就会去对象的原型中继续寻找 对函数属性来说,this指向这个对象,而不是它的原型。
赋值obj.prop = value, 删除delete obj.prop
管理proto:
Chrome和FireFox可以直接访问对象的__proto__属性,大多数现代浏览器支持用Object.getPrototypeOf(obj)只读访问。
Object.create(proto) 可以用给定的proto生成空的子对象,或者通过如下代码达到相同的功能:

function inherit(proto) {
   function F() {}   
   F.prototype = proto
   return new F()  
  }

其他方法:
for..in循环输出一个对象的所有属性(包括自身的和原型的)和对象的原型链。
如果一个属性prop属于对象obj那么obj.hasOwnProperty(prop)返回true,否则返回false。

Javascript 相关文章推荐
用 javascript 实现的点击复制代码
Mar 24 Javascript
IE和Firefox下event事件杂谈
Dec 18 Javascript
使用jQuery轻松实现Ajax的实例代码
Aug 16 Javascript
JQuery 实现在同一页面锚点链接之间的平滑滚动
Oct 29 Javascript
JS设置网页图片vspace和hspace属性的方法
Apr 01 Javascript
JS获得图片alt信息的方法
Apr 01 Javascript
js跨域请求的5中解决方式
Jul 02 Javascript
JS获取checkbox的个数简单实例
Aug 19 Javascript
JavaScript仿微信打飞机游戏
Jul 05 Javascript
Easyui使用Dialog行内按钮布局的实例
Jul 27 Javascript
微信小程序实现发送模板消息功能示例【通过openid推送消息给用户】
May 05 Javascript
带你使用webpack快速构建web项目的方法
Nov 12 Javascript
jquery实现下拉框功能效果【实例代码】
May 06 #Javascript
浅析在javascript中创建对象的各种模式
May 06 #Javascript
jquery自定义插件——window的实现【示例代码】
May 06 #Javascript
javascript创建对象的几种模式介绍
May 06 #Javascript
前端性能优化及技巧
May 06 #Javascript
jquery自定义插件开发之window的实现过程
May 06 #Javascript
快速使用Bootstrap搭建传送带
May 06 #Javascript
You might like
PHP 开发工具
2006/12/06 PHP
php实例分享之二维数组排序
2014/05/15 PHP
php的GD库imagettftext函数解决中文乱码问题
2015/01/24 PHP
thinkphp5 框架结合plupload实现图片批量上传功能示例
2020/04/04 PHP
JavaScript 组件之旅(三):用 Ant 构建组件
2009/10/28 Javascript
JS获取select的value和text值的简单实例
2014/02/26 Javascript
用瀑布流的方式在网页上插入图片的简单实现方法
2016/09/23 Javascript
基于JS实现checkbox全选功能实例代码
2016/10/31 Javascript
微信小程序中input标签详解及简单实例
2017/05/18 Javascript
Vue.js实现微信过渡动画左右切换效果
2017/06/13 Javascript
详细介绍RxJS在Angular中的应用
2017/09/23 Javascript
详解ionic本地相册、拍照、裁剪、上传(单图完全版)
2017/10/10 Javascript
nodejs更改项目端口号的方法
2018/05/13 NodeJs
详解关于vue-area-linkage走过的坑
2018/06/27 Javascript
RequireJS用法简单示例
2018/08/20 Javascript
elementUI 设置input的只读或禁用的方法
2018/10/30 Javascript
微信小程序实现点赞、取消点赞功能
2018/11/02 Javascript
JS求1到任意数之间的所有质数的方法详解
2019/05/20 Javascript
详解vue父子组件关于模态框状态的绑定方案
2019/06/05 Javascript
vue中音频wavesurfer.js的使用方法
2020/02/20 Vue.js
VSCode插件安装完成后的配置(常用配置)
2020/08/24 Javascript
Python2.x中str与unicode相关问题的解决方法
2015/03/30 Python
python删除列表中重复记录的方法
2015/04/28 Python
python简单图片操作:打开\显示\保存图像方法介绍
2017/11/23 Python
Django admin实现图书管理系统菜鸟级教程完整实例
2017/12/12 Python
Python3.5 Pandas模块缺失值处理和层次索引实例详解
2019/04/23 Python
使用Python检测文章抄袭及去重算法原理解析
2019/06/14 Python
opencv实现简单人脸识别
2021/02/19 Python
python 如何将数据写入本地txt文本文件的实现方法
2019/09/11 Python
彻底解决pip下载pytorch慢的问题方法
2021/03/01 Python
XD健身器材:Kevlar球、Crossfit健身球
2019/03/26 全球购物
教师作风建设剖析材料
2014/10/11 职场文书
2014年设备管理工作总结
2014/11/26 职场文书
电影焦裕禄观后感
2015/06/09 职场文书
游戏开发中如何使用CocosCreator进行音效处理
2021/04/14 Javascript
python中取整数的几种方法
2021/11/07 Python