那么,怎么用 JavaScript 合理并严格地依照标准对象构建顺序创建对象呢?1 确定继承关系。因为 JavaScript 没有变量类型绑定,所以应该能够允许多继承。其中第一个父类为主父类。Super._super = [ ];
Common._super = [ Super ];
Special._super = [ Common ];
2 加载类结构
这里涉及到一个技术,熟悉 JavaScript 面向对象编程的人都会很了解,称为原型链(prototype chaining)。JavaScript 面向对象的类结构,其实是靠每一个类的原型对象来保存的。每生成一个对象,都是根据原型对象,制作一个与之具有同样成员,成员值也都相同的新对象。如果直接让一个类的原型对象等于一个其父类的对象,就相当于复制了父类原型的所有成员,放入了当前类的原型中。这些成员除了显示可见的意外,还包括用于 instanceof 的类名属性等一些系统成员变量,从而能够实现子类成员 instanceof 父类类名,得到 true 的结果——这种认定用的属性可以传递给更下一层的子类。
  除了由原型链形成的主继承之外,因为 JavaScript 没有引用类型的概念,也就没有接口的概念。所以需要手动加载除主父类以外的其余父类成员。通过 for (var xxx in xxx) 的方式,可以取出其余父类原型中的成员,并用其建立当前类的类结构。这里需要注意,如果各种父类中有名称完全相同的方法,则会以最后加载进来的那个为子类的方法。这不得不说是 JavaScript 面向对象多重继承的一个遗憾。Super._loadclass = function()
{
if (Super._initialized) return;
// 继承父类的类结构。因为没有父类,所以继承父类类结构的语句可以去掉
// 创建自身独有类结构
Super.prototype.field01 = null;
Super.prototype.field02 = null;
Super.prototype.method01 = function()
{
// 方法体,其中可以使用 this 关键字
};
Super.prototype.method02 = function()
{
// 方法体,其中可以使用 this 关键字
};
Super._initialized = true;
};
Common._loadclass = function()
{
if (Common._initialized) return;
// 继承父类的类结构
if (Common._super.length != 0)
{ Common.prototype = new Common._super[0](); }
for (var index in Common._super) // 为防止下标不连续,使用此种循环
{
var superClass = Common._super[index];
if (!superClass._initialized)
{ superClass._loadclass(); } // 如果父类尚未加载,则先加载父类
for (var key in superClass.prototype)
{
if (key.charAt(0) == "_" || key == "constructor")
{ continue; } // 系统成员和构造方法不继承给子类
var value = superClass.prototype[key];
if (typeof value == "function")
Common.prototype[key] = value;
else Common.prototype[key] = null;
}
}
// 创建自身独有类结构
Common.prototype.field03 = null;
Common.prototype.method03 = function()
{
// 方法体,其中可以使用 this 关键字
};
Common._initialized = true;
}
Special._loadclass = function()
{
if (Special._initialized) return;
// 继承父类的类结构
if (Special._super.length != 0) Special.prototype = new Special._super[0]();
for (var index in Special._super) // 为防止下标不连续,使用此种循环
{
var superClass = Special._super[index];
if (!superClass._initialized)
{ superClass._loadclass(); } // 如果父类尚未加载,则先加载父类
for (var key in superClass.prototype)
{
if (key.charAt(0) == "_" || key == "constructor")
{ continue; } // 系统成员和构造方法不继承给子类
var value = superClass.prototype[key];
if (typeof value == "function")
Special.prototype[key] = value;
else Special.prototype[key] = null;
}
}
// 创建自身独有类结构
Special.prototype.field04 = null;
Special.prototype.method02 = function() // 覆盖父类的方法
{
Super.prototype.method02(); // 调用被覆盖的父类方法
// 方法体,其中可以使用 this 关键字
};
Special.prototype.method04 = function()
{
// 方法体,其中可以使用 this 关键字
};
Special._initialized = true;
}_loadclass 方法可以放在构造方法里边执行,以便首次执行构造方法生成对象之前,现场初次加载类结构。当然,也可以在声明构造方法之后统一执行,以便在浏览器获取 .js 之后即可完成加载。后边的例子会按照第一种方式,也就是首次生成对象时加载类结构来编写,以表达作者我对爪哇语言的不经意间的熟悉。另外我们可以看到每个类进行类加载时,加载继承结构的代码都完全一样。所以我们可以提取出一个顶层公共方法以方便类加载。在加载一个类之前需要设定其父类数组。/**
 * 加载父类类结构
 * 本方法用以加载指定类的所有继承结构。要求传入的类已经设定了 _super ,且是一个
 * 数组类型对象,里边的元素都是一个个类。
 * @param classObj 父类的类对象,即其构造方法自身,是一个函数类型的对象。一
 * 般来说,作为构造方法的函数,应该以大写字母开头,以示这是一个“类名”。
 */
