前段时间我试图扩展Object.prototype
...后来我在控制台看到来自jQuery文件的错误时感到很惊讶。我试图弄清楚出了什么问题,当然我发现延长Object.prototype
的信息是一个邪恶的","你不应该这样做因为JS是动态语言而且你的代码不会很快工作"以及jQuery现在将hasOwnProperty
方法添加到其for in
循环的信息。
因为我不想离开jQuery,所以我放弃了扩展Object.prototype
的想法。
直到现在。我的项目越来越大,我真的很烦,因为我必须重复多次代码的某些部分。下面是我在项目中使用的一些结构:
charts.js:
CHARTS = {
_init: function () {
this.monthlyChart();
/*
*
* more propertys goes here
*
*/
return this;
},
monthlyChart: function () {
//create my chart
return {
update: function () {
// update chart
}
};
}()
/*
*
* more propertys goes here
*
*/
}._init;
dashboard.js
NAVBAR = {
_init: function () {
/*
*
* more propertys goes here
*
*/
return this;
},
doSomething: function(){
$(document).ready(function(){
$('.myButton').on('click', function(){
var data = [];
// calling property from charts.js
CHARTS.monthlyChart.update(data);
});
});
}
}._init
正如我所提到的,项目现在非常大 - 它有超过40个js文件,其中一些有几千行代码。我每次都要重复_init
部分,以及我必须重复的许多功能$(document).ready
&& $(window).load
。
我试图为我的问题找到另一种解决方案。我尝试使用init
属性(more you can find here)创建类,但我这个解决方案迫使我添加另一个"不必要的"每个文件的代码片段和访问其他文件对象属性也使它变得复杂(到处返回适当的对象等)。正如评论中所建议的,我开始阅读JS中的getter和setter。
毕竟我创造了类似的东西:
//Auto initialization
if (typeof $document === 'undefined') {
var $document = $(document),
$window = $(window),
$body = $('body');
}
Object.defineProperty(Object.prototype, '_init', {
get: function () {
// if object has no property named `_init`
if (!this.hasOwnProperty('_init')) {
for (var key in this) {
// checking if name of property does starts from '_' and if it is function
if (this.hasOwnProperty(key) && key[0] === '_' && typeof this[key] === 'function') {
if (key.indexOf('_ready_') > -1) {
//add function to document ready if property name starts from '_ready_'
$document.ready(this[key].bind(this));
} else if (key.indexOf('_load_') > -1) {
//add function to window load if property name starts from '_load_'
$window.load(this[key].bind(this));
} else {
// else execute function now
this[key].bind(this)();
}
}
}
return this;
}
}
});
和我的对象:
var DASHBOARD = {
_runMe: function(){
},
_ready_runMeOnReady: function(){
},
_load_runMeOnLoad: function(){
},
iAmAString: ''
}._init
似乎这个解决方案适用于jQuery。但使用安全吗?我没有看到代码可能导致的任何问题,我也没有看到它可能导致的任何进一步问题。我会非常高兴如果有人会告诉我为什么我不应该使用这个解决方案。
此外,我还试图了解详细信息。从理论上讲,我通过Object.prototype
为defineProperty
定义了属性,而没有为其赋值。不知怎的,它不会导致jQuery fore in
循环中的任何错误,为什么?这是否意味着属性_init
在某个时刻或根本没有被定义,因为我只定义了它的吸引力?
任何帮助将不胜感激:)
答案 0 :(得分:3)
通过不在Object.defineProperty(obj, prop, descriptor)
中包含描述符,JavaScript默认将所有布尔描述符属性设置为false。亦即
writable
,enumerable
和configurable
。您的新属性对于for in
迭代器是隐藏的,因为您的_init
属性为enumerable:false
。
我不是JQuery的粉丝所以不会评论为什么关于JQuery
向JavaScript的基本类型添加属性没有绝对的规则,这取决于代码运行的环境。添加到基本类型会将其添加到全局命名空间。如果您的应用程序与第三方脚本共享命名空间,则可能会发生冲突,导致您的代码或第三方代码或两者都失败。
如果您是唯一的代码,则冲突不会成为问题,但添加到object.prototype会对使用object的所有代码产生额外开销。
我强烈建议您重新审视对全局_init的需求。当你每次需要一个新物体时,你肯定不会使用它。我是JavaScript数据结构的添加方法的粉丝,并试图远离正式的OOP范例
答案 1 :(得分:3)
你的问题实际上包含两个问题。
它认为此解决方案适用于jQuery。但是使用是否安全?我没有看到代码可能导致的任何问题,我也没有看到它可能导致的任何进一步问题。如果有人告诉我为什么我不应该使用这个解决方案,我会很高兴。
首先,避免修改内置原型有三个主要原因。
使用for-in
循环而没有hasOwnProperty
检查的代码太多了。在您的情况下,jQuery代码不执行检查。
for-in
循环而不进行.hasOwnProperty
检查。
for-in
循环仅遍历可枚举键。
Object.defineProperty
默认情况下会创建不可枚举的属性(ECMAScript 5.1 specification)
存在财产名称的风险。想象一下,你使用jQuery插件检查对象上是否存在._init
属性 - 它可能导致微妙且难以调试的错误。带有下划线的名称在现代JavaScript库中广泛用于指示私有属性。
但是你的问题更严重。定义全局._init
属性表明每个对象都具有通用初始化逻辑。它破坏了封装,因为你的对象无法完全控制其状态。
由于此原因,您无法依赖_init
方法的存在。你的同事不能用
您可以创建全局函数initialize
并包装所有需要初始化的对象。
您的对象不应该在一个对象中合并逻辑和视图(它违反了单一责任原则),并且您是意大利面条代码的受害者。 此外 - 对象初始化不应该将它绑定到DOM,某些控制器对象应该是逻辑和显示之间的代理。
检查流行的客户端MVC框架如何解决这个问题(Angular,Ember,Backbone)已经解决了这个问题,这是个好主意。
答案 2 :(得分:1)
使用getter和setter是否安全?
是。但是如果你只支持IE9 +。
修改Object.prototype是否安全?
没有。创建另一个对象以继承所有应用程序对象。
为什么扩展基本的JavaScript对象是 eval evil?
因为在加载脚本的网页上创建的每个SINGLE对象都将继承该属性或方法。 如果你这样做的话,有很多缺点,如冲突和性能开销。
有很多方法可以让它变得更好,让我告诉你我使用的那个。
// Here we create the base object:
var someBaseObject = {};
someBaseObject.someMethod = function () {
// some code here
}
someBaseObject.someProperty = "something";
// And inherit another object from the someBaseObject
someObject = Object.create(someBaseObject);
someObject.someAnotherMethod = function () {
// some code here
}
这种方法允许我们单独留下Object原型,并构建一个原型链,其中someObject继承自someBaseObject,someBaseObject继承自Object。
这篇文章我唯一想说的是:单独留下基础对象并构建自己的对象,这样你就不那么容易头痛了。
注意:IE9 +支持Object.create
。这是IE8的垫片,低于Douglas Crockford:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}