JavaScript:好的部分 – 如何不使用`new`
Crockford的书“ JavaScript:The Good Parts ”(第114页)说,构造函数应该总是以大写字母(例如Point)给出名称,并且只能使用具有首字母大写字母的函数名称其他的一切都应该小一些)。
这个约定帮助我们避免忘记在构造函数中使用new
操作符。
他接着说: “更好的应对策略是不使用new
的。”
我的问题是,我们如何编写JavaScript而不使用new
的呢?
- 我们可以用文字
{}
和[]
来避免new Object()
和new Array()
。 - 我们可以避免使用
0
,true
和''
new Number()
,new Boolean()
和new String()
。 - 我们可以避免使用类似
/pattern/
new RegExp()
。
我们如何避免new Date()
?
而且,最重要的是,我们如何避免在我们自己的自定义对象中使用new
?
Crockford给出了一个对象创build函数的例子,应该由JS本身在http://developer.yahoo.com/yui/theater/上提供的一个Javascript会话中提供。;
然而,YUI(3)团队本身使用“新”,他们遵循他的build议(因为他是雅虎首席JS架构师(更新:他继续前进,但这个回应最初写的时候声明是真的)。这个特别的陈述更多的是在“学术”层面上,那么这个语言应该被devise为“正确的”,而不是与基于阶级的inheritance材料的剩余部分相比,他(恕我直言)正确地说, JS是冲突的,基于原型但与“古典类”inheritance语言这一点。
不过,JS就这样去用“新”了。
你可以在这里find他的对象创buildfunction: http : //javascript.crockford.com/prototypal.html
if (typeof Object.create !== 'function') { Object.create = function (o) { function F() {} F.prototype = o; return new F(); }; } newObject = Object.create(oldObject);
编辑:更新使用Crockford的最新版本的function – 有三个。
更新 2015年6月:我们已经有Object.create(...)
相当长一段时间了,目前所有的浏览器支持(包括IE 9及以上),所以不需要使用Crockford的function。
然而 ,事实certificate,如果你使用Object.create
你应该确保你不要那么做:这个函数的FAR比使用new Constructor()
慢!
请参阅http://mrale.ph/blog/2014/07/30/constructor-vs-objectcreate.html以获得解释(适用于V8引擎),并参阅http://jsperf.com/object-create-vs- crockford-vs-jorge-vs-constructor / 62进行演示演示。
另一个不要背弃new Constructor(...)
是ES6类将会广泛采用,即使只是因为大多数Javascript开发者来自基于类的语言。
另外看看这篇针对 Object.create
文章: http : //davidwalsh.name/javascript-objects-deconstruction
不pipe喜不喜欢,特别是在你想与大量人分享的项目中(在时间和空间上 – 意思是说正确或者随着时间的推移,其他人接pipe你)有更多的原因使用new
。
更新 2015年9月:对于我自己,我已经开始使用ES 2015 Javascript的一切 – 使用io.js和/或Babel。 我也不会在我的项目中使用任何 new
,除了像new Error(...)
的Javascript内置插件。 我更喜欢使用function更强大的方法,我完全忽略了对象系统。 [my-object].prototype
, this
完全从我的项目。 我对这些想法持怀疑态度的时间最长,“因为对象工作得很好”。 但是在一个新的(io.js)项目开始之后非常不情愿地尝试一下,它就“点击”了,我不明白为什么我浪费了二十年。 好的,不完全是,今天的JS引擎和硬件更有利于这种风格。 特别是对于ES 2015,我build议给一个完全没有任何this
和class
的function样式(新的ES 2015关键字或基于使用constructorFn.prototype
的整个概念)一试。 它可能需要几个星期,但一旦“点击”,我保证你永远不会回来 – 不是自愿的。 它更方便,更强大。
我不知道如何避免new Date()
或new XMLHttpRequest()
。 但我知道如何避免使用新的我自己的types。
首先,我从Object.create()
开始。 这是一个ES5方法,所以无处不在。 我使用es5-shim添加它,然后我准备好了。
我喜欢模块模式,所以我首先把自己的types封装在一个自动执行的匿名函数var Xyz = (function() {...})()
。 这意味着我有一个私人空间来工作,而不会在全局名称空间混乱。 我用一个create()
函数和一个prototype
属性返回一个对象。 create()
函数适用于我的types的用户。 当他们想要一个,他们调用Xyz.create()
,并找回我的types的新的,初始化的对象。 prototype
属性是可用的,如果人们想要inheritance。
这是一个例子:
var Vehicle = (function(){ var exports = {}; exports.prototype = {}; exports.prototype.init = function() { this.mph = 5; }; exports.prototype.go = function() { console.log("Going " + this.mph.toString() + " mph."); }; exports.create = function() { var ret = Object.create(exports.prototype); ret.init(); return ret; }; return exports; })();
inheritance如下所示:
var Car = (function () { var exports = {}; exports.prototype = Object.create(Vehicle.prototype); exports.prototype.init = function() { Vehicle.prototype.init.apply(this, arguments); this.wheels = 4; }; exports.create = function() { var ret = Object.create(exports.prototype); ret.init(); return ret; }; return exports; })();
不使用new
和盲目的跟随Crockford是愚蠢的。
理解JavaScript并编写好的代码。 使用new
关键字是JavaScript OO的基石 。
你会错过了很多好的JavaScript代码,避免new
。
而不是任意地从工具箱中分割出大块,学习它并恰当地使用它。
克罗克福德有一个习惯,就是说任何在JavaScript中给他提供代码错误的东西都是不好的。
我个人会继续说: “更好的应对策略是胜任的。”
您可以通过创build工厂函数来避免new
:
var today = Date.getToday();
(如果你想知道,你不能避免它的工厂function本身:)
Date.getToday = function() { return new Date(); };
虽然我只认为你应该创build这样的function,如果它增加语义值(如上面的情况),或者如果你可以默认一些构造参数。 换句话说,不要只是为了避免使用new
。
这个问题已经被问及回答: JavaScript的“新”关键字是否被认为是有害的?
正如雷诺斯所说的,一味地跟着克罗克福德(或其他任何人)不理解他们为什么说这些事情是愚蠢的。
我认为他根本不使用new
build议是概念性的(学术性的),而不是字面上的。 Date
类是规则的完美例外,因为如何使用标准ECMAScript获取当前(或任意)date对象?
但是,关于不使用new
的自定义对象,你可以使用一些策略。 一个是使用类工厂方法,而不是构造函数,它可以把一个对象实例作为参数来“保佑”你的新types,或者默认使用一个新的对象字面值。 考虑以下:
var newCar = function(o) { o = o || {}; // Add methods and properties to "o"... return o; }
function F() { return { /* your fields and methods here */ } }
您可以通过返回匿名对象并在构造函数中使用闭包来避免“新”。 这也有助于隐藏私人数据。
考虑:
function SomeCounter(start) { var counter = start; return { getCounter : function() { return counter; }, increaseCounter : function() { counter++; } }; }
现在要使用这个,所有你需要做的是
var myCounter = SomeCounter(5); myCounter.increaseCounter(); console.log(myCounter.getCounter()); //should log 6
这样做的好处是你不需要记住使用“新”,但如果你这样做,不会伤害你。
var myCounter = new SomeCounter(5); //still works
你坚持使用“新”来实例化其他人的对象。 但是对于你自己的代码,你可以避免“this”和“new”的陷阱。
(顺便说一下,这个问题实际上不是'new'本身,而是一个用'new'实例化的对象可能在内部使用'this','this'的使用导致频繁而微妙的错误,因为javascript'this '要求调用者做额外的工作来将被调用的方法绑定到正确的对象上,而不正确的绑定在运行时很难检测到或检测到。
简洁版本:
为避免使用“new”来实例化您编写的对象,只需从任何函数返回一个对象即可。 在该函数内部,将任何方法附加到该对象并进行初始化。 你完成了 – 该函数是你的类定义和构造函数。
长版本,例如:
以下“自我”模式避免使用“新”和“这个”。 虽然模式确实在每个对象中存储方法副本,但是这并不重要,除非您在运行时创build大量对象。 如果是这样的话,那么你总是可以使用来自http://justjs.com/posts/this-considered-harmful的“flyweight”模式。; (虽然这个页面上的例子仍然使用'this',但是稍微调整一下以适应下面的模式。)
// this function defines a "class" -- the whole function is the constructor function Foo(x) { // create/init whatever object type you want here var self = {x: x}; // if subclassing, for instance, you can do: // var self = Baz(x); // public method self.foo = function() { return self.x; }; // public method self.bar = function() { console.log(self.x); logger(); }; // private method function logger() { console.log(self.x); } // ...more constructor bits can go here // don't forget to return self return self; } var f = Foo(1); var g = Foo(2); setTimeout(f.bar, 1000); setTimeout(g.bar, 1000); console.log(g.foo()); // 2 gx = 5; console.log(f.foo()); // 1 console.log(g.foo()); // 5 // ...then, 1 second later: // 1 (from f.bar) // 1 (from f.logger) // 5 (from g.bar) // 5 (from g.logger) // blows up if uncommented // f.logger();