关于JavaScript定义类和对象的几种方式


Posted in Javascript onNovember 09, 2010

可以看看这个例子:

var a = 'global'; 
(function () { 
alert(a); 
var a = 'local'; 
})();

大家第一眼看到这个例子觉得输出结果是什么?‘global'?还是‘local'?其实都不是,输出的是undefined,不用迷惑,我的题外话就是为了讲这个东西的。
其实很简单,看一看JavaScript运行机制就会明白。我们可以把这种现象看做“预声明”。但是如果稍微深究一下,会明白得更透彻。

这里其实涉及到对象属性绑定机制。因为所有JavaScript函数都是一个对象。在函数里声明的变量可以看做这个对象的“类似属性”。对象属性的绑定在语言里是有分“早绑定”和“晚绑定”之分的。

【早绑定】
是指在实例化对象之前定义其属性和方法。解析程序时可以提前转换为机器代码。通常的强类型语言如C++,java等,都是早绑定机制的。而JavaScript不是强类型语言。它使用的是“晚绑定”机制。

【晚绑定】
是指在程序运行前,无需检查对象类型,只要检查对象是否支持特性和方法即可。可以在绑定前对对象执行大量操作而不受任何惩罚。
上面代码出现的“预声明”现象,我们大可用“晚绑定”机制来解释。在函数的作用域中,所有变量都是“晚绑定”的。 即声明是顶级的。所以上面的代码和下面的一致:

var a = 'global'; 
(function () { 
var a; 
alert(a); 
a = 'local'; 
})();

在alert(a)之前只对a作了声明而没有赋值。所以结果可想而知。

<!-- 题外话到此结束 -->

RT:本文要说的是,在JavaScript里,我所知道的几种定义类和对象的方式:<! -- 声明:以下内容大部分来自《JavaScript高级程序设计》,只是个人叙述方式不同而已 -- >

【直接量方式】

使用直接量构建对象是最基础的方式,但也有很多弊端。

var Obj = new Object; 
Obj.name = 'sun'; 
Obj.showName = function() { 
alert('this.name'); 
}

我们构建了一个对象Obj,它有一个属性name,一个方法showName。但是如果我们要再构建一个类似的对象呢?难道还要再重复一遍?
NO!,我们可以用一个返回特定类型对象的工厂函数来实现。就像工厂一样,流水线的输出我们要的特定类型结果。

【工厂方式】

function createObj(name) { 
var tempObj = new Object; 
tempObj.name = name; 
tempObj.showName = function () { 
alert(this.name); 
}; 
return tempObj; 
} 
var obj1 = createObj('obj_one'); 
var obj2 = createObj('obj_two');

这种工厂函数很多人是不把他当做构建对象的一种形式的。一部分原因是语义:即它并不像使用了运算符new来构建的那么正规。还有一个更大的原因,是因为这个工厂每次产出一个对象都会创建一个新函数showName(),即每个对象拥有不同的版本,但实际上他们共享的是同一个函数。
有些人把showName在工厂函数外定义,然后通过属性指向该方法,可以避开这个问题:
function showName () { 
alert(this.name); 
} 
function createObj(name) { 
var tempObj = new Object; 
tempObj.name = name; 
tempObj.showName = showName; 
return tempObj; 
} 
var obj1 = createObj('obj_one'); 
var obj2 = createObj('obj_two');

可惜的是,这种方式让showName()这个函数看起来不像对象的一个方法。

【构造函数方式】
这种方式是为了解决上面工厂函数的第一个问题,即没有new运算符的问题。可是第二个问题它依然不能解决。我们来看看。

function Obj(name) { 
this.name = name; 
this.showName = function () { 
alert(this.name); 
} 
} 
var obj1 = new Obj('obj_one'); 
var obj2 = new Obj('obj_two');

