关于JavaScript的面向对象和继承有利新手学习


Posted in Javascript onJanuary 11, 2013

这是一篇关于JavaScript的面向对象和继承的文章,写于1年前,作者循序渐进,对想学习JavaScript中面向对象的同学来说是很有帮助的,因此试着翻译一下,不妥之处,请指正。原文链接Objects and Inheritance in Javascript

虽然一些Javascript用户可能永远也不需要知道原型或面向对象语言的性质,但是那些来自传统面向对象的语言的开发者使用的时候会发现JavaScript的继承模型非常的奇怪。而不同的JS框架提供了各自的方法来编写类面向对象(class-like)的代码,这使得JS的面向对象更加的难以理解。这样带来的结果是:
1、没有一个标准的方法来实现面向对象。
2、JS面向对象的底层概念并没有得到人们的熟知

原型继承
原型继承是一个非常简单的概念,它的本质是:
1、对象a继承自对象b,就说b是a的原型(prototype)。
2、a继承了b的所有的属性,即如果b.x的值为1,那么a.x的值为1
3、a自身的属性会重写b中同名的属性

让我们在具体的代码中看看效果,假设有一个John Smith和一个继承自他的Jane。

var john = {firstName: 'John', lastName: 'Smith'}; 
var jane = {firstName: 'Jane'}; 
jane.__proto__ = john;

现在,我们称john是jane的原型(prototype),jane继承了john的所有的属性
jane.lastName 
"Smith"//该属性继承自john

jane自身的属性具有较高的优先级,如下
jane.firstName;//该属性覆写了john中的firstName属性 
"Jane"

如果在这之后给john添加一个属性,jane也会动态的继承该属性,如下所示
john.hair = 'brown'; //给john添加一个新属性 
jane.hair; 
"brown"//结果表明jane继承了新添加的属性

现在,我们假设jane结婚了,因此有了一个新的姓(last name)
jane.lastName = 'Doe'

该属性覆盖了john当中的同名的属性(lastName)
jane.lastName 
"Doe"

但是,如果我们现在删除jane的lastName
delete jane.lastName 
该属性的值就会恢复为john的值 
[code] 
jane.lastName 
"Smith"

现在,jane也可以继承自其它的对象。在这个链中可以有任意多个继承,我们将它称为原型链(prototype chain),实际上,john也有一个prototype属性
john.__proto__; 
Object { }

在Firebug的控制台中,john.__proto__的值设为了Object{},但是Object{}代表着Object.prototype这个对象——所有对象的父类。

这就是对原型继承的一个简要描述。看起来还不错,是吧?
但是,实际上,我们是不能用__proto__的。。。

告诉大家一个不好的消息......
IE不支持__proto__属性,实际上,__proto__并不是ECMAScript规范中的属性,而且,Mozilla也打算在火狐浏览器以后的的版本中去掉该属性。

但是,这并不意味着__proto__属性不存在。虽然在某些浏览器中无法直接访问到__proto__属性,但是它还是以某种形式存在,我们还得和他打交道,只不过没有那么直接了。

类和继承
因此,我们可以说,JavaScript没有类
请记住:JavaScript中没有类
那既然这样,方法和继承有事怎么实现的呢?
通过原型(prototype)。在传统的面向对象的语言中,方法是依赖于类的,而在JavaScript当中,方法是依赖于对象的原型的,并且,原型是和对象的构造器(constructor)绑定在一起的。

在JavaScript当中,函数起到了构造器(constructor)的作用。通过使用new运算符,你可以把一个函数当做构造函数(constructor)来用。下面的代码展示了我们创建了一个Cat函数:

function Cat(name){ // <-这是一个常规的函数 
this.name = name // this指向新建的对象 
}

以上代码会自动创建一个Cat.prototype对象
Cat.prototype 
Cat { }

我们可用new操作符来创建Cat的一个实例
var garfield = new Cat('Garfield') // 创建一个实例 - Cat函数充当了构造函数

现在,Cat.prototype对象成为了所有的通过new Cat()创建的对象的原型,例如:
garfield.__proto__ === Cat.prototype 
true //看到了吗? `Cat.prototype` 现在是garfield对象的原型

现在,我们给Cat.prototype添加一个方法,添加之后,该方法可以被garfield访问到
Cat.prototype.greet = function(){ 
console.log('Meow, I am ' + this.name) 
} 
garfield.greet() 
"Meow, I am Garfield"

其他的Cat的实例也可以访问到
var felix = new Cat('Felix') 
felix.greet() 
"Meow, I am Felix"

因此,在JavaScript中,方法是依赖于对象的原型(prototype)而存在的。

实际上,我们也可以为garfield添加方法,它会覆写Cat.prototype中的同名方法,如下所示:

garfield.greet = function(){ 
console.log("What's new?"); 
}; 
garfield.greet(); 
"What's new?"

但这并不会影响到其他的对象
felix.greet(); 
"Meow, I am Felix"

因此,JavaScript中,方法可以直接和一个对象关联,可以和对象的原型关联,也可以和对象的任意一个父辈对象关联,即,可以和原型链(prototype chain)的任意一环关联。继承也就是这样实现的。

要创建一个二级原型链(prototype chain),我们首先需要创建另一个构造函数(constructor),叫Animal如何?

function Animal(){ 
}

接下来,我们需要将Cat.prototype的原型指向一个Animal的对象,这样一来,Cat的实例就会继承所有的Animal的方法。因此,我们将Cat.prototype的值设置为Animal的实例,如下所示:
Cat.prototype = new Animal();

