Javascript 仅在当前范围内扩展基元

Javascript 仅在当前范围内扩展基元,javascript,constructor,prototype,primitive,Javascript,Constructor,Prototype,Primitive,是否可以复制像String这样的原语,以便它只能在当前函数中扩展 var foo = function() { var String = // somehow clone String (the primitive function) String.prototype.first = function() { return this.charAt(0) }; return 'abc'.first(); } foo(); // returns 'a' 'abc'.first

是否可以复制像
String
这样的原语,以便它只能在当前函数中扩展

var foo = function() {
    var String = // somehow clone String (the primitive function)
    String.prototype.first = function() { return this.charAt(0) };
    return 'abc'.first();
}
foo(); // returns 'a'
'abc'.first(); // returns TypeError "abc".first is not a function
解决方案必须要么复制看似不可能的原语,要么在函数完成后销毁新方法

String.define = function(name, fn) {
    // Don't overwrite existing method
    if(String.prototype[name]) return false;

    // Make new method
    Object.defineProperty(String.prototype, name, {
        value: fn,
        configurable: true,
        enumerable: false,
        writable: false
    });

    // Delete method once parent function returns
    setTimeout(function() { 
        delete String.prototype[name] 
    }, 0);
}

var foo = function() {
    String.define('first', function() { 
        return this.charAt(0); 
    });
    return 'abc'.first();
}

foo(); // returns 'a'
'abc'.first(); // returns TypeError
这是通过为String(全局)创建方法并仅在包含该方法的函数完成时删除该方法来实现的。有效地使其仅存在于该范围内。我不知道在foo()处理时执行的异步代码是否可以访问String.prototype.first,但测试

编辑:仔细看了一下,这不起作用。删除原型方法的setTimeout仅在最外层父函数返回时运行。 在此代码段中,对“.first”的第二次调用在其上方的函数之外工作

var foo = function() {
    console.log((function () {
        String.define('first', function() {
            return this.charAt(0);
        });
        return 'abc'.first();
    })());
    console.log('abc'.first())
}

String
是一个全局变量,如果您更改它或添加到它的原型中,那么根据定义更改是全局变量。因此,要恢复更改,您必须恢复它们

通常情况下,人们会尝试派生一个自定义子类并向其添加新的原型方法,但众所周知,像String这样的内置类型很难生成子类,即使可以,也必须使用类似于
MyString('abc').first()的方法调用它

您的问题源于您错误地扩充
字符串
原型的想法污染原型从来都不是一个好主意。我理解你的想法,在添加并使用原型后,以某种方式快速删除原型,但这并不现实,
setTimeout
方法是一种可怕的、不可靠的黑客行为

但是,如果您真的坚持要添加到
字符串
原型中,那么一个想法是将所有新实例方法添加到一个不太可能与事物发生冲突的属性中。让我们称之为
Utils
。我们希望能够打电话

'abc'.Utils.first()
但是,我们如何才能使这项工作?我们在
Utils
属性上定义了一个getter,它返回方法的散列。使用getter(而不是
value
)为我们提供了
this
,我们可以将其传递给使用
bind
的方法

Object.defineProperty(String.prototype, 'Utils', { 
  get: function() {
    return {
      first: function() { return this.charAt(0); }.bind(this)
    };
  }
});

> 'abc'.Utils.first()
< "a"
Object.defineProperty(String.prototype,'Utils',{
get:function(){
返回{
第一个:函数(){返回this.charAt(0);}.bind(this)
};
}
});
>“abc”.Utils.first()
<“a”

为什么要扩展
String.prototype
?最简单的方法是只创建一个本地函数:

警报(foo());//“a”
警报(第一(“abc”);//ReferenceError:未定义第一个
函数foo(){
先返回(“abc”);
函数优先(字符串){
返回字符串.charAt(0);
}

}
顺便说一下
可配置
enumerable
writable
都有合适的默认值,可以不指定。这不符合预期,因为
setTimeout
函数
foo
返回后不会立即调用:。@AaditMShah为什么要重复我在自己的帖子中已经撤回的内容?这是一个非常不雅的攻击。此外,这如何解决OP的问题?您仍然可以在
foo
函数之外调用
“abc.Utils.first()
。是否错过了今早的第一杯咖啡?我不会称之为“黑客”;这只是将一些原型方法分组到一种名称空间下的一种方法,因此它们不太可能与其他方法冲突。也许我没有足够清楚地表明,我没有试图准确地实现OP声称想要的东西,因为这介于不可能和不可取之间。我是在回答OP的问题,即以某种更安全的方式添加到<代码>字符串<代码>原型的要求。如果你不喜欢,就投反对票。哦,等等,你已经这样做了。好吧,如果你只是想要一个名称空间,那么
var Utils={first:function(string){return string.charAt(0);}?为什么你想污染
String.prototype
,让事情变得比需要的更复杂?以我的拙见,使用getter是一种过分的做法。我知道您感觉到了OP的潜在问题(安全地将方法添加到本机原型中),并试图解决这个问题。但是,没有必要这样做。我们已经有了一个惯例:除非您是一个功能,否则不要将方法添加到本机原型中。如果您只是添加一条注释,说明不鼓励将方法添加到本机原型中,我不介意删除我的否决票。@AaditMShah我会这样做。我本人也反对污染本机原型。当您不得不添加修改原语类型这一极易出错的行为时,您将失去使用扩展方法的可读性优势。只需使用一个局部函数。