前言
上次发了一下手动实现 Promise https://segmentfault.com/a/11… 之后有猿友问我私聊我其它的像 bind, call 这些有没有标准的写法。我的回答是没有, 像这些 api 我们只能仿照它的功能,只能做得很像很像,但是绝对标准的答案是没有的,就像我之前的那篇文章,也只是在参照 PromiseA+ 规范去仿写。好多 Promise 特有的 API 和特性也没有写进去,因为我的目的是通过手写来让大家学习 promise,现在的自动化构建工具及其插件这么多,功能这么强大我们没有那么多的要求去为了实现兼容性而自己手动实现一遍。
包括我自己也是,面试的时候确实会问一些能不能手动实现一个什么什么的,但是这个目的并不是招你过来手写 API 的,因为我认为手动造轮子或造框架要求你的 js 基本功必须扎实,想去实现一个东西,必须要完完全全地了解它才可以。手动实现代码的过程中往往也能看出一个人的编程思想。当然,能写出来或者模拟地很像那肯定是非常好的。
这篇文章我建议大家不要刻意去收藏,去记从而去应付面试, 这样的学习方法不推荐 (当然以前我也这样),这种文章读几遍就行了, 最主要地是之后去看这些 API 的用法以及特点,以及发掘每个人的编程思想, 然后自己写下来,甚至写的更好,记忆更加深刻。并且我也建议大家项目中遇到问题之后, 第一时间不要去想着 复制错误信息然后 百度一下,必应一下,google 一下, 这样意义不大,正确的做法我觉得是先通过错误信息先自己找到错误根源, 想一想为什么会出错, 为什么达不到你想要的结果, 然后总结经验, 如果是一些框架 API 或者插件, 遇到问题最好先去文档里面 认真 的看一下, 然后学着自己手动解决。之前的我学新东西新技术很爱看视频,百度搜。但是现在的话我更愿意去看它的文档,有时间去逛它的社区,当然了, 新东西出来不要去盲目地学 学技术要本着一精多专的目的去学, 不过不论是什么方向, js 的基础是非常非常重要的。
好了,废话太多了,进入正题。。。
手动实现 Call
这些 API 的特点以及具体用法我就不介绍了,相信你如果奔着手动实现的方向来看,那么你一定对它很了解
核心: this 永远指向最后调用它的对象
代码:
Function.prototype.myCall = function(context, ...args) {context['key'] = this;
context.key(...args);
delete context.key;
}
写到这里,call 基本的特点已经实现, 其实面试中一般写到这里也就通过了, 但是实际上却还差很远。之前我的那篇文章也是如此,只是实现了基本的功能。因为完完全全的实现确实会花费很多时间.
还是简单说一下吧,我们知道 call 中是可以传入一些基本类型的,并且我们现在的这种实现方式增加了一个显示的 key 属性,实际上你在调用真正的 call 的时候去打印 this 是无法发现这些额外属性的,那么我们就仿照这些特性再去优化,接近一下
Function.prototype.myCall = function(context, ...args) {
let newContext = context;
if ([null, undefined].includes(context) ) {newContext = window || {};
}
switch (typeof context) {
case 'number': {newContext = new Number(context);
break;
}
case 'boolean': {newContext = new Boolean(context);
break;
}
case 'string': {newContext = new String(context);
break;
}
}
Object.defineProperty(newContext, 'key', {
value: this,
configurable: true,
enumerable: false,
});
newContext.key(...args);
delete newContext.key;
}
这里就差不多了,后面的我就直接上代码了
实现 apply
Function.prototype.myApply = function(context, obj) {if (typeof obj !== 'object') {throw new TypeError('CreateListFromArrayLike called on non-object')
}
let newContext = context;
if ([null, undefined].includes(context)) {newContext = window;}
switch (typeof context) {
case 'number': {newContext = new Number(context);
break;
}
case 'boolean': {newContext = new Boolean(context);
break;
}
case 'string': {newContext = new String(context);
break;
}
}
Object.defineProperty(newContext, 'key', {
value: this,
configurable: true,
enumerable: false,
});
newContext.key(...obj);
delete newContext.key;
};
实现 bind
Function.prototype.myBind = function(context, ...args) {
let _this = this;
let newFun = (...args2) => {_this.call(context, ...args, ...args2)
};
newFun.prototype = Object.create(_this.prototype)
return newFun
};
实现 Promise
https://segmentfault.com/a/11…
实现 Object.create
Object.create = Object.create || function(obj){var F = function(){};
F.prototype = obj;
return new F();}
实现 new
function myNew(fun) {if (typeof fun !== 'function') throw new TypeError('fun is not a constructor')
return function() {let obj = { '__proto__': fun.prototype}
fun.call(obj, ...arguments)
return obj
}
}
实现 reduce
Array.prototype.myReduce = function(fn, orginal = '__orginal') {let copy = [...this]
if (orginal !== '__orginal') {copy.unshift(orginal)
}
while (copy.length > 1) {let prev = copy.shift()
let next = copy.shift()
copy.unshift(fn(prev, next))
}
return copy[0]
}
总结
还是之前那句话,这些东西不推荐去硬记(如果你为了应付面试当我没说),目的是掌握它然后能够自己写下来。最后再说一下, 虽然我们现在都是站在巨人的肩旁上, 很多东西我们拿来即用就可以,但是是否还要有这种需求呢?有,当然有,任何东西都不是绝对完美的,react 好用,但是你知道 setState 到底应该同步还是异步的口水大战吗?完美是因为适应,你能手写是因为我知道你绝对理解,好多公司有自己的框架,也都是会借鉴,仿写甚至在源码的基础上去优化成为适应自身公司业务的框架。你想想,对 API 的核心原理以及用法都不理解,不知道 JS 线程与 GUI 渲染线程互斥,不理解模块化的意义, 不知道设计原则,不清楚何为设计何为模式,你又怎么进阶到手写源码,架构底层或是对前端性能进行优化呢? 所以,不要着急去背,哪里不会哪里没有理解就认真地去理解它。接下来我应该会发一系列的算法文章, 如果有兴趣的可以提前关注一下,当然了,如果你觉得对你有帮助, 麻烦点个赞, 如果觉得哪里写的不严谨麻烦指出,共勉!