如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?
第三天要刷的手写题如下:
-
手写一个函数,实现Function.prototype.call功能。
-
手写一个函数,实现Function.prototype.apply功能。
-
手写一个函数,实现Function.prototype.bind功能。
下面是我自己写的答案:
0. 总体思路
-
手写call apply bind 的核心在于js中的一个机制:
-
如果
typeof a.b === "function";
并且a.b是一个普通函数,则在执行a.b()
的时候,函数体中的this会指向a; -
不论是手写这三个中的哪一个,依据的核心都是上面的这个机制,只是使用的方式略微有些不同罢了!
此外,如果使用低版本的js写这三个函数,可能会碰到两个难题:
-
如何获取剩余参数
-
如何产生一个独一无二的属性名
-
本文使用ES6语法书写了这三个方法,同时将上面两个难点的解决方法放在了最后。
1. 手写一个函数,实现Function.prototype.call功能
function myCall (context, ...args: any) {
if(typeof this !== "function") throw new Error('myCall must be called by a function');
const _context = context || window;
const _prop = Symbol('b');
_context[_prop] = this;
const result = _context[_prop](...args);
delete _context[_prop];
return result;
}
2. 手写一个函数,实现Function.prototype.apply功能
和myCall唯一区别是参数的类型是不是以数组的形式传递进来的.
function myApply (context, args: any[]) {
if(typeof this !== "function") throw new Error('myApply must be called by a function');
const _context = context || window;
const _prop = Symbol('b');
_context[_prop] = this;
const result = args ? _context[_prop](...args) : _context[_prop]();
delete _context[_prop];
return result;
}
3. 手写一个函数,实现Function.prototype.bind功能
-
区别在于:会形成一个闭包,返回的是一个函数
-
执行和删除的时机也不同
-
带一点柯里化的味道
function myBind (context, ...args: any) {
if(typeof this !== "function") throw new Error('myBind must be called by a function');
const _context = context || window;
const _prop = Symbol('b');
_context[_prop] = this;
return function (...rest: any) {
const parm = [...args, ...rest];
const result = _context[_prop](...parm);
delete _context[_prop];
return result;
}
}
4. ES5中如何获取剩余参数
使用Array.prototype.slice
或者Array.from
都可以实现将类数组变成真正数组的效果。
function getRest (_arguments: IArguments) {
const _rest = Array.prototype.slice.call(_arguments);
const _first = _rest.splice(0, 1);
return [ _first, _rest ];
}
5. ES5中如何产生一个独一无二的对象属性
使用时间戳,如果觉得不保险的话,使用精度更高的performance.now
function getUniqueB () {
return 'b'.concat((+new Date()).toString(16));
}