您不必将方法添加到每个对象——只需添加您正在使用的对象类型。如果它是一个字符串方法,您可以将它添加到String.prototype
所有字符串中并对其进行定义。
var obj = { "text" : "Hi" };
// We only want to add this method to strings.
String.prototype.addText = function(){
return this+' there!';
};
alert("OBJECT TEXT+method: " + obj.text.addText());
请注意,这确实使方法enumerable,这意味着它们显示在一个for..in
循环中。
对象.defineProperty
如果您只关心自 2010 年以来支持的浏览器(没有 IE8),那么我强烈建议您使用Object.defineProperty
来定义属性,这样它们就不会被枚举:
var obj = { foo: 'bar' };
// Extending all objects this way is likely to break other scripts...
Object.prototype.methodA = function() { return 'A'; };
// Instead you can do extensions this way...
Object.defineProperty(Object.prototype, 'methodB', {
value: function() { return 'B'; },
// Prevent the method from showing up in for..in
enumerable: false,
// Allow overwriting this method.
writable: true,
// Allow reconfiguring this method.
configurable: true
});
for (var propertyName in obj) {
console.log(propertyName);
// Logs: "foo" and "methodA" but not "methodB"
}
上面的代码记录了“foo”属性和“methodA”属性,但它没有记录“methodB”属性,因为我们将它定义为不可枚举。您还会注意到“toString”、“valueOf”、“hasOwnProperty”等内置方法也没有出现。这是因为它们也被定义为不可枚举的。
这样其他脚本将被允许for..in
自由使用,一切都应该按预期工作。
回到特定的内置插件
我们也可以在特定类型的对象上定义不可枚举的方法Object.defineProperty
。例如,以下contains
向所有数组添加了一个方法,true
如果数组包含一个值,false
如果它不包含,则该方法将返回:
Object.defineProperty(Array.prototype, 'contains', {
value: (function() {
// We want to store the `indexOf` method so that we can call
// it as a function. This is called uncurrying `this`.
// It's useful for ensuring integrity, but I'm mainly using
// it here so that we can also call it on objects which aren't
// true Arrays.
var indexOf = Function.prototype.call.bind(Array.prototype.indexOf);
return function(value) {
if (this == null)
throw new TypeError('Cannot be called on null or undefined.');
return !!~indexOf(this, value);
}
})(),
enumerable: false,
writable: true,
configurable: true
});
var colors = [ 'red', 'green', 'blue', 'orange' ];
console.log(colors.contains('green')); // => true
console.log(colors.contains('purple')); // => false
请注意,由于我们在 上定义了此方法Array.prototype
,因此它仅适用于数组。它在其他对象上不可用。但是,本着其他数组方法的精神,它的编写具有足够的通用性,可以在类似数组的对象上调用它:
function foo() {
Array.prototype.contains.call(arguments, 5);
}
console.log(foo(1, 2, 3, 4, 5)); // => true
console.log(foo(6, 7, 8, 9, 10)); // => false
调用contains
上面的arguments
作品,事件虽然arguments
不是一个真正的数组。
简化样板
UsingObject.defineProperty
提供了很大的功能,但它也需要很多额外的代码,大多数人宁愿不要一直输入这些代码。ECMAScript 委员会在定义函数时就理解了这一点,但他们认为人们可以编写辅助函数来使代码更清晰。请记住这一点。例如,您始终可以执行以下操作:
var define = (function() {
// Let's inherit from null so that we can protect against weird situations
// like Object.prototype.get = function() { };
// See: https://mail.mozilla.org/pipermail/es-discuss/2012-November/026705.html
var desc = Object.create(null);
desc.enumerable = false;
desc.writable = true;
desc.configurable = true;
return function define(constructor, name, value) {
if (typeof constructor != 'function'
|| !('prototype' in constructor))
throw new TypeError('Constructor expected.');
desc.value = value;
Object.defineProperty(constructor.prototype, name, desc);
}
})();
然后你可以这样做:
define(String, 'addText', function() {
return this + ' there!';
});
console.log('Hi'.addText()); // => "Hi there!"
甚至已经开发了一些小型库来帮助解决其中的一些问题。查看Andrea Giammarchi 的 redefine.js作为示例。
警告
这里唯一真正要注意的是,如果您将自己的方法添加到内置函数中,则可能会与 (a) JavaScript 的未来版本或 (b) 其他脚本发生名称冲突。(名称冲突问题将在下一版本的 JavaScript 中使用symbols解决。)许多人会认为这是一个足够大的问题,你不应该修改内置函数,而应该坚持只修改你“拥有的东西” " -- 你用自己的构造函数创建的东西。在某些(或许多)情况下,我倾向于同意,但我认为对于您自己的个人使用和学习,使用内置原型可能是一件非常有用和有趣的事情。
查看这篇关于权衡修改内置插件的一些成本的文章,以更详细地描述可能的缺点:http ://perfectionkills.com/extending-built-in-native-objects-evil-or-not/ 请注意这篇文章有点过时了(1 1/2 年),他的主要抱怨之一是可枚举性问题,这Object.defineProperty
在所有现代浏览器中都可以克服。正如我所说,另一个主要问题(名称冲突)将在下一版本的 JavaScript 中解决。情况正在好转!