我一直在重新考虑其他人的JavaScript代码。
BEFORE:
function SomeObj(flag) {
var _private = true;
this.flag = (flag) ? true : false;
this.version="1.1 (prototype)";
if (!this._someProperty) this._init();
// leading underscore hints at what should be a 'private' to me
this.reset(); // assumes reset has been added...
}
SomeObj.prototype.reset = function() {
/* perform some actions */
}
/* UPDATE */
SomeObj.prototype.getPrivate = function() {
return _private; // will return undefined
}
/* ...several other functions appended via `prototype`...*/
AFTER:
var SomeObj = function (flag) {
var _private = true;
this.flag = (flag) ? true : false;
this.version = "2.0 (constructor)";
this.reset = function () {
/* perform some actions */
};
/* UPDATE */
this.getPrivate = function() {
return _private; // will return true
}
/* other functions and function calls here */
}
对我来说,第一个例子看起来很难阅读,特别是在更大的背景下。像这样使用reset
属性添加像prototype
这样的方法似乎受到的控制要少得多,因为它可能发生在脚本的任何地方。我重构的代码(上面的第二个例子)看起来更整洁,因此它更容易阅读,因为它是独立的。我已经通过变量声明获得了一些隐私,但我已经失去了原型链的可能性。
...
问题:
首先,我有兴趣知道前面prototype
还有什么遗失,或者对原型链的丢失有更大的影响。 This article已有6年历史,但声称使用prototype
属性比封闭模式更有效率。
上述两个示例仍然会由new
运算符实例化;他们都是'经典'建构者。最后我甚至想把它从一个模型中移开到一个模型中,其中所有的属性和函数都被声明为var
并且我有一个方法,我公开它能够返回一个对象,打开所有的属性和方法我需要,具有(通过关闭)特权的那些私有。像这样:
var SomeObj = (function () {
/* all the stuff mentioned above, declared as 'private' `var`s */
/* UPDATE */
var getPrivate = function () {
return private;
}
var expose = function (flag) {
// just returns `flag` for now
// but could expose other properties
return {
flag: flag || false, // flag from argument, or default value
getPrivate: getPrivate
}
};
return {
expose: expose
}
})(); // IIFE
// instead of having to write `var whatever = new SomeObj(true);` use...
var whatever = SomeObj.expose();
StackOverflow上有一些答案可以解决'原型与封闭'问题(例如here和here)。但是,与prototype
属性一样,我感兴趣的是朝着这个方向迈进,远离new
运算符意味着我的代码效率和任何可能性的损失(例如{{1}失去了)。如果我不打算使用原型继承,我实际上在instanceof
运算符前面会丢失任何东西吗?
一个更宽松的问题,如果我被允许,因为我要求上述具体细节:如果new
和prototype
确实是最有效的方式,有更多的优势(无论你认为他们可能是什么,而不是关闭,是否有任何指导方针或设计模式以便以更整洁的方式编写它们?
...
请注意,new
每次都返回一个新对象,因此这是实例化发生的地方。据我所知,该对象引用expose
闭包中声明的方法,它们是所有对象中的相同方法(除非被覆盖)。在SomeObj
变量(我现在已经更正)的情况下,这可以从flag
的参数继承,具有默认值,或者再次参考封装的预先存在的方法或财产。所以有一些对象正在生成,并且还有一些继承(加上多态性?)。
所以重复问题2:如果我不打算继续使用原型继承,那么在expose
运算符前面我是否真的丢失了什么?
非常感谢到目前为止的答案,这有助于澄清我的问题。
答案 0 :(得分:1)
根据我的经验,不使用.prototype
而丢失的唯一内容是内存 - 每个对象最终都拥有自己定义的函数对象的副本。
如果您只打算实例化“小”数量的对象,这可能不是一个大问题。
关于您的具体问题:
关于该链接文章的第二条评论具有高度相关性。作者的基准测试是错误的 - 它测试运行构造函数的开销,也声明了四个内部函数。它不测试这些功能的后续性能。
您的“闭包和公开”代码示例不是OO,它只是一个带有一些封闭私有变量的命名空间。由于它不使用new
,如果您希望从中实例化对象,则没有用。
我无法回答这个问题 - “它取决于”就像你能得到的那样好。
答案 1 :(得分:1)
前面提到
prototype
还有什么遗失?
我确信有人可以提供答案,但我至少会试一试。使用prototype
至少有两个理由:
prototype
方法可以静态使用将方法创建为对象成员意味着它是为对象的每个实例创建的。这是每个对象更多的内存,它减慢了对象的创建速度(因此提高了效率)。人们倾向于说prototype
方法就像类方法,而成员方法就像对象方法,但这是非常误导的,因为原型链中的方法仍然可以使用对象实例。
您可以将原型定义为对象本身,因此您可能更喜欢语法(但并非完全不同):
SomeObj.prototype = {
method1: function () {},
method2: function () {}
}
你的论据似乎不太受控制对我有效。我知道在创建对象时涉及两个块是很奇怪的。然而,有点虚假的是,没有什么能阻止某人覆盖你的其他物体的原型。
//Your code
var SomeObj = function (flag) { //...
//Sneaky person's code
delete SomeObj.reset;
SomeObj.prototype.reset = function () { /* what now? */ }
new
如果你只想通过{}
表示法动态创建特定的对象实例,那么它与使用new
并没有什么不同。您需要使用new
从类(function
)定义创建同一对象的多个实例。这并不罕见,因为它适用于任何面向对象的编程语言,它与重用有关。
对于您当前的应用程序,这可能很有用。但是,如果你想出了一些可以跨上下文重用的强大插件,那么必须重写它会很烦人。我认为您正在寻找类似require.js
的内容,它允许您定义可以使用require
函数导入的“模块”。您在define
函数闭包中定义了一个模块,因此无论如何都可以将构造函数和原型定义包装在一起,除非他们导入该模块,否则没有其他人可以触摸它们。
prototype
它们不是互斥的:
var attachTo = {};
;(function (attachTo, window, document, undefined) {
Plugin = function () { /* constructor */ };
Plugin.prototype = { /* prototype methods */ };
attachTo.plugin = Plugin;
})(attachTo, window, document);
var plugin = new (attachTo.plugin);
答案 2 :(得分:1)
数目:
您已经回答了这个问题:您松散原型链。 (实际上你没有松开它,但你的原型将永远是空的)。结果是:
对性能/内存有一点影响,因为为每个实例创建了方法。但它在很大程度上取决于JavaScript引擎,只有在需要创建大量对象时才应该担心它。
您不能通过修改原型来猴子补丁实例。也不是一个大问题,因为这样做会导致维护噩梦。
让我对你的问题做一个小小的纠正:不是“原型与闭包”的问题,实际上闭包的概念与基于原型的语言是正交的。
问题与你如何创建对象有关:每次从零定义一个新对象,或者从原型中克隆它。
您展示的关于使用函数来限制范围的示例是JavaScript中的常规做法,即使您决定使用原型,也可以继续这样做。例如:
var SomeObj = (function (flag) {
/* all the stuff mentioned above, declared as 'private' `var`s */
var MyObj = function() {}
MyObj.prototype = {
flag: flag,
reset: reset
};
return {
expose: function() { return new MyObj(); }
}
})();
如果您担心模块化,请查看requirejs,它是一种称为AMD(异步模块定义)技术的实现。有些人不喜欢AMD,有些人喜欢它。我对它的体验是积极的:它为浏览器创建模块化JavaScript应用程序帮助了我很多。
有一些图书馆让你的原型生活变得更轻松:composejs,dejavu和我自己的barman(是的,这是一种无耻的自我推销,但你可以看一下进入源代码,看看处理对象定义的方法)。
关于模式:由于您可以使用工厂方法轻松隐藏对象实例化,因此您仍然可以使用new
在内部克隆原型。
答案 3 :(得分:0)
问题提问:
基本上,在reset
中使用prototype
方法意味着构造函数的所有实例都将共享该方法的完全相同的副本。通过在构造函数中创建一个本地方法,您将为每个实例提供一个方法副本,这将消耗更多内存(如果您有大量实例,这可能会成为一个问题)。除此之外,两个版本都是相同的;将function SomeObj
更改为var SomeObj = function
只会因其父作用域中SomeObj
的提升方式而有所不同。你说你已经通过变量声明和#34;获得了一些隐私,但我没有看到任何私有变量......
根据您提到的IIFE方法,您将无法检查是否instance instanceof SomeObj
。
不确定这是否回答了您的问题,但还有Object.create
,您仍然可以设置原型,但摆脱new
关键字。但是,你失去了拥有构造函数的能力。