它的好处是不用在构造函数内新建一个对象了,因为new运算符执行的时候会自动创建一个对象,并且只有通过this才能访问这个对象。所以我们可以直接通过this来对这个对象进行赋值。而且不用再return,因为this指向默认为构造函数的返回值。
同时,用了new关键字来创建我们想要的对象是不是感觉更“正式”了。
可惜,它仍然不能解决会重复生成方法函数的问题,这个情况和工厂函数一样。

【原型方式】
这种方式对比以上方式,有个很大的优势,就是它解决了方法函数会被生成多次的问题。它利用了对象的prototype属性。我们依赖原型可以重写对象实例。

var Obj = function () {} 
Obj.prototype.name = 'me'; 
Obj.prototype.showName = function () { 
alert(this.name); 
} 
var obj1 = new Obj(); 
var obj2 = new Obj();

我们依赖原型对构造函数进行重写,无论是属性还是方法都是通过原型引用的方式给新建的对象,因此都只会被创建一次。可惜的是,这种方式存在两个致命的问题:
1。没办法在构建对象的时候就写入想要的属性,因为原型在构造函数作用域外边,没办法通过传递参数的方式在对象创建的时候就写入属性值。只能在对象创建完毕后对值进行重写。
2。致命问题在于当属性指向对象时,这个对象会被多个实例所共享。考虑下面的代码:
var Obj = function () {} 
Obj.prototype.name = 'me'; 
Obj.prototype.flag = new Array('A', 'B'); 
Obj.prototype.showName = function () { 
alert(this.name); 
} 
var obj1 = new Obj(); 
var obj2 = new Obj(); 
obj1.flag.push('C'); 
alert(obj1.flag); // A,B,C 
alert(obj2.flag); //A,B,C

是的,当flag属性指向对象时,那么实例obj1和obj2都共享它,哪怕我们仅仅改变了obj1的flag属性,但是它的改变在实例obj2中任然可见。
面对这个问题,让我们不得不想是否应该把【构造函数方式】和【原型方式】结合起来,让他们互补。。。

【构造函数和原型混合方式】
我们让属性用构造函数方式创建,方法用原型方式创建即可:

var Obj = function (name) { 
this.name = name; 
this.flag = new Array('A', 'B'); 
} 
Obj.prototype = { 
showName : function () { 
alert(this.name); 
} 
} 
var obj1 = new Obj(); 
var obj2 = new Obj(); 
obj1.flag.push('C'); 
alert(obj1.flag); // A,B,C 
alert(obj2.flag); //A,B

这种方式有效地结合了原型和构造函数的优势,是目前用的最多,也是副作用最少的方式。
不过,有些追求完美的家伙还不满足,因为在视觉上还没达到他们的要求,因为通过原型来创建方法的过程在视觉上还是会让人觉得它不太像实例的方法(尤其对于传统OOP语言的开发者来说。)
所以,我们可以让原型活动起来,让他也加入到构造函数里面去,好让这个构造函数在视觉上更为统一。而这一系列的过程只需用一个判断即可完成。
var Obj = function (name) { 
this.name = name; 
this.flag = new Array('A', 'B'); 
if (typeof Obj._init == 'undefined') { 
Obj.prototype = { 
showName : function () { 
alert(this.name); 
} 
}; 
Obj._init = true; 
} 
}

如上,用_init作为一个标志来判断是否已经给原型创建了方法。如果是那么就不再执行。这样其实在本质上是没有任何变化的,方法仍是通过原型创建,唯一的区别在于这个构造函数看起来“江山统一”了。
但是这种动态原型的方式是有问题的,《JavaScript高级程序设计》里并没有深究。创建第一个对象的时候会因为prototype在对象实例化之前没来的及建起来,是根本无法访问的。所以第一个对象是无法访问原型方法的。同时这种方式在子类继承中也会有问题。
关于解决方案,我会在下一文中说明。

其实就使用方便来说的话,个人觉得是没必要做这个判断的。。。呵呵 ^_^

