正确的方法来创建自定义JS方法

时间:2013-01-27 22:10:57

标签: javascript oop object methods prototype

只是一个小型的原型继承问题 最近我试图创建一个自定义方法jut,例如:JS的.toUpperCase()和其他方法......使用前缀对象的this引用。

它很好用(愚蠢无用的例子)

Object.prototype.customMethod = function(){
    return this.toLowerCase() ; 
};

可以像:

一样使用
// access some object and get a key value
member.name; // JOHN
// use custom Method
member.name.customMethod(); // john

问题是,似乎方法.customMethod()在每个Object全局继承。
如何减少干扰并仅引用前缀对象?还是根本?

以下是一个示例: http://jsbin.com/evaneg/2/edit

// CREATE OBJECT
var obj = { "text" : "Hi" };

// TEST OBJECT KEY VALUE
alert( "OBJECT TEXT: "+ obj.text );     // Hi


// CREATE CUSTOM METHOD ( just like e.g. JS's .toUpperCase() method )
Object.prototype.addText = function(){ 
  return this+' there!';  
};

// USE CUSTOM .addText() METHOD
alert( "OBJECT TEXT+method: "+obj.text.addText() ); // Hi there! // wow, the method works!


for(var key in obj){
  alert( 'obj KEYS: '+ key ); // text // addText
}

// hmm... addText method as obj Key ???
// ok let's try with a new one...

var foobee = { "foo" : "bee" };

for(var key in foobee){
  alert( 'foobee KEYS: '+ key ); // foo // addText
}

// addText  ...again... but why?

我在SOverflow上也阅读了这个http://javascript.crockford.com/prototypal.html和许多其他类似的东西,但是大多数都专注于使用创建使用特定参数的new F( arguments ),这不是我的情况。谢谢你的任何解释

2 个答案:

答案 0 :(得分:3)

您不必将方法添加到每个对象 - 只是您正在使用的对象类型。如果是字符串方法,您可以将其添加到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());

请注意,这确实会使方法可枚举,这意味着它们会显示在for..in循环中。


Object.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时,Object.defineProperty不是真正的数组。


简化锅炉

使用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); } })(); 提供了很多功能,但它还需要大量额外的代码,大多数人不愿意一直输入。当ECMAScript委员会定义了这个函数时就明白了,但是他们假设人们可以编写帮助函数来使代码更清晰。记住这一点。例如,您可以始终执行以下操作:

define(String, 'addText', function() {
    return this + ' there!';
});

console.log('Hi'.addText()); // => "Hi there!"

然后你可以这样做:

Object.defineProperty

甚至还开发了一些小型库来帮助解决这些问题。以Andrea Giammarchi's redefine.js为例。


注意

唯一真正的警告是,如果您将自己的方法添加到内置插件中,则可能会与(a)未来版本的JavaScript或(b)其他脚本发生名称冲突。 (名称冲突问题正在使用符号的JavaScript的下一个版本中得到解决。)许多人认为这是一个足够大的问题,你不应该修改内置函数但应该坚持使用只修改你“拥有”的东西 - 你用你自己的构造函数创建的东西。我倾向于在某些(或许多)情况下达成一致,但我认为,对于您自己的个人使用和学习,使用内置原型可能是一件非常有帮助且有趣的事情。

查看这篇关于权衡修改内置插件的一些成本的文章,以便更详细地描述可能存在的缺陷:http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/请注意,这篇文章有点过时(1年半),其中一个他的主要抱怨是可枚举性事项,在所有现代浏览器中都可以用{{1}}来克服。正如我所说,其他主要问题(名称冲突)将在下一版本的JavaScript中得到解决。情况正在好转!

答案 1 :(得分:0)

你也应该使用新的F()。你只需要定义F.让我们调用F'名称',但它更容易识别。

var Name = function(){
    var newName = {
        text: 'Hello',
        customMethod: function(){
            this.text = this.text.toUpperCase();
        }
    }
    return newName;
}

var Member = function(){
    var newMember = {
        name = new Name(),
        customMethod: function(){
        this.name.customMethod(); // make a member invoke upperCase on name (or any number of vars)
        }
    }
    return newMember;
}

var member = new Member();  //now when you run new Member, it runs the function above. and automatically makes your object with it's properties and methods.

console.log(member.name.text); //Hello!
member.customMethod();
// or
member.name.customMethod();
console.log(member.name.text); //HELLO!