var _loadSuperClassStruct = function(classObj)
{
if (!classObj || typeof classObj != "function" || !classObj._super)
{ return false; } // 如果已经被初始化,则认为父类加载已经成功完成
if (classObj._initialized) { return true; } // 继承父类的类结构
if (classObj._super.length != 0)
{ classObj.prototype = new classObj._super[0](); }
for (var index in classObj._super) // 以防 index 不连续
{
var superClass = classObj._super[index];
if (!superClass._initialized)
{
try
{
superClass._loadclass(); // 如果父类尚未加载,则先加载父类
}
catch (ex) { continue; } // 如果父类无法加载,则跳过继承这个父类
}
for (var key in superClass.prototype)
{
if (key.charAt(0) == "_" || key == "constructor")
{ continue; } // 系统成员和构造方法不继承给子类
var value = superClass.prototype[key];
if (typeof value == "function")
{ classObj.prototype[key] = value; }
else { classObj.prototype[key] = null; }
}
}
return true;
};于是,加载一个类结构的方法就可以如下ClassA._loadclass = function()
{
if (ClassA._initialized) return; // 继承父类的类结构
if (!_loadSuperClassStruct(ClassA))
{ throw new Error("Can't load super class of ClassA."); } // 创建自身独有类结构
ClassA.prototype.field = null;
ClassA.prototype.method = function()
{
// 方法体,其中可以使用 this 关键字
};
ClassA._initialized = true;
}
3 分配堆空间
按照脚本语言标准,分配堆空间,也就是在堆内存中开辟对象的空间,是由 new 关键字构成的。new Super();
new Common();
new Special();
4 初始化对象
构造方法提供初始化对象的功能。如果尚未加载类,则应先加载类。
这里边涉及二个技术,称为对象冒充(object masquerading)和混合工厂方式对象构造法。对象冒充是利用 JavaScript 其中 ECMAScript 部分,即脚本标准语言部分中反射的机制,来在一种对象的构造方法中执行别的对象的构造方法。因为 this. 字段所指的内容发生了变化,所以被调用的构造方法中的语句都会变为构造当前对象。相关内容可参看 function 类对象的 call() 方法和 apply() 方法。混合工厂方式基于 new 不可重叠原理来实现。对于符合 ECMAScript 标准的语言来说,new 的创建空间作用,应该在构造方法执行之前先行生效。对于 JavaScript 来说,就意味着当第一次生成新对象执行 new 的时候,对象类还没有加载,那么 new 出来的就是一个极其不完整的东西,该有的成员很可能都不会存在。所以在构造方法里边再行 new 一个对象,并将其作为返回值;此时因为构造方法内部 new 的存在,调用构造方法之前的那个 new 就会被无效化。于是看起来是直接新建了一个对象,其实是在构造方法内部,加载类完毕之后再新建的对象。这样就避免了初次建立对象无法正确获得类结构的问题。只是我不知道为什么这种多次 new 只有一个能生效的特性会被称为“混合工厂方式对象构造法”。可能是说这种把待生成对象当做返回值的方法应被称为工厂方法,而其本身又是构造方法,所以叫“混合工厂方法”吧。function Super(field01Value)
{
// 先加载类
if (!Super._initialized)
{
Super._loadclass();
return new Super(field01Value);
} // 初始化对象各变量
this.field01 = field01Value;
this.field02 = "super value 02";
}
function Common()
{
// 先加载类
if (!Common._initialized)
{
Common._loadclass();
return new Common();
} // 调用父类构造方法
Super.call(this, "common value 01"); // 初始化对象各变量
this.field03 = "common value 03";
}
function Special()
{
// 先加载类
if (!Special._initialized)
{
Special._loadclass();
return new Special();
} // 调用父类构造方法
Common.call(this, ""); // 初始化对象各变量
this.field04 = "special value 04";
this.field01 = "special value 01"; // 修改父类已经赋值过的属性值
}以上,不知道这个例子是不是妥帖。通过这种标准化的类加载方式,我们可以将严格的面向对象程序设计方案引入 JavaScript 。额外的,这种严格过程的对象生成方式,可以方便程序员生成新对象之后控制内存防止泄露。也能够避免方法体重复加载带来的内存浪费。如果将相关内容做成通用的 .js 文件,则并不会为对象化编程带来什么额外编码负担。

