JavaScript 面向对象编程,严格过程的标准化编程法,目前为止最好的 JS 生成对象代码结构
JavaScript 面向对象编程
作者:Shane Loo Li
历史:初版 2012-4-15 Sunday Shane Loo Li
修改首次初始化对象和类加载的执行顺序 2012-4-16 Monday Shane Loo Li
市面上流行了很多 JavaScript 面向对象的编程方法,其中不少都有好些问题。这里总结最正确的 JavaScript 面向对象编程模式。
对于类 Special 继承自类 Common 继承自类 Super 的情况,一个 Special 对象的创建,详细说来,应该经历以下步骤。
1 确定继承关系
1.1 读取 Special 的父类,发现是 Common
1.2 读取 Common 的父类,发现是 Super
1.3 读取 Super 的父类,发现没有了(隐形父类 Object)
2 加载类结构(如果没有加载的话。加载过的话则不需要重复加载)
2.1 加载 Super, Common, Special 类的类属性和类方法都有哪些
2.2 加载 Super 类对象具有哪些属性,哪些方法
2.3 加载 Common 类对象具有哪些属性,哪些方法
2.4 加载 Special 类对象具有哪些属性,哪些方法
2.5 给 Super, Common, Special 类的类属性分配空间
2.6 给 Super, Common, Special 类的类属性赋值
3 分配堆空间
3.1 分配 Super 的空间,包括 Super 所有成员的空间
3.2 分配 Common 的空间,包括 Common 所有成员的空间
3.3 分配 Special 的空间,包括 Special 所有成员的空间
4 初始化对象
4.1 调用 Super 的构造方法
4.2 调用 Common 的构造方法
4.3 调用 Special 的构造方法
那么,怎么用 JavaScript 合理并严格地依照标准对象构建顺序创建对象呢?
1 确定继承关系。因为 JavaScript 没有变量类型绑定,所以应该能够允许多继承。其中第一个父类为主父类。
2 加载类结构
这里涉及到一个技术,熟悉 JavaScript 面向对象编程的人都会很了解,称为原型链(prototype chaining)。JavaScript 面向对象的类结构,其实是靠每一个类的原型对象来保存的。每生成一个对象,都是根据原型对象,制作一个与之具有同样成员,成员值也都相同的新对象。如果直接让一个类的原型对象等于一个其父类的对象,就相当于复制了父类原型的所有成员,放入了当前类的原型中。这些成员除了显示可见的意外,还包括用于 instanceof 的类名属性等一些系统成员变量,从而能够实现子类成员 instanceof 父类类名,得到 true 的结果——这种认定用的属性可以传递给更下一层的子类。
除了由原型链形成的主继承之外,因为 JavaScript 没有引用类型的概念,也就没有接口的概念。所以需要手动加载除主父类以外的其余父类成员。通过 for (var xxx in xxx) 的方式,可以取出其余父类原型中的成员,并用其建立当前类的类结构。这里需要注意,如果各种父类中有名称完全相同的方法,则会以最后加载进来的那个为子类的方法。这不得不说是 JavaScript 面向对象多重继承的一个遗憾。
_loadclass 方法可以放在构造方法里边执行,以便首次执行构造方法生成对象之前,现场初次加载类结构。当然,也可以在声明构造方法之后统一执行,以便在浏览器获取 .js 之后即可完成加载。后边的例子会按照第一种方式,也就是首次生成对象时加载类结构来编写,以表达作者我对爪哇语言的不经意间的熟悉。
另外我们可以看到每个类进行类加载时,加载继承结构的代码都完全一样。所以我们可以提取出一个顶层公共方法以方便类加载。在加载一个类之前需要设定其父类数组。
于是,加载一个类结构的方法就可以如下
3 分配堆空间按照脚本语言标准,分配堆空间,也就是在堆内存中开辟对象的空间,是由 new 关键字构成的。
4 初始化对象
构造方法提供初始化对象的功能。如果尚未加载类,则应先加载类。
这里边涉及二个技术,称为对象冒充(object masquerading)和混合工厂方式对象构造法。
对象冒充是利用 JavaScript 其中 ECMAScript 部分,即脚本标准语言部分中反射的机制,来在一种对象的构造方法中执行别的对象的构造方法。因为 this. 字段所指的内容发生了变化,所以被调用的构造方法中的语句都会变为构造当前对象。相关内容可参看 function 类对象的 call() 方法和 apply() 方法。
混合工厂方式基于 new 不可重叠原理来实现。对于符合 ECMAScript 标准的语言来说,new 的创建空间作用,应该在构造方法执行之前先行生效。对于 JavaScript 来说,就意味着当第一次生成新对象执行 new 的时候,对象类还没有加载,那么 new 出来的就是一个极其不完整的东西,该有的成员很可能都不会存在。所以在构造方法里边再行 new 一个对象,并将其作为返回值;此时因为构造方法内部 new 的存在,调用构造方法之前的那个 new 就会被无效化。于是看起来是直接新建了一个对象,其实是在构造方法内部,加载类完毕之后再新建的对象。这样就避免了初次建立对象无法正确获得类结构的问题。只是我不知道为什么这种多次 new 只有一个能生效的特性会被称为“混合工厂方式对象构造法”。可能是说这种把待生成对象当做返回值的方法应被称为工厂方法,而其本身又是构造方法,所以叫“混合工厂方法”吧。
以上,不知道这个例子是不是妥帖。通过这种标准化的类加载方式,我们可以将严格的面向对象程序设计方案引入 JavaScript 。额外的,这种严格过程的对象生成方式,可以方便程序员生成新对象之后控制内存防止泄露。也能够避免方法体重复加载带来的内存浪费。如果将相关内容做成通用的 .js 文件,则并不会为对象化编程带来什么额外编码负担。
以上例子是我昨天写的,可以在浏览器中直接使用以检测效果。
相关概念参考资料:http://www.w3school.com.cn/js/index_pro.asp
作者:Shane Loo Li
历史:初版 2012-4-15 Sunday Shane Loo Li
修改首次初始化对象和类加载的执行顺序 2012-4-16 Monday Shane Loo Li
市面上流行了很多 JavaScript 面向对象的编程方法,其中不少都有好些问题。这里总结最正确的 JavaScript 面向对象编程模式。
对于类 Special 继承自类 Common 继承自类 Super 的情况,一个 Special 对象的创建,详细说来,应该经历以下步骤。
1 确定继承关系
1.1 读取 Special 的父类,发现是 Common
1.2 读取 Common 的父类,发现是 Super
1.3 读取 Super 的父类,发现没有了(隐形父类 Object)
2 加载类结构(如果没有加载的话。加载过的话则不需要重复加载)
2.1 加载 Super, Common, Special 类的类属性和类方法都有哪些
2.2 加载 Super 类对象具有哪些属性,哪些方法
2.3 加载 Common 类对象具有哪些属性,哪些方法
2.4 加载 Special 类对象具有哪些属性,哪些方法
2.5 给 Super, Common, Special 类的类属性分配空间
2.6 给 Super, Common, Special 类的类属性赋值
3 分配堆空间
3.1 分配 Super 的空间,包括 Super 所有成员的空间
3.2 分配 Common 的空间,包括 Common 所有成员的空间
3.3 分配 Special 的空间,包括 Special 所有成员的空间
4 初始化对象
4.1 调用 Super 的构造方法
4.2 调用 Common 的构造方法
4.3 调用 Special 的构造方法
那么,怎么用 JavaScript 合理并严格地依照标准对象构建顺序创建对象呢?
1 确定继承关系。因为 JavaScript 没有变量类型绑定,所以应该能够允许多继承。其中第一个父类为主父类。
2 加载类结构
这里涉及到一个技术,熟悉 JavaScript 面向对象编程的人都会很了解,称为原型链(prototype chaining)。JavaScript 面向对象的类结构,其实是靠每一个类的原型对象来保存的。每生成一个对象,都是根据原型对象,制作一个与之具有同样成员,成员值也都相同的新对象。如果直接让一个类的原型对象等于一个其父类的对象,就相当于复制了父类原型的所有成员,放入了当前类的原型中。这些成员除了显示可见的意外,还包括用于 instanceof 的类名属性等一些系统成员变量,从而能够实现子类成员 instanceof 父类类名,得到 true 的结果——这种认定用的属性可以传递给更下一层的子类。
除了由原型链形成的主继承之外,因为 JavaScript 没有引用类型的概念,也就没有接口的概念。所以需要手动加载除主父类以外的其余父类成员。通过 for (var xxx in xxx) 的方式,可以取出其余父类原型中的成员,并用其建立当前类的类结构。这里需要注意,如果各种父类中有名称完全相同的方法,则会以最后加载进来的那个为子类的方法。这不得不说是 JavaScript 面向对象多重继承的一个遗憾。
_loadclass 方法可以放在构造方法里边执行,以便首次执行构造方法生成对象之前,现场初次加载类结构。当然,也可以在声明构造方法之后统一执行,以便在浏览器获取 .js 之后即可完成加载。后边的例子会按照第一种方式,也就是首次生成对象时加载类结构来编写,以表达作者我对爪哇语言的不经意间的熟悉。
另外我们可以看到每个类进行类加载时,加载继承结构的代码都完全一样。所以我们可以提取出一个顶层公共方法以方便类加载。在加载一个类之前需要设定其父类数组。
于是,加载一个类结构的方法就可以如下
3 分配堆空间按照脚本语言标准,分配堆空间,也就是在堆内存中开辟对象的空间,是由 new 关键字构成的。
4 初始化对象
构造方法提供初始化对象的功能。如果尚未加载类,则应先加载类。
这里边涉及二个技术,称为对象冒充(object masquerading)和混合工厂方式对象构造法。
对象冒充是利用 JavaScript 其中 ECMAScript 部分,即脚本标准语言部分中反射的机制,来在一种对象的构造方法中执行别的对象的构造方法。因为 this. 字段所指的内容发生了变化,所以被调用的构造方法中的语句都会变为构造当前对象。相关内容可参看 function 类对象的 call() 方法和 apply() 方法。
混合工厂方式基于 new 不可重叠原理来实现。对于符合 ECMAScript 标准的语言来说,new 的创建空间作用,应该在构造方法执行之前先行生效。对于 JavaScript 来说,就意味着当第一次生成新对象执行 new 的时候,对象类还没有加载,那么 new 出来的就是一个极其不完整的东西,该有的成员很可能都不会存在。所以在构造方法里边再行 new 一个对象,并将其作为返回值;此时因为构造方法内部 new 的存在,调用构造方法之前的那个 new 就会被无效化。于是看起来是直接新建了一个对象,其实是在构造方法内部,加载类完毕之后再新建的对象。这样就避免了初次建立对象无法正确获得类结构的问题。只是我不知道为什么这种多次 new 只有一个能生效的特性会被称为“混合工厂方式对象构造法”。可能是说这种把待生成对象当做返回值的方法应被称为工厂方法,而其本身又是构造方法,所以叫“混合工厂方法”吧。
以上,不知道这个例子是不是妥帖。通过这种标准化的类加载方式,我们可以将严格的面向对象程序设计方案引入 JavaScript 。额外的,这种严格过程的对象生成方式,可以方便程序员生成新对象之后控制内存防止泄露。也能够避免方法体重复加载带来的内存浪费。如果将相关内容做成通用的 .js 文件,则并不会为对象化编程带来什么额外编码负担。
接下来用一个例子最后总结一下 JavaScript 严格面向对象的编程方法。
以上例子是我昨天写的,可以在浏览器中直接使用以检测效果。
相关概念参考资料:http://www.w3school.com.cn/js/index_pro.asp