关于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 相关文章推荐
JQUERY 对象与DOM对象之两者相互间的转换
Apr 27 Javascript
JavaScript和JQuery实用代码片段(一)
Apr 07 Javascript
JS自定义功能函数实现动态添加网址参数修改网址参数值
Aug 02 Javascript
Jquery中给animation加更多的运作效果实例
Sep 05 Javascript
表格奇偶行设置不同颜色的核心JS代码
Dec 24 Javascript
通过Javascript读取本地Excel文件内容的代码示例
Apr 08 Javascript
jquery中绑定事件的异同
Feb 28 Javascript
详解基于vue-cli优化的webpack配置
Nov 06 Javascript
ES6中的迭代器、Generator函数及Generator函数的异步操作方法
May 12 Javascript
详解vue 动态加载并注册组件且通过 render动态创建该组件
May 30 Javascript
JS Web Flex弹性盒子模型代码实例
Mar 10 Javascript
vue+canvas实现拼图小游戏
Sep 18 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
php下获取Discuz论坛登录用户名、用户组、用户ID等信息的实现代码
2010/12/29 PHP
PHP根据传入参数合并多个JS和CSS文件的简单实现
2014/06/13 PHP
YII模块实现绑定二级域名的方法
2014/07/09 PHP
PHP使用pcntl_fork实现多进程下载图片的方法
2014/12/16 PHP
PHP测试成功的邮件发送案例
2015/10/26 PHP
PHP魔术方法之__call与__callStatic使用方法
2017/07/23 PHP
Yii框架getter与setter方法功能与用法分析
2019/10/22 PHP
jquery选择器(常用选择器说明)
2010/09/28 Javascript
js动态生成指定行数的表格
2013/07/11 Javascript
js鼠标及对象坐标控制属性详细解析
2013/12/14 Javascript
改变隐藏的input中value的值代码
2013/12/30 Javascript
js闭包的用途详解
2014/11/09 Javascript
javascript实现的猜数小游戏完整实例代码
2016/05/10 Javascript
jQuery之动画效果大全
2016/11/09 Javascript
JQuery实现列表中复选框全选反选功能封装(推荐)
2016/11/24 Javascript
jQuery通过改变input的type属性实现密码显示隐藏切换功能
2017/02/08 Javascript
JavaScript基于对象方法实现数组去重及排序操作示例
2018/07/10 Javascript
微信小程序实现多选框全选与取消全选功能示例
2019/05/14 Javascript
JavaScript This指向问题详解
2019/11/25 Javascript
js实现简单的倒计时
2021/01/28 Javascript
[44:10]2018DOTA2亚洲邀请赛 4.5 淘汰赛 EG vs VP 第一场
2018/04/06 DOTA
Python用zip函数同时遍历多个迭代器示例详解
2016/11/14 Python
Python简单读写Xls格式文档的方法示例
2018/08/17 Python
解决Pycharm 包已经下载,但是运行代码提示找不到模块的问题
2019/08/31 Python
Python实现的北京积分落户数据分析示例
2020/03/27 Python
Python如何操作docker redis过程解析
2020/08/10 Python
Python爬虫教程之利用正则表达式匹配网页内容
2020/12/08 Python
瑞典时尚服装购物网站:Miinto.se
2017/10/30 全球购物
英国排名第一的停车场运营商:NCP
2019/08/26 全球购物
小区门卫管理制度
2014/01/29 职场文书
文明之星事迹材料
2014/05/09 职场文书
部队反四风对照检查材料
2014/09/26 职场文书
学校党委干部个人对照检查材料思想汇报
2014/10/09 职场文书
企业财务总监岗位职责
2015/04/03 职场文书
2015年财务部年度工作总结
2015/05/19 职场文书
微信小程序实现轮播图指示器
2022/06/25 Javascript