关于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 相关文章推荐
javascript document.images实例
May 27 Javascript
让图片旋转任意角度及JQuery插件使用介绍
Mar 20 Javascript
ExtJS DOM元素操作经验分享
Aug 28 Javascript
原生js操作checkbox用document.getElementById实现
Oct 12 Javascript
JavaScript中使用arguments获得函数传参个数实例
Aug 27 Javascript
JS限制文本框只能输入数字和字母方法
Feb 28 Javascript
7个jQuery最佳实践
Jan 12 Javascript
Bootstrap popover用法详解
Dec 22 Javascript
Vue.js 2.0窥探之Virtual DOM到底是什么?
Feb 10 Javascript
js读取json文件片段中的数据实例
Mar 09 Javascript
微信小程序表单验证功能完整实例
Dec 01 Javascript
javascript自定义加载loading效果
Sep 15 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
为php4加入动态flash文件的生成的支持
2006/10/09 PHP
PHP 开发工具
2006/12/06 PHP
使用phpQuery采集网页的方法
2013/11/13 PHP
PHP闭包实例解析
2014/09/08 PHP
Smarty日期时间操作方法示例
2016/11/15 PHP
用JAVASCRIPT如何给&amp;lt;textarea&amp;gt;&amp;lt;/textarea&amp;gt;赋值
2007/04/20 Javascript
Javascript 获取LI里的内容
2008/12/17 Javascript
JavaScript去掉空格的方法集合
2010/12/28 Javascript
JavaScript实现搜索框的自动完成功能(一)
2016/02/25 Javascript
jQuery鼠标悬停内容动画切换效果
2017/04/27 jQuery
jquery 禁止鼠标右键并监听右键事件
2017/04/27 jQuery
使用Angular Cli如何创建Angular私有库详解
2019/01/30 Javascript
微信小程序 (地址选择1)--选取搜索地点并显示效果
2019/12/17 Javascript
vue element-ui实现input输入框金额数字添加千分位
2019/12/29 Javascript
vue中activated的用法
2021/01/03 Vue.js
[55:16]Mski vs VGJ.S Supermajor小组赛C组 BO3 第二场 6.3
2018/06/04 DOTA
Python写的服务监控程序实例
2015/01/31 Python
利用QT写一个极简单的图形化Python闹钟程序
2015/04/07 Python
使用Python对MySQL数据操作
2017/04/06 Python
python 显示数组全部元素的方法
2018/04/19 Python
python3+django2开发一个简单的人员管理系统过程详解
2019/07/23 Python
Flask框架学习笔记之使用Flask实现表单开发详解
2019/08/12 Python
python 类的继承 实例方法.静态方法.类方法的代码解析
2019/08/23 Python
通过实例学习Python Excel操作
2020/01/06 Python
CSS3实现图片抽屉式效果的示例代码
2019/11/06 HTML / CSS
优瑞自动咖啡机官网:Jura
2018/09/29 全球购物
Otticanet美国:最顶尖的世界名牌眼镜, 能得到打折季的价格
2019/03/10 全球购物
工程测量与监理专业应届生求职信
2013/11/27 职场文书
六一儿童节活动策划方案
2014/01/27 职场文书
大学中国梦演讲稿
2014/04/23 职场文书
节电标语大全
2014/06/23 职场文书
银行竞聘上岗演讲稿
2014/09/12 职场文书
家庭困难证明
2014/10/12 职场文书
幼儿园开学报名通知
2015/07/16 职场文书
深入理解python多线程编程
2021/04/18 Python
【海涛dota解说】DCG联赛第一周 LGD VS DH
2022/04/01 DOTA