【问题标题】:Adding methods to Object / Number / String prototype向 Object / Number / String 原型添加方法
【发布时间】:2013-08-22 20:28:22
【问题描述】:

免责声明

  • 这个帖子应该为遇到类似问题的其他人提供帮助,以及检查是否有更好的解决方案。我将附上我自己的解决方案,但欢迎提出想法和改进(除了使其更通用)。
  • 我知道,一般来说,扩展内置对象是个坏主意。因此,此线程的一个假设是有充分的理由并且没有办法绕过它。

场景

作为开发人员,我想为所有 Javascript 对象添加一个方法someMethod,其中ObjectNumberString 的实现是不同的。

我希望解决方案满足以下验收标准:

  • A) 该解决方案适用于浏览器
    • A1) 解决方案在严格模式下工作,以防脚本在严格上下文中使用
    • A2) 该解决方案在非严格模式下工作,因为'use strict'; 将在压缩过程中被删除,例如YUI Compressor[1]
  • B) 该解决方案适用于node.js
    • B1) 解决方案在严格模式下工作(原因参见 A1)
    • B2) 该解决方案在非严格模式下工作,原因与 B2 中相同,而且 node.js 中的严格模式无法在功能级别上激活[2]
  • C) 我希望允许其他对象覆盖此方法
  • D) 如果可能,我希望控制该方法是否显示在 for .. in 循环中,以避免与其他库发生冲突
  • E) 解决方案实际上应该修改原型。

[1]Minfication removes strict directives
[2]Any way to force strict mode in node?

【问题讨论】:

    标签: javascript


    【解决方案1】:

    我自己的解决方案

    在尝试解决这个问题时,我遇到了一些问题,导致一个或另一个接受标准被打破(例如 [1] 中描述的问题)。一段时间后,我想出了以下似乎对我有用的解决方案。当然,这可以写成更通用的方式。

    (function () {
        'use strict';
    
        var methodName = 'someMethod',
            /** Sample method implementations */
            __someMethod = {
                'object': function () {
                    var _this = this.valueOf();
    
                    return ['Object'].concat( Array.prototype.slice.call( arguments ) );
                },
    
                'number': function () {
                    var _this = this.valueOf();
    
                    return ['Number'].concat( Array.prototype.slice.call( arguments ) );
                },
    
                'string': function () {
                    var _this = this.valueOf();
    
                    return ['String'].concat( Array.prototype.slice.call( arguments ) );
                },
    
                'boolean': function () {
                    var _this = this.valueOf();
    
                    return ['Boolean', _this];
                }
            };
    
        if( Object.defineProperty ) {
            Object.defineProperty( Number.prototype, methodName, {
                value: __someMethod['number'],
                writable: true
            } );
    
            Object.defineProperty( String.prototype, methodName, {
                value: __someMethod['string'],
                writable: true
            } );
    
            Object.defineProperty( Boolean.prototype, methodName, {
                value: __someMethod['boolean'],
                writable: true
            } );
    
            Object.defineProperty( Object.prototype, methodName, {
                value: __someMethod['object'],
                writable: true
            } );
        } else {
            Number.prototype[methodName] = __someMethod['number'];
            String.prototype[methodName] = __someMethod['string'];
            Boolean.prototype[methodName] = __someMethod['boolean'];
            Object.prototype[methodName] = __someMethod['object'];
        }
    })(); 
    

    编辑:我更新了解决方案,为 [1] 中提到的问题添加了解决方案。即它是行(例如)var _this = this.valueOf();。如果使用,原因就很清楚了

    'number': function (other) {
        return this === other;
    }
    

    在这种情况下,你会得到

    var someNumber = 42;
    console.log( someNumber.someMethod( 42 ) ); // false
    

    这当然不是我们想要的(同样,原因在 [1] 中说明)。所以你应该使用_this 而不是this

    'number': function (other) {
        var _this = this.valueOf();
        return _this === other;
    }
    
    // ...
    
    var someNumber = 42;
    console.log( someNumber.someMethod( 42 ) ); // true
    

    [1]Why does `typeof this` return "object"?

    【讨论】:

    • 我不明白这一点。您只需将方法添加到.prototype,并尽可能使属性不可枚举和不可配置。我错过了什么?
    • 对不起——我确实错过了一件事;即[1]中提到的内容。除此之外,不,你没有错过任何东西。但是由于提到的所有可能性,在浏览器或 nodejs 中被破坏之前,我有很多类似的版本。
    • 嗯,好的。无论如何,当不支持 Object.defineProperty 而不是仅仅做 Number.prototype[methodName] = __someMethod['number'] 时,您是否有理由添加包装函数?
    • 是和不是。曾经有一个,建立在一个错误的信念之上,并且永远不会再改变(this 将引用错误的对象)。我会调整解决方案(看,这就是我在这里发布它的原因:))。
    • 尝试为Boolean.prototype 做同样的事情我发现需要稍微改变方法。而不是做var _this = Number( this ),它不适用于Boolean,需要使用var _this = this.valueOf()。我会更新答案。
    【解决方案2】:

    创建一个包装对象(注意这只是一个例子,它不是很健壮):

    var $ = (function(){
      function $(obj){
        if(!(this instanceof $))
            return new $(obj);
    
        this.method = function(method){
            var objtype = typeof obj;
            var methodName = method + objtype[0].toUpperCase() + objtype.substr(1);
            typeof _$[methodName] == 'function' && _$[methodName].call(obj);
        }
      }
    
      var _$ = {};
    
      _$.formatNumber = function(){
        console.log('Formatting number: ' + this);
      }
    
      _$.formatString = function(){
        console.log('Formatting str: "' + this + '"');
      }
    
      _$.formatObject = function(){
        console.log('Formatting object: ');
        console.log(JSON.stringify(this));
      }
    
      return $;
    })();
    

    用法:

    var num = 5;
    var str = 'test';
    var obj = {num: num, str: str};
    
    var $num = $(num);
    $num.method('format');
    
    $(str).method('format');
    $(obj).method('format');
    

    Demo

    【讨论】:

    • 是的,它有效——但它忽略了问题的重点:修改原型。我想我应该更清楚并将其添加为标准,即使它在假设中也是如此。 (编辑:完成)。
    猜你喜欢
    • 1970-01-01
    • 2020-09-13
    • 2014-05-26
    • 2019-01-17
    • 2016-09-03
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 2015-10-16
    相关资源
    最近更新 更多