Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When proxying an array, and accessing the map function, why is the array's constructor checked?

Tags:

javascript

I'm creating a proxy of an array, and then calling .map on the proxy to iteratively retrieve the elements in the target array:

const proxiedArray = new Proxy([...], {
    get: function(target, prop) {
      console.log(target, prop) // This is just to see what properties I'm retrieving
      return target[prop]
    }
})

proxiedArray.map(el => ...)

Surprisingly, The log is:

[...] "map"
[...] "length"
[...] "constructor"
[...] "0"
[...] "1"
[...] "2"
[...] "3",
etc

I saw on this post that arrays can be 'wrapped transparently', which I assume is why .map works. But I see that then the proxy is asked for 3 properties: map, length, and constructor.

Why is this? (in particular, why is the constructor checked?)

like image 572
Zach Smith Avatar asked May 17 '26 18:05

Zach Smith


1 Answers

When you do proxiedArray.map, you are accessing the "map" property from the object. This is why, that property gets logged first.


If you check the polyfill of Array.prototype.map, there is this code:

var len = O.length >>> 0

which is accessing current array's length. So, that explains the length property.


I'm not sure about the constructor property. I copied the polyfill of Array.prototype.map, added debugger inside it and executed your code. The constructor property was not logged.

It is probably coming from this bit A = new Array(len). Internally, javascript might be handling this part a bit differently than what the polyfill suggests (Something like A = new this.constrcutor(len) will access the constructor property)

Array.prototype.map = function(callback /*, thisArg*/ ) {

  var T, A, k;

  if (this == null) {
    throw new TypeError('this is null or not defined');
  }

  // 1. Let O be the result of calling ToObject passing the |this| 
  //    value as the argument.
  var O = Object(this);

  // 2. Let lenValue be the result of calling the Get internal 
  //    method of O with the argument "length".
  // 3. Let len be ToUint32(lenValue).
  var len = O.length >>> 0;

  // 4. If IsCallable(callback) is false, throw a TypeError exception.
  // See: http://es5.github.com/#x9.11
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }

  // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
  if (arguments.length > 1) {
    T = arguments[1];
  }

  // 6. Let A be a new array created as if by the expression new Array(len) 
  //    where Array is the standard built-in constructor with that name and 
  //    len is the value of len.
  A = new Array(len);

  // 7. Let k be 0
  k = 0;

  // 8. Repeat, while k < len
  while (k < len) {

    var kValue, mappedValue;

    // a. Let Pk be ToString(k).
    //   This is implicit for LHS operands of the in operator
    // b. Let kPresent be the result of calling the HasProperty internal 
    //    method of O with argument Pk.
    //   This step can be combined with c
    // c. If kPresent is true, then
    if (k in O) {

      // i. Let kValue be the result of calling the Get internal 
      //    method of O with argument Pk.
      kValue = O[k];

      // ii. Let mappedValue be the result of calling the Call internal 
      //     method of callback with T as the this value and argument 
      //     list containing kValue, k, and O.
      mappedValue = callback.call(T, kValue, k, O);

      // iii. Call the DefineOwnProperty internal method of A with arguments
      // Pk, Property Descriptor
      // { Value: mappedValue,
      //   Writable: true,
      //   Enumerable: true,
      //   Configurable: true },
      // and false.

      // In browsers that support Object.defineProperty, use the following:
      // Object.defineProperty(A, k, {
      //   value: mappedValue,
      //   writable: true,
      //   enumerable: true,
      //   configurable: true
      // });

      // For best browser support, use the following:
      A[k] = mappedValue;
    }
    // d. Increase k by 1.
    k++;
  }

  // 9. return A
  return A;
};

const proxiedArray = new Proxy([1, 2, 3], {
  get: function(target, prop) {
    console.log(prop) // This is just to see what properties I'm retrieving
    return target[prop]
  }
})

proxiedArray.map(el => el)
like image 67
adiga Avatar answered May 19 '26 07:05

adiga



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!