日期:2014-05-16  浏览次数:20419 次

关于javascript定义类与对象

在说这个话题之前,我想先说几句题外话:最近偶然碰到有朋友问我“hoisting”的问题。即在js里所有变量的声明都是置顶的,而赋值则是在之后发生的。可以看看这个例子:

?

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指向默认为构造函数的返回值。