不用构造函数(Constructor)new关键字也能实现JavaScript的面向对象


Posted in Javascript onJanuary 11, 2013

JavaScript中的对象模型(object model)并不广为人知。我曾写过一篇关于他们的博客。之所以不被人所熟知,原因之一就是JavaScript是这些被人广泛使用的语言中唯一一个通过原型(prototype)来实现继承的。但是,我认为另一个原因就是这种对象模型非常复杂,难于解释。它为什么这么复杂并且又令人困惑呢?那是因为JavaScript试图去隐藏它传统的面向对象的特性——最终导致了它的双重人格(译者注:作者意思是JavaScript既有面向过程的特征,又有面向对象的特征)。

我认为正是由于JavaScript对象模型的难以理解和使用,才出现了一些像CoffeeScript,Dart和TypeScript这些通过编译可以生成JS代码的语言。

JavaScript的前辈们和那些顽固派相信JavaScript有更好的对象模型,并且为其将被大家所遗忘感到惋惜。即使JavaScript的专家Nicholas Zakas也欢迎在ECMAScript 6中加入的新的class语法——只不过是对原型风格(prototypal style)的语法做了一些修饰。换句话说,传统的OOP赢了。

一个大胆的设想
但是,让我们以玩笑的方式做一个设想:我们假想穿越到过去,那时候传统的面向对象的程序设计还没有像现在这样被大家广泛的接受,相反的,基于原型的继承模型得到了大家的广泛接受。这样的话会发生什么?我们最终又会得到些什么样的设计模式呢?

我们再设想:假设JavaScript没有构造函数或者没有new关键字会怎样?事情又会变成什么样的呢?让我们推到以前的重来。:)

首先,第一件事情,在JavaScript中,我们可以使用对象字面量的来创建一个新对象。如下所示:

var felix = { 
name: 'Felix', 
greet: function(){ 
console.log('Hello, I am ' + this.name + '.'); 
} 
};

接下来,假设我们想要将greet函数一般化,将其提取出来,放到一个一般的位置,这样一来,我们就可以创建多个对象来共享同一个greet方法。该怎么实现呢?
我们有好几种选择,先以mixin开始吧。

1、混入(对象扩张)Mixin(Augmentation)
在JavaScript语言中,混入属性非常简单。你只需要将混入对象的属性复制到要混入的对象中去即可。我们将使用一个“augment”函数来实现它,看代码就明白了:

var Dude = { 
greet: function(){ 
console.log('Hello, I am ' + this.name + '.') 
} 
}; 
var felix = { name: 'Felix' }; 
augment(felix, Dude);//将Dude中的属性复制一份到felix中,即混入(mixin)

在上面的代码中,augment函数将Dude对象的属性混入到了felix当中。在很多的JS库中,augment函数被叫做extend。我不喜欢用extend,因为一些语言用extend表示继承,以至于是我很困惑。我更喜欢用“augment”表示,因为实际上这种做法并不是继承,并且语法augment(felix, Dude)已经很清楚的表明你是用Dude中的属性对felix进行了扩充,而不是继承。

也许你早就猜到了augment的代码实现了,没错,非常简单。如下所示:

function augment(obj, properties){ 
for (var key in properties){ 
obj[key] = properties[key]; 
} 
}

2、对象克隆(Cloning)
mixin的一个替代的办法就是先克隆Dude对象,然后再给克隆的对象设置name属性。如下所示:
var Dude = { 
greet: function(){ 
console.log('Hello, I am ' + this.name + '.'); 
} 
} 
var felix = clone(Dude);//克隆Dude对象 
felix.name = 'Felix';

这两种方法之间的唯一不同就是添加属性的顺序。如果你想覆写克隆的对象中的某些方法,你可以考虑使用这种手法。
var felix = clone(Dude); 
felix.name = 'Felix'; 
felix.greet = function(){ 
console.log('Yo dawg!'); 
};//覆写greet方法

如果想要调用父类的方法也很简单——使用apply函数即可,如下所示
felix.greet = function(){ 
Dude.greet.apply(this); 
this.greetingCount++; 
}

这比原型风格的代码要好很多,因为你不必去使用构造函数的.prototype属性——我们不会使用任何构造函数。
以下是clone函数的实现:
function clone(obj){ 
var retval = {};//创建一个空对象 
augment(retval, obj);//复制属性 
return retval; 
}

3、继承(Inheritance)
最后,就是继承了。在我看来,继承被高估了,但是继承在“实例对象”之间共享属性方面确实要比对象扩张有一些优势。让我们编写一个inherit函数,这个函数接收一个对象作为参数,并且返回一个继承自该对象的新对象。
var felix = inherit(Dude); 
felix.name = 'Felix';

使用继承,你可以创建多个继承自同一个对象的子对象,这些子对象可以实时的继承父对象的属性。如下面的代码所示,
var garfield = inherit(Dude);//garfield继承自Dude 
Dude.walk = function(){//给Dude添加新的方法walk 
console.log('Step, step'); 
}; 
garfield.walk(); // prints "Step, step" 
felix.walk(); // also prints "Step, step"

在inherit函数中使用了基于原型对象的继承
function inherit(proto){ 
if (Object.create){ 
// 使用ES5中的Object.create方法 
return Object.create(proto); 
}else if({}.__proto__){ 
//使用非标准属性__proto__ 
var ret = {}; 
ret.__proto__ = proto; 
return ret; 
}else{ 
//如果两种都不支持,使用构造函数继承 
var f = function(){}; 
f.prototype = proto; 
return new f(); 
} 
}

