Marvin Xu
Marvin Xu

Reputation: 369

Why console.log receiver value in get handler cause an error?

I am learning ES6 Proxy, and try to understand param 'receiver' in a get trap, so I tried to console.log the receiver value. But when run in nodeJS, it causes an error:

RangeError: Maximum call stack size exceeded

let proxy = new Proxy({}, {
  get (target, key, receiver) {
    console.log('receiver:', receiver)
  }
})
let obj = Object.create(proxy)
console.log(obj)

I want to know what is causing this error, and how to test receiver's reference in different situations.

Upvotes: 2

Views: 311

Answers (3)

MaximPro
MaximPro

Reputation: 542

TLDR: problem in implementation of console.log in the chrome/node.js


If we take your code and run in firefox we will see the next:

Object {}

Yes, that's all what will output firefox.

You might ask the question: "What the matter?". We know that the same code in chrome and node.js makes infinite loop and maximum callstack exceeded.


My view of the problem:

Let's take a little edited example of your code:

let proxy = new Proxy({}, {
  get (target, key, receiver) {
    console.log(key)
  }
})
console.log(Object.create(proxy))

Let's see what it will output in the different environments:

Chrome:

{}
Symbol(Symbol.toStringTag)
2 splice

Node.js:

Symbol(nodejs.util.inspect.custom)
Symbol(Symbol.toStringTag)
{}

Firefox:

Object {}

What a twist! Don't you think? Each platform has printed different results. And here we can conclude the only one thing: Every implementation has different behaviour API of the console.log.

Let's see another example:

let proxy = new Proxy({}, {
  get (target, key, receiver) {
    console.log(key)
  }
})
alert(Object.create(proxy))

In this example I've just changed API from console.log to window.alert. Well, go to see results of printing in different environments.

Chrome:

Symbol(Symbol.toPrimitive)
toString
valueOf
Uncaught TypeError: Cannot convert object to primitive value

Firefox:

Symbol(Symbol.toPrimitive)
toString
valueOf
Uncaught TypeError: can't convert Object.create(...) to string

Wow, are you surprised? Same results in both browsers with different engines.

Whatwg doesn't specify how implementations should implement console.log API exactly, but window.alert has concrete steps and that why we got same results. And that inspite of window.alert algorithm doesn't reveal how it should convert arguments to string for printing to the screen in special alert box.


But I can explain how the last example works with proxy unlike with the console.log.

If you know how js works with objects when it try convert to primitive it will be piece of cake.

There is operation in ECMAScript that is called ToString. When this operation is called (I guess that window.alert call it to convert its value to string) it runs in further the operation with the name ToPrimitive (this operation is achieved because, we have an object not a primitive value).

Following occurs the next:

  1. The try to get from object Symbol.toPrimitive property with further its calling to receive its primitive value. If the result of calling Symbol.toPrimitive is object, then will be throw an error.
  2. If Symbol.toPrimitive hasn't been found, then will be call OrdinaryToPrimitive (its order of performing depends on hint, in our case hint is "string" value)
  3. If hint is "string" value then occurs searching and calling the following methods: "toString", "valueOf". If hint isn't "string" value then searching and calling is performed with: "valueOf", "toString".
  4. If methods haven't been found or the last one from them returns object then throwing an error.

If the steps above doesn't invoke the error, then returns a primitive value.

So if you notice your attention to example with window.alert, you notice that the invocation of window.alert will be the cause of printing the methods that are definetely same as in ToString: Symbol.toPrimitive, "toString", "valueOf". And the final is an error that messages about fail of conversion into string.

Conclusion: console.log has different implementation in engines, some implementation such as chrome inside console.log may use arguments directly, thats why we observe strange logging from chrome unlike firefox.

Upvotes: 0

I hope that the following code will help to solve your problem.

let proxy = new Proxy({}, {
    get: function (target, key, receiver) {
        console.log('receiver:' + receiver);
    }
});
let obj = Object.create(proxy);
console.log(obj);

Here you have create a Proxy object and it makes an infinite loop which never ends.

This error (maximum call stack trace exceed) means that somewhere in your code, you are calling a function which in turn calls another function and so forth, until you hit the call stack limit. This is almost always because of a recursive function with a base case that isn't being met.

+(string concatenation operator) with object will call the toString method on the object and a string will be returned. So, '' + object is equivalent to object.toString(). And toString on object returns "[object Object]".

With , the object is passed as separate argument to the log method. So, this takes much time to provide arguments to the console log by sepertely and makes a "maximum call stack trace exceed" error.

So according to me I hope that this shold be the issue.

Upvotes: 0

t.niese
t.niese

Reputation: 40872

The receiver in get (target, key, receiver) refers to the Proxy object, so you create an endless loop.

console.log(obj) tries to log the contents of obj, so it is iterating over all its keys, and retrieves their value. To get their value, the get of the Proxy is invoked, and in that get you have console.log('receiver:', receiver), and there receiver refers to obj, so again it tries to log the contents of obj, … which results in an endless recursive loop.

If you want to understand the param receiver in a get trap then you should not use logging, but the debugger, breakpoints, and the variable inspector in the debugger.

Upvotes: 2

Related Questions