除此之外,我们还要告诉新的Cat.prototype,它实际上是Cat的一个实例:
Cat.prototype.constructor = Cat // 让`Cat.prototype` 知道它是Cat的一个实例

虽然这样做的目的主要是为了类之间的层次关系,但通常还是有必要这样做的。

现在,既然继承自Animal.prototype和Cat.prototype的对象是属于动物类,那么所有Cat的实例也间接的继承自Animal.prototype。如果我们给Animal.prototype添加一个新方法,那么,所有的Cat的实例也能够访问到该方法。

Animal.prototype.breed = function(){ 
console.log('Making a new animal!'); 
return new this.constructor(); 
}; 
var kitty = garfield.breed(); 
Making a new animal!

通过上面的代码我们就实现了继承,简单吧。

结语
虽然JavaScript中基于原型的继承很怪并且需要花一些时间才能习惯,但是他的核心思想是很简单的。只要你真正理解了这些本质上的概念,你就会有信心在这些良莠不齐的代码中驾驭JavaScript的OO。(完)^_^

Javascript 相关文章推荐
利用Angular.js限制textarea输入的字数
Oct 20 Javascript
深入理解vue-loader如何使用
Jun 06 Javascript
vue2.0项目中使用Ueditor富文本编辑器示例代码
Aug 14 Javascript
利用Angular2 + Ionic3开发IOS应用实例教程
Jan 15 Javascript
11行JS代码制作二维码生成功能
Mar 09 Javascript
Node.js中,在cmd界面,进入退出Node.js运行环境的方法
May 12 Javascript
基于vue-upload-component封装一个图片上传组件的示例
Oct 16 Javascript
jQuery简单实现根据日期计算星期几的方法
Jan 09 jQuery
详解如何搭建mpvue框架搭配vant组件库的小程序项目
May 16 Javascript
node.js 微信开发之定时获取access_token
Feb 07 Javascript
vue计算属性+vue中class与style绑定(推荐)
Mar 30 Javascript
vuex实现购物车的增加减少移除
Jun 28 Javascript
不用构造函数(Constructor)new关键字也能实现JavaScript的面向对象
Jan 11 #Javascript
javascript使用中为什么10..toString()正常而10.toString()出错呢
Jan 11 #Javascript
javascript将数组插入到另一个数组中的代码
Jan 10 #Javascript
jquery实现点击TreeView文本父节点展开/折叠子节点
Jan 10 #Javascript
javascript 中String.match()与RegExp.exec()的区别说明
Jan 10 #Javascript
防止文件缓存的js代码
Jan 10 #Javascript
js修改table中Td的值(定义td的单击事件)
Jan 10 #Javascript
You might like
用来解析.htgroup文件的PHP类
2012/09/05 PHP
PHP5.3的垃圾回收机制(动态存储分配方案)深入理解
2012/12/10 PHP
php 判断服务器操作系统的类型
2014/02/17 PHP
PHP类相关知识点实例总结
2016/09/28 PHP
分享8个Laravel模型时间戳使用技巧小结
2020/02/12 PHP
CCPry JS类库 代码
2009/10/30 Javascript
jQuery Select(单选) 模拟插件 V1.3.62 改进版
2010/07/17 Javascript
jQuery ajax(复习)—Baidu ajax request分离版
2013/01/24 Javascript
JavaScript?Apple设备检测示例代码
2013/11/15 Javascript
javascript调试过程中找不到哪里出错的可能原因
2013/12/16 Javascript
JQuery文本改变触发事件如聚焦事件、失焦事件
2014/01/15 Javascript
jquery操作对象数组元素方法详解
2014/11/26 Javascript
Javascript基础教程之数组 array
2015/01/18 Javascript
jQuery禁用快捷键例如禁用F5刷新 禁用右键菜单等的简单实现
2016/08/31 Javascript
JS控件bootstrap datepicker使用方法详解
2017/03/25 Javascript
Vue.js tab实现选项卡切换
2017/05/16 Javascript
关于HTTP传输中gzip压缩的秘密探索分析
2018/01/12 Javascript
react 实现页面代码分割、按需加载的方法
2018/04/03 Javascript
解决Vue中mounted钩子函数获取节点高度出错问题
2018/05/18 Javascript
JavaScript引用类型Function实例详解
2018/08/09 Javascript
在vue中使用v-bind:class的选项卡方法
2018/09/27 Javascript
[56:41]iG vs Winstrike 2018国际邀请赛小组赛BO2 第二场
2018/08/17 DOTA
Python获取远程文件大小的函数代码分享
2014/05/13 Python
Python中的fileinput模块的简单实用示例
2015/07/09 Python
Python 正则表达式的高级用法
2016/12/04 Python
python遍历一个目录,输出所有的文件名的实例
2018/04/23 Python
python 通过视频url获取视频的宽高方式
2019/12/10 Python
python如何变换环境
2020/07/21 Python
python 基于pygame实现俄罗斯方块
2021/03/02 Python
公司领导推荐信
2013/11/12 职场文书
无工作经验者个人求职信范文
2013/12/22 职场文书
单位在职证明书
2014/09/11 职场文书
警示教育片观后感
2015/06/17 职场文书
2015上半年个人工作总结
2015/07/27 职场文书
MySQL Server层四个日志的实现
2022/03/31 MySQL
使用CSS实现六边形的图片效果
2022/08/05 HTML / CSS