使用 jQuery 时扩展 object.prototype 的替代方法
Alternative methods for extending object.prototype when using jQuery
前段时间我尝试扩展 Object.prototype
... 后来我在控制台中看到来自 jQuery 文件的错误时,我感到很惊讶。我试图找出问题所在,当然我发现扩展 Object.prototype
是 "evil"、"you shouldn't do that because JS is dynamic language and your code will not work soon" 的信息以及 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) 创建 class 但我这个解决方案迫使我向每个文件添加另一个 "unnecessary" 代码段并访问其他文件object 属性 也让它变得复杂(return proper objects 无处不在等)。正如评论中所建议的那样,我开始阅读有关 JS 中的 getters 和 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。但是使用安全吗?我没有看到代码可能导致的任何问题,也没有看到它可能导致的任何其他问题。如果有人能告诉我为什么我不应该使用这个解决方案,我会很高兴。
我也在尝试了解它是如何工作的细节。从理论上讲,我通过 defineProperty
为 Object.prototype
定义了 属性,但没有为其赋值。不知何故它不会在 jQuery fore in
循环中引起任何错误,为什么?这是否意味着 属性 _init
在某些时候或根本没有定义,因为我只定义了它的 getter?
任何帮助将不胜感激:)
通过不在 Object.defineProperty(obj, prop, descriptor)
中包含描述符,JavaScript 将所有布尔描述符属性默认为 false。即
writable
、enumerable
和 configurable
。你的新 属性 对 for in
迭代器是隐藏的,因为你的 _init
属性 是 enumerable:false
.
我不是 JQuery 的粉丝所以不会评论 JQuery
的原因
向 JavaScript 的基本类型添加属性没有绝对规则,将取决于您的代码所处的环境 运行。添加到基本类型会将其添加到全局命名空间。如果您的应用程序与第三方脚本共享命名空间,您可能会发生冲突,导致您的代码或第三方代码或两者都失败。
如果您是唯一的代码,那么冲突将不是问题,但是添加到 object.prototype 将导致所有使用对象的代码的额外开销。
我强烈建议您重新检查是否需要全局 _init。当然,您不会在每次需要新对象时都使用它。我是 JavaScript 数据结构的 add hock 方法的粉丝,并尽量远离正式的 OOP 范例
使用 getter 和 setter 安全吗?
是的。但是如果你只支持IE9+.
修改Object.prototype安全吗?
没有。创建另一个对象以继承您的所有应用程序对象。
为什么扩展基本 JavaScript 对象是 eval 邪恶的?
因为在加载脚本的网页上创建的每个对象都将继承该 属性 或方法。
如果你这样做,会有很多缺点,比如碰撞和性能开销。
有很多方法可以让它变得更好,让我告诉你我使用的一种。
// 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。
我在此 post 中唯一想说的是:不要理会基础对象并构建您自己的对象,这样您就不会那么头疼了。
注意:Object.create
在 IE9+ 中受支持。这是 IE8 的垫片,降低 Douglas Crockford:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
你的问题实际上包含两个问题。
It seams that this solution works with jQuery. But is it safe to use? I don't see any problem the code can cause and I don't see any further problems that it may cause. I will be really happy if somebody will tell me why I shouldn't use this solution.
首先,避免修改内置原型主要有以下三个原因。
For-in 循环
有太多代码使用 for-in
循环而没有 hasOwnProperty
检查。在您的情况下,jQuery 代码不执行检查。
解决方案
- 不要在没有
.hasOwnProperty
检查的情况下使用 for-in
循环。
- 在这种情况下不适用,因为它是第三方代码,您无法修改它。
for-in
循环仅遍历 enumerable 键。
- 您已经使用了该解决方案。
Object.defineProperty
默认创建不可枚举的属性 (ECMAScript 5.1 specification)
- IE8 不支持。
冲突
属性 名称存在风险。想象一下,您使用 jQuery 插件来检查对象上是否存在 ._init
属性 - 它可能会导致微妙且难以调试的错误。带有下划线前缀的名称在现代 JavaScript 库中广泛用于指示私有属性。
封装违规(糟糕的设计)
但是你的问题更严重。定义 global ._init
属性 表明每个对象都有通用的初始化逻辑。它破坏了封装,因为您的对象无法完全控制它们的状态。
因此,您不能依赖 _init
方法的存在。您的同事无法使用
实现他们自己的 class
替代设计
全局初始化程序
您可以创建全局函数 initialize
并将所有需要初始化的对象包装在其中。
解耦视图和逻辑
你的对象不应该在一个对象中合并逻辑和视图(它违反了单一责任原则),你是意大利面条代码的受害者。
此外 - 对象初始化不应将其绑定到 DOM,一些控制器对象应该是您的逻辑和显示之间的代理。
检查流行的客户端 MVC 框架如何解决这个问题(Angular、Ember、Backbone)是个好主意。
前段时间我尝试扩展 Object.prototype
... 后来我在控制台中看到来自 jQuery 文件的错误时,我感到很惊讶。我试图找出问题所在,当然我发现扩展 Object.prototype
是 "evil"、"you shouldn't do that because JS is dynamic language and your code will not work soon" 的信息以及 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) 创建 class 但我这个解决方案迫使我向每个文件添加另一个 "unnecessary" 代码段并访问其他文件object 属性 也让它变得复杂(return proper objects 无处不在等)。正如评论中所建议的那样,我开始阅读有关 JS 中的 getters 和 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。但是使用安全吗?我没有看到代码可能导致的任何问题,也没有看到它可能导致的任何其他问题。如果有人能告诉我为什么我不应该使用这个解决方案,我会很高兴。
我也在尝试了解它是如何工作的细节。从理论上讲,我通过 defineProperty
为 Object.prototype
定义了 属性,但没有为其赋值。不知何故它不会在 jQuery fore in
循环中引起任何错误,为什么?这是否意味着 属性 _init
在某些时候或根本没有定义,因为我只定义了它的 getter?
任何帮助将不胜感激:)
通过不在 Object.defineProperty(obj, prop, descriptor)
中包含描述符,JavaScript 将所有布尔描述符属性默认为 false。即
writable
、enumerable
和 configurable
。你的新 属性 对 for in
迭代器是隐藏的,因为你的 _init
属性 是 enumerable:false
.
我不是 JQuery 的粉丝所以不会评论 JQuery
的原因向 JavaScript 的基本类型添加属性没有绝对规则,将取决于您的代码所处的环境 运行。添加到基本类型会将其添加到全局命名空间。如果您的应用程序与第三方脚本共享命名空间,您可能会发生冲突,导致您的代码或第三方代码或两者都失败。
如果您是唯一的代码,那么冲突将不是问题,但是添加到 object.prototype 将导致所有使用对象的代码的额外开销。
我强烈建议您重新检查是否需要全局 _init。当然,您不会在每次需要新对象时都使用它。我是 JavaScript 数据结构的 add hock 方法的粉丝,并尽量远离正式的 OOP 范例
使用 getter 和 setter 安全吗?
是的。但是如果你只支持IE9+.
修改Object.prototype安全吗?
没有。创建另一个对象以继承您的所有应用程序对象。
为什么扩展基本 JavaScript 对象是 eval 邪恶的?
因为在加载脚本的网页上创建的每个对象都将继承该 属性 或方法。 如果你这样做,会有很多缺点,比如碰撞和性能开销。
有很多方法可以让它变得更好,让我告诉你我使用的一种。
// 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。
我在此 post 中唯一想说的是:不要理会基础对象并构建您自己的对象,这样您就不会那么头疼了。
注意:Object.create
在 IE9+ 中受支持。这是 IE8 的垫片,降低 Douglas Crockford:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
你的问题实际上包含两个问题。
It seams that this solution works with jQuery. But is it safe to use? I don't see any problem the code can cause and I don't see any further problems that it may cause. I will be really happy if somebody will tell me why I shouldn't use this solution.
首先,避免修改内置原型主要有以下三个原因。
For-in 循环
有太多代码使用 for-in
循环而没有 hasOwnProperty
检查。在您的情况下,jQuery 代码不执行检查。
解决方案
- 不要在没有
.hasOwnProperty
检查的情况下使用for-in
循环。- 在这种情况下不适用,因为它是第三方代码,您无法修改它。
for-in
循环仅遍历 enumerable 键。- 您已经使用了该解决方案。
Object.defineProperty
默认创建不可枚举的属性 (ECMAScript 5.1 specification)- IE8 不支持。
- 您已经使用了该解决方案。
冲突
属性 名称存在风险。想象一下,您使用 jQuery 插件来检查对象上是否存在 ._init
属性 - 它可能会导致微妙且难以调试的错误。带有下划线前缀的名称在现代 JavaScript 库中广泛用于指示私有属性。
封装违规(糟糕的设计)
但是你的问题更严重。定义 global ._init
属性 表明每个对象都有通用的初始化逻辑。它破坏了封装,因为您的对象无法完全控制它们的状态。
因此,您不能依赖 _init
方法的存在。您的同事无法使用
替代设计
全局初始化程序
您可以创建全局函数 initialize
并将所有需要初始化的对象包装在其中。
解耦视图和逻辑
你的对象不应该在一个对象中合并逻辑和视图(它违反了单一责任原则),你是意大利面条代码的受害者。 此外 - 对象初始化不应将其绑定到 DOM,一些控制器对象应该是您的逻辑和显示之间的代理。
检查流行的客户端 MVC 框架如何解决这个问题(Angular、Ember、Backbone)是个好主意。