上面的代码看起来不怎么好,那是因为我们使用了特性监测来判断到底使用3种方式中的哪一种。

但是,怎么使用构造方法呢(也就是,初始化方法)?你该怎么在实例对象之间共享初始化代码呢?在一些情况下,如果你只需要为对象设置一些属性,这时候,初始化函数不是必须的,就像我们上面的例子中那样。但是如果你有更多的初始化代码呢,你也许会制定一个约定,例如:使用一个叫initialize的初始化方法。我们假设在Dude中定义了一个叫initialize的方法,如下

var Dude = { 
initialize: function(){ 
this.greetingCount = 0; 
}, 
greet: function(){ 
console.log('Hello, I am ' + this.name + '.'); 
this.greetingCount++; 
} 
}

然后,你可以这样来初始化对象
var felix = clone(Dude); 
felix.name = 'Felix'; 
felix.initialize();或者也可以 
var felix = { name: 'Felix' }; 
felix.name = 'Felix'; 
augment(felix, Dude); 
felix.initialize();还可以 
var felix = inherit(Dude); 
felix.name = 'Felix'; 
felix.initialize();结语

我表示通过上面定义的三个函数——augment,clone和inherit,你可以对JavaScript中的对象做任何你想做的事,而不必使用构造函数和new关键字。我认为这三个函数所体现的语义更简单并且更接近于JavaScript底层的对象系统。(完)^_^
Javascript 相关文章推荐
javascript SocialHistory 检查访问者是否访问过某站点
Aug 02 Javascript
Javascript 定时器调用传递参数的方法
Nov 12 Javascript
jQuery EasyUI API 中文文档 - ProgressBar 进度条
Sep 29 Javascript
javascript+canvas制作九宫格小程序
Dec 28 Javascript
JavaScript实现表格快速变色效果代码
Aug 19 Javascript
基于jQuery实现收缩展开功能
Mar 18 Javascript
详解利用 Express 托管静态文件的方法
Sep 18 Javascript
解决vue单页使用keep-alive页面返回不刷新的问题
Mar 13 Javascript
mint-ui在vue中的使用示例
Apr 05 Javascript
webpack 3.X学习之多页面打包的方法
Sep 04 Javascript
Vue press 支持图片放大功能的实例代码
Nov 09 Javascript
利用Vue的v-for和v-bind实现列表颜色切换
Jul 17 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
js修改table中Td的值(定义td的双击事件)
Jan 10 #Javascript
You might like
分页显示Oracle数据库记录的类之二
2006/10/09 PHP
回答PHPCHINA上的几个问题:URL映射
2007/02/14 PHP
用windows下编译过的eAccelerator for PHP 5.1.6实现php加速的使用方法
2007/09/30 PHP
解读PHP的Yii框架中请求与响应的处理流程
2016/03/17 PHP
php删除txt文件指定行及按行读取txt文档数据的方法
2017/01/30 PHP
json跟xml的对比分析
2008/06/10 Javascript
javascript 兼容FF的onmouseenter和onmouseleave的代码
2008/07/19 Javascript
Javascript学习笔记1 数据类型
2010/01/11 Javascript
JavaScript DOM 学习第二章 编辑文本
2010/02/19 Javascript
重载toString实现JS HashMap分析
2011/03/13 Javascript
javaScript面向对象继承方法经典实现
2013/08/20 Javascript
window.onresize 多次触发的解决方法
2013/11/08 Javascript
jquery将一个表单序列化为一个对象的方法
2014/01/03 Javascript
JavaScript学习笔记之内置对象
2015/01/22 Javascript
jQuery实现复选框批量选择与反选的方法
2015/06/17 Javascript
jQuery幻灯片特效代码分享--鼠标滑过按钮时切换(2)
2020/11/18 Javascript
轻松实现jquery选项卡切换效果
2016/10/10 Javascript
bootstrap+jquery项目引入文件报错的解决方法
2018/01/22 jQuery
layui 对弹窗 form表单赋值的实现方法
2019/09/04 Javascript
vue中jsonp插件的使用方法示例
2020/09/10 Javascript
Pycharm简单使用教程(入门小结)
2019/07/04 Python
Python二维码生成识别实例详解
2019/07/16 Python
pycharm中如何自定义设置通过“ctrl+滚轮”进行放大和缩小实现方法
2020/09/16 Python
html5 canvas绘制矩形和圆形的实例代码
2016/06/16 HTML / CSS
艺龙旅行网酒店预订:国内、港澳台酒店
2018/06/26 全球购物
数百万免费的图形资源:Freepik
2020/09/21 全球购物
介绍一下Java中标识符的命名规则
2014/02/03 面试题
程序员跳槽必看面试题总结
2013/06/28 面试题
教师找工作推荐信
2013/11/23 职场文书
校园网站的创业计划书范文
2013/12/30 职场文书
市政管理求职信范文
2014/05/07 职场文书
年度安全生产目标责任书
2014/07/23 职场文书
2016年暑期见闻作文
2015/11/25 职场文书
2016年基层党支部书记公开承诺书
2016/03/25 职场文书
62句有关感恩节文案(推荐收藏)
2019/11/28 职场文书
Moment的feature导致线上bug解决分析
2022/09/23 Javascript