Javascript 相关文章推荐
JS遮罩层效果 兼容ie firefox jQuery遮罩层
Jul 26 Javascript
jQuery实现可用于博客的动态滑动菜单
Mar 09 Javascript
JavaScript焦点事件、鼠标事件和滚轮事件使用详解
Jan 15 Javascript
Bootstrap表单组件教程详解
Apr 26 Javascript
浅谈JS继承_借用构造函数 &amp; 组合式继承
Aug 16 Javascript
微信小程序 教程之模块化
Oct 17 Javascript
巧用数组制作图片切换js代码
Nov 29 Javascript
jquery根据name取得select选中的值实例(超简单)
Jan 25 jQuery
微信小程序input框中加入小图标的实现方法
Jun 19 Javascript
JS获取指定月份的天数两种实现方法
Jun 22 Javascript
微信小程序实现笑脸评分功能
Nov 03 Javascript
vue3 源码解读之 time slicing的使用方法
Oct 31 Javascript
JS图片浏览组件PhotoLook的公开属性方法介绍和进阶实例代码
Nov 09 #Javascript
一个javascript图片阅览组件
Nov 09 #Javascript
js中格式化日期时间型数据函数代码
Nov 08 #Javascript
window.location.hash 使用说明
Nov 08 #Javascript
JavaScript游戏之是男人就下100层代码打包
Nov 08 #Javascript
JavaScript游戏之优化篇
Nov 08 #Javascript
javascript开发中因空格引发的错误
Nov 08 #Javascript
You might like
php trim 去除空字符的定义与语法介绍
2010/05/31 PHP
解析百度搜索结果link?url=参数分析 (全)
2012/10/09 PHP
PHP防止表单重复提交的几种常用方法汇总
2014/08/19 PHP
PHP实现的简单mock json脚本分享
2015/02/10 PHP
PHP实现删除多重数组对象属性并重新赋值的方法
2017/06/07 PHP
浅谈PHP实现大流量下抢购方案
2017/12/15 PHP
asp函数split()对应php函数explode()
2019/02/27 PHP
bcastr2.0 通用的图片浏览器
2006/11/22 Javascript
解析Javascript中难以理解的11个问题
2013/12/09 Javascript
js图片轮播效果实现代码
2020/04/18 Javascript
jQuery+css实现的切换图片功能代码
2016/01/27 Javascript
JS触摸屏网页版仿app弹窗型滚动列表选择器/日期选择器
2016/10/30 Javascript
简单的三步vuex入门
2018/05/20 Javascript
使用pkg打包Node.js应用的方法步骤
2018/10/19 Javascript
JS实现的小火箭发射动画效果示例
2018/12/08 Javascript
详解Nodejs get获取远程服务器接口数据
2019/03/26 NodeJs
js实现超级玛丽小游戏
2020/03/18 Javascript
JavaScript事件委托实现原理及优点进行
2020/08/29 Javascript
Javascript执行上下文顺序的深入讲解
2020/11/04 Javascript
如何使用gpu.js改善JavaScript的性能
2020/12/01 Javascript
pandas 使用均值填充缺失值列的小技巧分享
2019/07/04 Python
Python3中configparser模块读写ini文件并解析配置的用法详解
2020/02/18 Python
使用matlab 判断两个矩阵是否相等的实例
2020/05/11 Python
基于python tkinter的点名小程序功能的实例代码
2020/08/22 Python
python读取excel数据绘制简单曲线图的完整步骤记录
2020/10/30 Python
PyTorch中clone()、detach()及相关扩展详解
2020/12/09 Python
css3的@media属性实现页面响应式布局示例代码
2014/02/10 HTML / CSS
html5需遵循的6个设计原则
2016/04/27 HTML / CSS
社区十八大感言
2014/01/19 职场文书
怎样写好创业计划书的内容
2014/02/06 职场文书
解放思想演讲稿
2014/09/11 职场文书
2014入党积极分子批评与自我批评思想报告
2014/10/06 职场文书
党员考试作弊检讨书1000字
2015/02/16 职场文书
单位证明范文
2015/06/18 职场文书
土木工程生产实习心得体会
2016/01/22 职场文书
新手必备之MySQL msi版本下载安装图文详细教程
2021/05/21 MySQL