javascriptproxyapplyprimitiveconstruct

How to proxy JavaScript creation primitive


I want to do something when creating a string, for example:

String = new Proxy(String, {
    construct: (t, a) => {
        return { a: 123 }
    }
})
console.log(new String('q')) // { a: 123 }

However, if you use primitives, it doesn't work.

String = new Proxy(String, {
    construct: (t, a) => {
        return { a: 123 }
    }
})
console.log('1') // Expected: { a: 123 }, Actual: 1

Is there any way?

then the second question is, when the runtime converts primitives, can I proxy the process?

var a = '123' // This is a primitive
console.log('123'.substring(0,1)) // Actual: 1
// The runtime wraps the primitive as a String object.
// then uses a substring, and then returns the primitive.

now:

String = new Proxy(String, {
    construct: (t, a) => {
        return { a: 123 }
    },
    apply: (target, object, args) => {
        return { a: 123 }
    }
})
console.log('1'.a) // Expected: 123 , Actual: undefined

I know I can add 'a' to the prototype of String to achieve the expectations.

But I want to be able to proxy access to arbitrary attributes for primitives. (Is '1'.*, Is not jsut '1'.a)

Is there any way?

Thank you for your answer.


Solution

  • First question: it's not possible, because the JavaScript engine are not really using primitive contractors. (in the same way Number.constructor is not called on each number, String.contractor is not called on each string). It's a part of the language.

    I first thought the second question was also impossible (as @Bergi said), but after a bit of research, I discovered it's possible to replace the entire prototype object for a proxy using Object.setPrototypeOf.

    string_proxy = new Proxy(String, {
        get(target, prop, receiver) {
            return 123
        },
    })
    Object.setPrototypeOf(String.prototype,string_proxy)
    
    console.log('1'.a) // Expected: 123 , Actual: 123
    

    Explanation:

    prototype is a non-configurable property (locked getter/setter) of String. However, it can be set using Object.setPrototypeOf. Normally to set a class prototype it should be used like that:

    class Human {}
    class SuperHero {}
    
    // Set the instance properties
    Object.setPrototypeOf(SuperHero.prototype, Human.prototype);
    
    // Hook up the static properties
    Object.setPrototypeOf(SuperHero, Human);
    

    In this case, doing setPrototypeOf(String,string_proxy) only result in String.x being sent to the proxy, since this would port all instance properties of Proxy to the static properties of String.

    "" was not affected by it, because it's an instance of String. So the solution was to port the instance properties of Proxy to the instance properties of String.