Extend a Primitive only in current scope

独自空忆成欢 提交于 2019-12-23 16:52:34

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!