案例背景
本人在复习资料中 意外看到了几年前写的 call 源码,奇葩思路传递了一个字符串, 不多说直接上代码
Function.prototype.mycall = function(...args) {
const fn = this;
if (typeof fn !== 'function') throw new TypeError('call 必须被函数调用');
let target = args.shift();//获取第一个参数
if (!target || typeof target !== 'object') target = window; //不传参默认只想 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'); //源生的打印出 undefined
echo.mycall('123'); //我自己的打印出 ''
复制代码
发现使用源生的 call 打印出来的是 undefined 使用自己的 mycall 打印了 ”
第一个坑
name 是 window 对象的关键字
引用 MDN 上的定义
Window.name:获取/设置窗口的名称。
window.name 会调用 toString 将赋给它的值转换成对应的字符串表示。
第二个坑
猜想是否是 this 指向出问题(源生的 call 可能不是单纯的把 this 指向于第一个参数)
我们来打印一下 this
Function.prototype.mycall = function(...args) {
const fn = this;
if (typeof fn !== 'function') throw new TypeError('call 必须被函数调用');
let target = args.shift();//获取第一个参数
if (!target || typeof target !== 'object') target = window; //不传参默认只想 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'); //源生的打印出 undefined
echo.mycall('123'); //我自己的打印出 ''
复制代码
结果出乎意料
String {"123"}//源生的call
Window {window: Window, self: Window, document: document, name: "", location: Location, …}//mycall
复制代码
让我们来修复这个问题
Function.prototype.mycall = function(...args) {
const fn = this;
if (typeof fn !== 'function') throw new TypeError('call 必须被函数调用');
let target = args.shift();//获取第一个参数
if (!target) target = window; //不传参默认指向 window
//如果不是 object 需要使用 new 构造出对应的构造器原型对象的实例 修复指向问题
/*======关键代码======== */
if (typeof target !== 'object') {
const type = Object.prototype.toString.call(target).slice(8, -1);
target = new type.constructor(target);
}
/*======关键代码======== */
target._fn = fn;
const res = target._fn(args);
delete target._fn;
return res;
};
复制代码
完事,测试正常
完整代码
Function.prototype.mycall = function(...args) {
const fn = this;
if (typeof fn !== 'function') throw new TypeError('call 必须被函数调用');
let target = args.shift();//获取第一个参数
if (!target) target = window; //不传参默认指向 window
//如果不是 object 需要使用 new 构造出对应的构造器原型对象的实例 修复指向问题
/*======关键代码======== */
if (typeof target !== 'object') {
const type = Object.prototype.toString.call(target).slice(8, -1);
target = new type.constructor(target);
}
/*======关键代码======== */
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'); //源生的打印出 undefined
echo.mycall('123'); //我自己的打印出 undefined
复制代码
遗留的小 bug
- 自己写的 mycall 会在 echo 打印 this 的时候多出一个 _fn 属性
欢迎大佬 指正 完成 py 交易
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END