跳到主要内容
CNode

究竟怎样OOP?

社区
Ppengchun发布于 15 年前最后回复 12 年前
13107620

只看见了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)

Y
yakczh#1312 年前

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

A
anonymous#1215 年前

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

A
anonymous#1115 年前

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

F
frank#1015 年前

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

V
vincent#915 年前

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

M
mxfli#815 年前

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

A
anonymous#715 年前

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;

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

Person是拼写错了,多谢纠正。

外部使用的时候出于习惯我常常采用静态调用的方式,例如:

var user = Person.create('name', age);

在person.js中包一层也就避免了使用上的不一致,你new或者不new,它返回的都是一样的东西

P
pengchun#515 年前

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

Q
qleelulu#215 年前

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

F
fengmk2#115 年前

最后的解决方案有个模式术语叫工厂方法。

其实在javascript或者python这些脚本语言里面,很多对象都是静态单件对象,我常常喜欢最简单的方式,因为它在整个进程里无需实例化的,正如nodejs的module.exports。

var foo = {
echo: function() {
console.log('hello world');
}
};

参与回复

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