CNode

究竟怎样OOP?

Ppengchun发布于15 年前最后回复12 年前13 回复10732 浏览0 收藏

只看见了OO就浮想联翩的同学请面壁去。说实在的,为这个题目我纠结了很久,JavaScript中本身一切就是对象(Object),那为什么还要纠结怎样OOP呢? 接yixuan的话题,我先说一个例子。在用NodeJS写程序的过程中,我们常常碰到这样的代码(person.js): /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: / exports.create = function(name, age) { var p = { name : name, age : age, echo : function() { console.log("Name: " + name + ", Age: " + age); } } return p; } 我们在另一个文件test_class.js中这样使用: var Person = require('./person.js'); var user = Person.create('aleafs', 28); user.echo(); 运行test_class.js,不出我们所料,结果如下: $ node test_class.js Name: aleafs, Age: 28 如果只求实现功能,我想我们的目的达到了,并且在大多数情况下它能运行得很好。但是,如果创建的person对象很多,我们知道,每一个person对象都要在内存中占用一块地方存放它的echo方法——尽管echo方法对每一个实例都是完全相同的,显然有点浪费对不对? 与上面的代码类似的,person.js还有如下的实现方式: / vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: / exports.create = function(name, age) { this.name = name; this.age = age; this.echo = function() { console.log("Name: " + this.name + ", Age: " + this.age); } } 这样的实现多少想点OOP的程序了,对吧?可是它仍然存在echo方法的内存浪费问题。为此,我们寄希望于prototype机制: / vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: / var Person = function(name, age) { this.name = name; this.age = age; } Person.prototype.echo = function() { console.log("Name: " + this.name + ", Age: " + this.age); } exports.create = Person; 由于在JavaScript中,对同一个类的多个对象而言,prototype只被解析并保存一份实例,这个方法确实能够避免我们上面提到的内存浪费问题。但是另一个问题来了,我们对test_class.js原封不动,运行之: $node test_class.js node.js:116 throw e; // process.nextTick error, or 'error' event on first tick ^ TypeError: Cannot call method 'echo' of undefined at Object. (/home/pengchun/b.js:4:6) at Module._compile (module.js:373:26) at Object..js (module.js:379:10) at Module.load (module.js:305:31) at Function._load (module.js:271:10) at Array. (module.js:392:10) at EventEmitter._tickCallback (node.js:108:26) 什么情况!居然说找不到echo方法!哦,你一定想到了,在test_class.js中我们不能继续使用 var user = Person.create('aleafs', 28); 了,我们得用new创建一个对象,像这样: var user = new Person.create('aleafs', 28); 试了一下,果然能运行了: $ node test_class.js Name: aleafs, Age: 28 爷爷的,这不是坑爹吗?有没有new并不报语法错误,却在运行时报错。实际上,在上面第二种方法实现person类的情况下也存在这个问题,我们不说了。看看我是怎么解决这个问题的: / vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */ var Person = function(name, age) { this.name = name; this.age = age; } Person.prototype.echo = function() { console.log("Name: " + this.name + ", Age: " + this.age); } exports.create = function(name, age) { return new Person(name, age); }; 看见了没,我仍然采用prototype的方法来定义类。不同的是,我用exports对象指定输出变量时,没有直接把Person类赋值给create,而是创建了一个函数,在这个函数内new了一个对象返回。这样就避免了在外部使用new关键字的问题。 这个方法很好,某某大师也推荐过。我继续推荐这篇文章: http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html

查看回复

回复 (13)

A
anonymous#1·15 年前

建议用Person.new('somebody', 22) 更优雅

A
anonymous#2·15 年前

发现用coffeescript写node程序太爽了,对原生的OO进行了一定的扩展,好用多了。另外backbone也是个好东西,很喜欢里面的bind,可以解决回调函数的this问题。哦,再加一个:underscore

A
anonymous#3·15 年前

YUI3里是这样的 我认为也不错 var Person = function(name, age) { if (! this instanceof Person){ return new Person(name, age); } this.name = name; this.age = age; } Person.prototype.echo = function() { console.log("Name: " + this.name + ", Age: " + this.age); } exports.create = Person;

F
frank#4·15 年前

记得很早之前,有个朋友也说过关于new的使用,,于是翻译了D.C文章,文章在: 《[翻译]废掉new》 http://forum.ajaxjs.com:8080/viewthread.php?tid=54&extra=page%3D1 希望对怎么理解new有帮助

V
vincent#5·15 年前

同意,原则上js本身就是一个脚本语言,为啥非要带个new整的跟java似的。根据不同的使用需求使用最高效的代码即可。这种方式个人很推荐。因为刚开发了个web im要弹出很多对话框,看着一堆new也挺闹心。这种包装符合个人喜好。关键是对于内存开销的节省。非常OK

M
mxfli#6·15 年前

原则还是把new包装在“类”库的内部,外部调用只有函数,保持这个原则,在JS中很重要。上升到OOP的高度,总会让很多人很纠结。

Q
qleelulu#7·15 年前

var user = Persion.create('aleafs', 28); 这个就是调用 Person(name, age)啊,你这个函数根本就没返回值,所以user是undefined啊。 new Person(name, age) 返回的是函数本身啊(因为Person函数没有返回对象)。 PS:在外部使用new关键字为什么会是个问题? 再PS: Persion 是不是拼错了?

F
fengmk2#8·15 年前

最后的解决方案有个模式术语叫工厂方法。 其实在javascript或者python这些脚本语言里面,很多对象都是静态单件对象,我常常喜欢最简单的方式,因为它在整个进程里无需实例化的,正如nodejs的module.exports。 var foo = { echo: function() { console.log('hello world'); } };

P
pengchun#9·15 年前
引用 qleeluluvar user = Persion.create('aleafs', 28); 这个就是调用 Person(name, age)啊,你这个函数根本就没返回值,所以user是undefined...

Person是拼写错了,多谢纠正。 外部使用的时候出于习惯我常常采用静态调用的方式,例如: var user = Person.create('name', age); 在person.js中包一层也就避免了使用上的不一致,你new或者不new,它返回的都是一样的东西

P
pengchun#10·15 年前

是的,prototype一个最大的好处就是方便继承

Y
yakczh#13·12 年前

Person.new('somebody', 22) 怎么写?

参与回复
登录后即可参与回复。登录