3

只是一个小的原型继承问题
最近我试图创建一个自定义方法,例如: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

问题是,似乎 Method.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.htmlnew F( arguments )和许多其他类似的东西,但它们中的大多数都专注于使用创建一个使用特定参数的东西,这不是我的情况。感谢您的任何解释

4

2 回答 2

4

您不必将方法添加到每个对象——只需添加您正在使用的对象类型。如果它是一个字符串方法,您可以将它添加到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 中解决。情况正在好转!

于 2013-01-27T23:12:58.477 回答
0

您也应该使用 new 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!
于 2013-01-27T22:38:45.227 回答