JS真的错了吗——Object-Oriented JavaScript(Part 2)
?
一、js是世界上最容易被误解的语言function F(){};F.prototype = oldObject;var newObject = new F();这太繁琐了。基于原型语言理论上应该存在一个函数create(prototypeObject),功能是基于原型对象产生新对象,例如,var newObject = create(oldObject);看到这样的代码,人们就会自然很清晰地联想到,newObject是以oldObject模板构造出来的。class Empolyee{String name;public Employee(String name){this.name = name;}public getName(){return this.name;}}class Coder extends Employee {String language;public Coder(name,language){super(name);this.language = language;}public getLanguage(){return this.language;}}? 1 实现创建对象现有的对象都是基本类型,怎么创建用户自定义的对象呢?(解释:var i = 1;这里的i是解释器帮忙封装的Number对象,虽然看起来跟C的int没区别,但实际上可以i.toString()。)java使用构造函数来产生对象,我们尝试把java的Empolyee的构造函数代码拷贝下来,看看可不可以模仿function Empolyee(name){this.name = name;}?我们只要生成一个空对象obj,再把函数里面的this换成obj,执行函数,就可以生成自定义对象啦!我们把Employee这样用来创建对象的函数称作构造函数。1) 首先我们用原生的方式为function添加方法call和apply,实现把把函数里面的this替换成obj。call,apply在Lisp语言中已经有实现,很好参考和实现。2) 然后实现生成实例?function Empolyee(name){this.name = name;}var employee = {};Employee.call(employee,'Jack');??? ?3) 到这里,以类似java方式产生对象基本完成了,但是这个employee对象没有方法我们的function是第一类对象,可以运行时创建,可以当做变量赋值,所以没有问题。function Empolyee(name){this.name = name;this.getName = function(){return this.name};}很好,我们团队顺利向前走了一步,今晚大家不用加班了!? 2 实现继承创建对象成功了,接着考虑实现继承。现在我们所有数据都是对象,没有类,有两种方案摆在我们的面前a.类继承b.原型继承function Coder extends Employee(name,language){super(name);this.language = language;}?1) 把extends后面的函数自动记录下来,放到function对象的parentFunc变量2) 如果第一行是super(),替换成var parent = newInstance(Coder.parentFunc,XXX),这样内部保留一个名为parent父对象;3) 把this替换为obj,super替换换成parent4) "."和"[]"重新定义,需要支持在对象内部parent对象查找属性。这四步都属于比较大的改动,只要认真想一想都觉得不是太容易。更重要的是,即使把这4步实现了,不但语言变得太复杂了,而且产生的对象根本享受不了继承带来的好处——内存中的代码复用,因为这样产生的每个对象都有"父类(函数)"的代码而不是仅有一份。这时候该注意到java中使用类的意义了,java类的代码在内存只有一份,然后每个对象执行方法都是引用类的代码,所有子类对象调用父类方法的时候,执行的代码都是同一份父类的方法代码。但是JS没有类,属性和方法都是存在对象之中,根本没有办法做到java那样通过类把代码共享给所有对象!a方案宣告失败? ? ? ? ?2.b 实现原型继承看b方案。我们现在的js语言,一切都是对象,显然非常适合使用基于原型的继承方式,就看具体如何实现了。我们新建一个topObject来代表顶层对象,那么创建employee对象的时候,应该在employee对象内部设置一个属性引用topObject;同理,创建coder对象的时候,应该在coder对象内部设置一个属性引用employee对象,我们把这个引用原型对象的属性命名约定为"__proto__"。更进一步,为了构建一个对象的过程更自然,构建时候应该先在新对象中设置引用原型对象的属性,以表示先用模板制作出一个和模板一致的对象,然后再才执行构造函数初始化这个新对象自身的属性,以添加个性化的东西。具体实现代码如下:var topObject = {__version__ : 1.0;};function Empolyee(name){this.name = name;this.getName = function(){return this.name};}var employee = {};employee.__proto__ = topObject;Employee.call(employee,'Jack');function Coder(name,language){this.name = name;this.language = this.language;this.getLanguage = function(){return this.language};}var coder = {};coder.__proto__ = employee;Coder.call(coder,'Coder Jack','Java');?当然我们还要做的工作就是在javascript解释器中增加对__proto__的支持,当一个对象访问一个自身没有的属性的时候,就通过__proto__属性查找原型链上是否存在该属性。function sliceArguments(argumentsObj,n){var args = [];for(var i=0;i<argumentsObj.length;i++){if(i>=n){args.push(argumentsObj[i]);}}return args;}function newInstance(prototype,constructor){var obj = {};obj.__proto__ = prototype;constructor.apply(obj,sliceArguments(arguments,2));}var employee = newInstance(topObject,Employee,'Jack');var coder = newInstance(employee,Coder,'Coder Jack','Java');? ? ? ? ? ? ? ?优化2:缩减参数仔细一看,function newInstance的参数可以更少,我们可以把原型对象prototype作为属性放在constructor,那样我们的函数就可以只有一个参数了。属性名就约定为prototype吧。2.1 我们修改解释器,把topObject写入语言作为原生的顶级对象;再修改function的源代码,让每一个新建的function都默认具有属性prototype = topObject2.2 优化后的代码如下function newInstance(constructor){var obj = {};obj.__proto__ = constructor.prototype;constructor.apply(obj,sliceArguments(arguments,1));return obj;}function Employee(name){this.name = name;this.getName = function(){return this.name};}var employee = newInstance(Empolyee,'Jack');var employee2 = newInstance(Empolyee,'Jack2');var employee3 = newInstance(Empolyee,'Jack3');function Coder(name,language){this.name = name;this.language = language;this.getLanguage = function(){return this.language};}Coder.prototype = newInstance(Empolyee,'');var coder = newInstance(Coder,'Coder Jack','Java');var coder2 = newInstance(Coder,'Coder Lee','C#');var coder3 = newInstance(Coder,'Coder Liu','C++');var coder4 = newInstance(Coder,'Coder Liu','JavaScript');?至此,我们利用已有的设施,简单有效地开发出一个面向对象的javascript版本!Congratulations!