原生js 之 new apply bind call 实现
- 缘起:
因为自己报了一个金三银四冲刺班, 2.21 的题目是手动实现一个bind 函数,自己不会实现,而且连基本的用法都不清楚,另外这样一道题是大厂的高频面试题,需要熟练手写实现,所以在这里记录一下自己的实现过程;
- new 函数的实现:
使用工厂函数实现new 新建对象的时候,会返回一个含有this 指向的对象或者是return 出来的对象,总之new 是返回一个对象;
那么在实现new 函数的时候需要return obj;
那么 new 的实现代码是:
function _new (ctor , ...args) {
if(typeof ctor !== 'function') {
throw new TypeError('ctor must be a function');
}
let obj = new Object();
obj.__proto__ = Object.create(ctor.prototype);
let res = ctor.apply(obj , [...args]);
let isObj = typeof res === 'object' && typeof res !== null;
let isFunction = typeof res === 'function';
return isFunction || isObj ? res : obj;
}
看一下 _new 函数的用法:
function person (name) {
this.name = name;
}
let myperson = _new(person , 'lili');
console.log(myperson);
上面输出的是:
person {name : 'lili'}
看上面的例子可以看出,_new 函数的参数就好理解了; ctor 接收的是对象参数,...args是 函数接收的参数对象; 那么 _new 函数关键的逻辑是如何实现生成一个对象并把接受的...args 添加到新建的这个对象上了;
我们继续: 首先需要边界值判断,ctor 不是函数的情况需要throw 一个TypeError;
之后new 一个obj 作为对象副本(深拷贝), __proto__ 需要继承ctor 的prototype , 之后新建一个res 来apply 获取args 数据,看写法:
let res = ctor.apply(obj , [...args]);
这里借用的是ctor 对象,...args 参数传递给obj;
最后的情况,添加一个判断,判断是对象类型还是函数类型,按照判断的结果进行输出;
前面说到new 可以返回一个对象,那么测试的用例可以这样写:
function anotherPerson (name) {
var obj = {};
obj[name] = name;
return obj;
}
let myanotherperson = _new(anotherPerson , 'huanghuang');
console.log(myanotherperson);
输出的结果{name:'huanghuang'};
上面说完了new 函数的实现,下面分一个组直接说call 和 apply 的实现;
注意上面两个函数的使用是直接用在Function 上面的,那么写法应该是:
Function.prototype.myapply = function (context , ..args){
let context = context || window;
context.fn = this;
let result = eval('context.fn(...args)');
delete context.fn;
return result;
}
eval 函数是计算一个字符串的值;
call 函数的实现方式和bind函数的实现方式一致;
是需要立即执行的,bind 函数和他们不一致的地方在于bind 函数不需要立即执行,所以实现的时候是不一样的;
实现的代码如下:
Function.prototype.myBind = function(context , ...args) {
if(typeof this !== 'function'){
throw new TypeError('is not a function');
}
// 注意 bind 不是立即执行,所以需要返回一个函数;
let self = this;
let fbound = function() {
self.apply(this instanceof self ? this : context , context.concat(Array.prototype.slice.call(arguments)));
}
// 需要判断fbound 的prototype 是否继承函数原型链的prototype;
if(this.prototype) {
fbound.prototype = Object.create(this.prototype);
}
return fbound;
}
上述就是apply , call, bind new 函数的手动实现,很神奇,大概是自己并不了解这四个函数的原理吧; 不熟悉的原因还在于对js 的原型链自己并不熟悉, 是自己的知识盲区,通过实现者四个函数自己对于原型链的把握会好的多;
about me : 毕业一年半,目前正在搭建完整的web 体系 ,金三银四也在看机会,欢迎沟通; wechat:Yingbin192 , 微信公众号:
, 欢迎关注;