Search code examples
javascriptecmascript-6instanceoffunction-binding

What kind of “tampering” is prevented by making `Function.prototype[Symbol.hasInstance]` non-writable and non-configurable?


The ECMAScript 2015 official spec on Function.prototype says (ECMA-262 6th Ed. §19.2.3.6):

[The Symbol.hasInstance property] is non-writable and non-configurable to prevent tampering that could be used to globally expose the target function of a bound function.

Now, even non-writable and non-configurable properties of the prototype can be overwritten with Object.defineProperty and indeed if you try to overwrite Symbol.hasInstance to always return true then it will do so.

I don't understand the quote though.

Presumably the scenario where the global function could be exposed is in the case of a bound function when you overwrite the target's Symbol.hasInstance to return true. Naturally it would return false because the target swaps its prototype onto the bound function, and therefore the bound function is not an instance of the target. Also, to my best understanding I believe the reason it would end up on the global scope is because a bound function has no prototype and therefore cannot physically be an instance of the target function, so if you force it as an instance then the target's prototype is forced on the non-existent bound prototype and it ends up failing and placing the target's this on the global scope. However, even when I set it to return true I still cannot get it to expose the target globally.

Note, this is something I am trying to do to better understand the inner workings of JavaScript. In practical application I wouldn't want to expose the target globally.

I have tried hours and hours of fiddling with a range of code snippets of bound functions and Symbol.hasInstance returning true but to no avail. I cannot get it to expose the target's functions and data globally. If anyone understands this better it would really be greatly appreciated. I've hit a brick wall.


Solution

  • What the quoted passage refers to is that you cannot do:

    // A malicious library loads here and overrides the function.
    (function(){
      Object.defineProperty(Function.prototype, Symbol.hasInstance, {
        value: function(instance){
          const context = this;
    
          // Here, `this === SomeClass`
        },
      });
    }();
    
    // Some library loads here.
    (function(){
      function SomeClass(){}
    
      const BoundClass = SomeClass.bind(null);
    
      var tmp = {} instanceof BoundClass;
    })();
    

    So in this example, if the property were configurable: true, a malicious library would be able to access SomeClass, which would otherwise have been an entirely private and scoped within an IIFE.