0

The ECMAScript 2015 official spec on Symbol.hasInstance writes:

This property (referring to Symbol.hasInstance) 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 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.

10
  • I guess the tamper-proofing works. Commented Jul 6, 2016 at 1:08
  • It can't prevent full tampering though. You can definitely overwrite Symbol.hasInstance even if it's non-writable if you use Object.defineProperty(). I have overwritten it many times. It is only set to non-writable to prevent standard reassignment.
    – dragonfire
    Commented Jul 6, 2016 at 1:11
  • Something like this? javascriptweblog.wordpress.com/2011/12/05/… Commented Jul 6, 2016 at 1:13
  • Unfortunately, it doesn't really address my issue. It's a terrific article though, and thanks for pointing it out.
    – dragonfire
    Commented Jul 6, 2016 at 1:24
  • I am trying to understand that spec snippet to be able to complete an article I'm writing on well-known Symbols. I believe I understand the theory as to why it would end up on the global scope; however, not being able to implement a snippet which demonstrates it means I cannot finish the article (and possibly my understanding is flawed). The theory is that since a bound function has no prototype then forcing the bound function's non-existent prototype to be an instance of the target will cause the assignment to fail, hence triggering a default assignment to the global scope.
    – dragonfire
    Commented Jul 6, 2016 at 1:32

1 Answer 1

2

Let's clarify, you are talking about section 19.2.3.6 of the spec, which is the spec for Function.prototype[Symbol.hasInstance].

The text in the most recent version of the spec is:

This property is non-writable and non-configurable to prevent tampering that could be used to globally expose the target function of a bound function.

What this is saying 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.

7
  • Brilliant. Indeed Object.defineProperty seems capable of shadowing but not overwriting Symbol.hasInstance. That's something I missed so thank you very much for showing that to me! I still don't understand two things which would complete this answer: 1) Why are we worried specifically about target functions? I assume this has something to do with bound functions having no prototype but I'm not sure how that plays in here. 2) How does this expose into the global scope? This most definitely is on the right track and the intention of the spec. It's talking about redefining Function.prototype. Ty!!
    – dragonfire
    Commented Jul 6, 2016 at 3:13
  • 1) It's concerning because it's a case that could allow you to access private data from inside a scope that you have no access to. Say you have a library that happens to use .bind and instanceof internally, normally you'd have no access to that, but this would let you get a reference to the function. 2) It's the global scope because any global script you be able to redefine the property if it weren't non-configurable. Commented Jul 6, 2016 at 3:24
  • So to complete your example would you say that your definePrototype would return this? Also after re-defining Function.prototype (assuming that would work) how would you then get access to the private scope of the object i.e. what would the code to do that look like?
    – dragonfire
    Commented Jul 6, 2016 at 3:29
  • I expanded my example. Commented Jul 6, 2016 at 3:41
  • I assume it returns true which is how {} is an instanceof BoundClass (and anything for that matter). I also assume that once grabbing const context = this that value provides access to the IIFE's private scope and can be accessed and used later. Thank you for your detailed answer
    – dragonfire
    Commented Jul 6, 2016 at 4:53

Not the answer you're looking for? Browse other questions tagged or ask your own question.