问题
Is it possible to copy a primitive like String
so it can be extended only in the current function?
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
The solution must either copy the primitive which doesn't seem possible, or destroy the new method after the function is complete...
回答1:
String
is a global and if you change it or add to its prototype, then the changes are by definition global. So to revert the changes, you have to revert them.
Normally one would try to derive a custom subclass and add the new prototype method to it, but built-in types like String are notoriously hard to subclass, and even if you could you'd have to call it with something like MyString('abc').first()
.
Your problems stem from your misguided notion of augmenting the String
prototype. It's never a good idea to pollute prototypes. I understand your idea of dealing with that by somehow removing it real quick after adding it and using it, but that's not realistic, and the setTimeout
approach is a horrible, unreliable hack.
However, if you really insist on adding to the String
prototype, then one idea is to add all your new instance methods to a single property which is less likely to collide with things. Let's call it Utils
. We would want to be able to call
'abc'.Utils.first()
But how can we make this work? We define a getter on the Utils
property which returns a hash of the methods. Using a getter (instead of value
) provides us with the this
, which we can pass along to the methods using bind
.
Object.defineProperty(String.prototype, 'Utils', {
get: function() {
return {
first: function() { return this.charAt(0); }.bind(this)
};
}
});
> 'abc'.Utils.first()
< "a"
回答2:
Why do you want to extend String.prototype
? The easiest way is to just create a local function:
alert(foo()); // "a"
alert(first("abc")); // ReferenceError: first is not defined
function foo() {
return first("abc");
function first(string) {
return string.charAt(0);
}
}
There's no advantage in using methods over functions.
回答3:
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
This works by creating the method for String (the global) and deleting it only when the function containing it is complete. Effectively making it exist only in that scope. I don't know if asynchronous code executed in the time foo() is processing would have access to String.prototype.first but it would be interesting to test...
edit: Took a closer look and this doesn't work. the setTimeout that erases the prototype method only runs once the outermost parent function returns. In this snippet, second call to '.first' works outside the function above it.
var foo = function() {
console.log((function () {
String.define('first', function() {
return this.charAt(0);
});
return 'abc'.first();
})());
console.log('abc'.first())
}
来源:https://stackoverflow.com/questions/30658421/extend-a-primitive-only-in-current-scope