Case background

I accidentally saw the call source code written a few years ago in the review materials, strange ideas passed a string, not to mention directly on the code

Function.prototype.mycall = function(. args) {
      const fn = this;
      if (typeoffn ! = ='function') throw new TypeError('Call must be called by a function');
      let target = args.shift();// Get the first argument
      if(! target ||typeoftarget ! = ='object') target = window; // The default point is window
      target._fn = fn;
      const res = target._fn(args);
      delete target._fn;
      return res;
};
const obj = { name: 'mike' };
function echo() {
  console.log(this.name);
}

echo.call('123'); // The source prints undefined
echo.mycall('123'); // Print out my own
Copy the code

Mycall = mycall; myCall = myCall;

The first pit

Name is the key of the window object

Reference the definition on MDN

Window.name: Gets/sets the name of the Window.

Window.name calls toString to convert the value assigned to it to the corresponding string representation.

The second pit

Wonder if there is a problem with this pointing to the first argument (the call source might not simply point this to the first argument)

So let’s print this

Function.prototype.mycall = function(. args) {
  const fn = this;
  if (typeoffn ! = ='function') throw new TypeError('Call must be called by a function');
  let target = args.shift();// Get the first argument
  if(! target ||typeoftarget ! = ='object') target = window; // The default value is window
  target._fn = fn;
  const res = target._fn(args);
  delete target._fn;
  return res;
};
const obj = { name: 'mike' };
function echo() {
  console.log(this);
  // console.log(this.name);
}

echo.call('123'); // The source prints undefined
echo.mycall('123'); // Print out my own
Copy the code

The result was unexpected

StringΒ {"123"}//ζΊη”Ÿηš„call
WindowΒ {window: Window, self: Window, document: document, name: "", location: Location, …}//mycall
Copy the code

Let’s fix that

Function.prototype.mycall = function(. args) {
  const fn = this;
  if (typeoffn ! = ='function') throw new TypeError('Call must be called by a function');
  let target = args.shift();// Get the first argument
  if(! target) target =window; // The default point is window

  // If not object, use new to construct the corresponding constructor prototype object instance to fix the pointing problem
  /*====== key code ======== */
  if (typeoftarget ! = ='object') {
      // Just want to implement a call result directly called call😎 kill!
    / * - * /const type = Object.prototype.toString.call(target).slice(8, -1);
    / * - * /target = new type.constructor(target);
    / * + * /target = new target.constructor(target);
  }
  /*====== key code ======== */

  target._fn = fn;
  const res = target._fn(args);
  delete target._fn;
  return res;
};
Copy the code

We’re done. The tests are normal

The complete code

Function.prototype.mycall = function(. args) {
  const fn = this;
  if (typeoffn ! = ='function') throw new TypeError('Call must be called by a function');
  let target = args.shift();// Get the first argument
  if(! target) target =window; // The default point is window

  // If not object, use new to construct the corresponding constructor prototype object instance to fix the pointing problem
  /*====== key code ======== */
  if (typeoftarget ! = ='object') {
    target = new target.constructor(target);
  }
  /*====== key code ======== */
  target._fn = fn;
  const res = target._fn(args);
  delete target._fn;
  return res;
};
const obj = { name: 'mike' };
function echo() {
  console.log(this.name);
}
echo.call('123'); // The source prints undefined
echo.mycall('123'); // My own prints out undefined
Copy the code

Minor bugs left over

  • Mycall has an extra _fn property when echo prints this

Welcome to complete the PY transaction