解决方案 »

  1.   

    接下来用一个例子最后总结一下 JavaScript 严格面向对象的编程方法/**
     * 加载父类类结构
     * 本方法用以加载指定类的所有继承结构。要求传入的类已经设定了 _super ,且是一个
     * 数组类型对象,里边的元素都是一个个类。
     * @param classObj 父类的类对象,即其构造方法自身,是一个函数类型的对象。一
     * 般来说,作为构造方法的函数,应该以大写字母开头,以示这是一个“类名”。
     */
    var _loadSuperClassStruct = function(classObj)
    {
    if (!classObj || typeof classObj != "function" || !classObj._super)
    { return false; } // 如果已经被初始化,则认为父类加载已经成功完成
    if (classObj._initialized) { return true; } // 继承父类的类结构
    if (classObj._super.length != 0)
    { classObj.prototype = new classObj._super[0](); }
    for (var index in classObj._super) // 以防 index 不连续
    {
    var superClass = classObj._super[index];
    if (!superClass._initialized)
    {
    try
    {
    superClass._loadclass(); // 如果父类尚未加载,则先加载父类
    }
    catch (ex) { continue; } // 如果父类无法加载,则跳过继承这个父类
    }
    for (var key in superClass.prototype)
    {
    if (key.charAt(0) == "_" || key == "constructor")
    { continue; } // 系统成员和构造方法不继承给子类
    var value = superClass.prototype[key];
    if (typeof value == "function")
    { classObj.prototype[key] = value; }
    else { classObj.prototype[key] = null; }
    }
    }
    return true;
    };// ---------- ---------- Animal 的声明段 ---------- ----------
    /**
     * 声明继承关系:Animal 类的父类——没有父类。
     */
    Animal._super = [ ];
    /**
     * Animal 类的类加载方法。
     */
    Animal._loadclass = function()
    {
    if (Animal._initialized) { return; } // 继承父类的类结构
    if (!_loadSuperClassStruct(Animal))
    { throw new Error("Can't load super class of Animal."); } // 创建自身独有类结构
    Animal.prototype.name = null;
    Animal.prototype.health = null;
    Animal.prototype.run = function()
    {
    if (this.getHealth())
    { alert(this.name + " is running..."); }
    else { alert(this.name + " is walking..."); }
    };
    Animal.prototype.getHealth = function()
    {
    return this.health;
    }
    Animal._initialized = true;
    };
    /**
     * Animal 类对象的构造方法
     */
    function Animal(nameValue)
    {
    // 先加载类
    if (!Animal._initialized)
    {
    Animal._loadclass();
    return new Animal(nameValue);
    } // 初始化对象各变量
    this.name = nameValue;
    }
    // ---------- ---------- Bird 的声明段 ---------- ----------
    /**
     * 声明继承关系:Bird 类的父类是 Animal。
     */
    Bird._super = [ Animal ];
    /**
     * Bird 类的类加载方法。
     */
    Bird._loadclass = function()
    {
    if (Bird._initialized) return; // 继承父类的类结构
    if (!_loadSuperClassStruct(Bird))
    { throw new Error("Can't load super class of Bird."); } // 创建自身独有类结构
    Bird.prototype.fly = function()
    {
    if (this.getHealth())
    { alert(this.name + " is flying..."); }
    else
    { alert("Sorry, it can't fly. Please wait for some day."); }
    };
    Bird._initialized = true;
    };
    /**
     * Bird 类对象的构造方法
     */
    function Bird(nameValue)
    {
    // 先加载类
    if (!Bird._initialized)
    {
    Bird._loadclass();
    return new Bird(nameValue);
    } // 初始化对象各变量
    Animal.call(this, nameValue);
    }
    // ---------- ---------- HomeAnimal 的声明段 ---------- ----------
    /**
     * 声明继承关系:HomeAnimal 类的父类是 Animal。
     */
    HomeAnimal._super = [ Animal ];
    /**
     * HomeAnimal 类的类加载方法。
     */
    HomeAnimal._loadclass = function()
    {
    if (HomeAnimal._initialized) return; // 继承父类的类结构
    if (!_loadSuperClassStruct(HomeAnimal))
    { throw new Error("Can't load super class of HomeAnimal."); } // 创建自身独有类结构
    HomeAnimal.prototype.getHealth = function()
    {
    return true;
    };
    HomeAnimal._initialized = true;
    };
    /**
     * HomeAnimal 类对象的构造方法
     */
    function HomeAnimal(nameValue)
    {
    // 先加载类
    if (!HomeAnimal._initialized)
    {
    HomeAnimal._loadclass();
    return new HomeAnimal(nameValue);
    } // 初始化对象各变量
    Animal.call(this, nameValue);
    }
    // ---------- ---------- Duck 的声明段 ---------- ----------
    /**
     * 声明继承关系:Duck 有二个父类,分别是 Bird 和 HomeAnimal ,其中 Bird 是主父类
     */
    Duck._super = [ Bird, HomeAnimal ];
    /**
     * Duck 类的类加载方法。
     */
    Duck._loadclass = function()
    {
    if (Duck._initialized) return; // 继承父类的类结构
    if (!_loadSuperClassStruct(Duck))
    { throw new Error("Can't load super class of Duck."); } // 创建自身独有类结构
    Duck.prototype.fly = function()
    {
    alert("Duck can't fly.");
    };
    Duck._initialized = true;
    };
    /**
     * Duck 类对象的构造方法
     */
    function Duck()
    {
    // 先加载类
    if (!Duck._initialized)
    {
    Duck._loadclass();
    return new Duck();
    } // 初始化对象各变量
    Bird.call(this, "duck");
    }
    // ---------- ---------- 测试程序段 ---------- ----------
    var duckObj = new Duck();
    // 方法调用
    duckObj.run(); // 继承自父类的方法。方法中还会调用 HomeAnimal 覆盖 Animal 的方法:getHealth()
    duckObj.fly(); // 覆盖掉 Bird 类的方法。// 对象类型认定
    alert(duckObj instanceof Duck);
    alert(duckObj instanceof Bird);
    alert(duckObj instanceof HomeAnimal);
    alert(duckObj instanceof Animal);// 类结构动态改变效果确认
    Duck.prototype.testMethod = function() { alert("OK"); }
    duckObj.testMethod();
      

  2.   

    以上例子是我昨天写的,可以在浏览器中直接使用以检测效果。内容也发在自己的空间里边了,地址是:http://blog.csdn.net/shanelooli/article/details/7463574相关概念参考资料:http://www.w3school.com.cn/js/index_pro.asp
      

  3.   


    为啥要在js里模拟类呢 js本身就不需要类的概念啊
      

  4.   


    之前不是想实现比较复杂的继承关系吗……比如我现在这边正在做一个准单页面应用,页面分为几个区域,区域要分别请求权限决定是否显示。同时还有一组自己模拟的弹出窗口。这样可以让弹出窗口与页面区域有一个公共的父类,把 showSelf() hideSelf() 一类的东西写在父类里边。当时想的是,还可以把这些东西放在同一个数组里边统一遍历。  后来实际做才发现因为没有引用类型的概念,所以只需要自己保证了方法名称一致,任何对象都可以混在一起。进一步体会到,因为缺乏足够的检查方式,JavaScript 的 IDE 比强类型语言要差一些,很多自动完成、跳转,都做得不是特别完善,造成很多小的编程漏洞只能靠编程者自己的水平预防,无法通过自动化的检查器。嗯,也许不是无法,而是现在没有能和 XCode, Eclipse 一类东西媲美的开发工具吧。
      

  5.   

    最近发觉,应该在构造方法里边制作
    var privateFields = {
        name: "Shane",
        age: 28
    };
    this.getPrivateFields = function() { return privateFields; }这样,类声明的各种方法,都可以通过 this.getPrivateFields() 来获取私有变量了。使用这样声明的类,其对象在操作时,就可以避免变成错误之无意间对属性值的修改。
      

  6.   


    似乎 ecma6 会有类出现。
    不同人有不同编程习惯吗。
    可能一边用C#一边用js , 都用类的概念来编程的话就比较容易设计规范吧。
    楼主不错!挺!