1. 手写 Object.create(原型式继承)
传入现有的对象来创键新对象的
__proto__
function create(obj) {function F() {}F.prototype = objreturn new F()}
2. 手写 instanceof
instanceof:判断 a 是否由 B 实现的。
- 获取对象的原型 proto
- 获取类的原型 prototype
- 如果两者想等返回 true;如果对象的原型为 null 返回 false;否则一直获取对象的原型
function instanceOf(left, right) {let proto = Object.getPrototypeOf(left)const prototype = right.prototypewhile (true) {if (!proto) return falseif (proto === prototype) return trueproto = Object.getPrototypeOf(proto)}}
3. 手写 new 操作符
要实现 new 关键字,首先要搞懂 new Constructor() 时都发生了什么。 从结果来看,new 关键字的作用是创建了一个新对象,并且这个新对象和构造函数在一条原型链上。此外,对象的属性值是构造函数的参数。 根据上述描述,得到如下代码:
// 1. 创建新对象const obj = {}// 2. 将新对象和构造函数绑到一条原型链上obj.__proto = fn.prototype// 当然,步骤 1 和 2 也可以合并为一步操作:const obj = Object.create(fn.prototype)// 3. 对象的属性值是构造函数的参数// 假设 fn 是构造函数, 那么构造函数 fn 一定有绑定 this 的操作, 即 this.a = a// 因此我们通过 bind 或 apply 来绑定属性, 并将 this 指向 objconst ret = fn.apply(obj, arguments)// 4. 考虑到构造函数可能会有返回值,可能为this,也可能返回其他值,所以这里一定不能用 ret 作为返回值!!网上的答案这里都是错的。return obj
function myNew() {// 利用 shift 得到 fnconst fn = [].shift.call(arguments)const obj = {}obj.__proto__ == fn.prototypefn.apply(obj, arguments)return obj}
4. 手写 typeof
typeof 的实现原理比较简单,主要是利用 Object.prototype.toString.call() 。 该方法会任意类型的数据转换成对象,并利用 toString 方法打印字符串。
function typeOf(obj) {return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()}
function getType(value) {// 判断数据是 null 的情况if (value === null) {return value + "";}// 判断数据是引用类型的情况if (typeof value === "object") {let valueClass = Object.prototype.toString.call(value),type = valueClass.split(" ")[1].split("");type.pop();return type.join("").toLowerCase();} else {// 判断数据是基本数据类型的情况和函数的情况return typeof value;}}
6. 手写 call
call 函数的实现步骤:
- 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
- 判断传入上下文对象是否存在,如果不存在,则设置为 window 。
- 处理传入的参数,截取第一个参数后的所有参数。
- 将函数作为上下文对象的一个属性。
- 使用上下文对象来调用这个方法,并保存返回结果。
- 删除刚才新增的属性。
- 返回结果。
// call函数实现Function.prototype.myCall = function(context) {// 判断调用对象if (typeof this !== "function") {console.error("type error");}// 获取参数let args = [...arguments].slice(1),result = null;// 判断 context 是否传入,如果未传入则设置为 windowcontext = context || window;// 将调用函数设为对象的方法context.fn = this;// 调用函数result = context.fn(...args);// 将属性删除delete context.fn;return result;};
7. 手写 apply
apply 函数的实现步骤:
- 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
- 判断传入上下文对象是否存在,如果不存在,则设置为 window 。
- 将函数作为上下文对象的一个属性。
- 判断参数值是否传入
- 使用上下文对象来调用这个方法,并保存返回结果。
- 删除刚才新增的属性
- 返回结果
// apply 函数实现Function.prototype.myApply = function(context) {// 判断调用对象是否为函数if (typeof this !== "function") {throw new TypeError("Error");}let result = null;// 判断 context 是否存在,如果未传入则为 windowcontext = context || window;// 将函数设为对象的方法context.fn = this;// 调用方法if (arguments[1]) {result = context.fn(...arguments[1]);} else {result = context.fn();}// 将属性删除delete context.fn;return result;};
8. 手写 bind
bind 函数的实现步骤:
- 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
- 保存当前函数的引用,获取其余传入参数值。
- 创建一个函数返回
- 函数内部使用 apply 来绑定函数调用,需要判断函数作为构造函数的情况,这个时候需要传入当前函数的 this 给 apply 调用,其余情况都传入指定的上下文对象。
// bind 函数实现Function.prototype.myBind = function(context) {// 判断调用对象是否为函数if (typeof this !== "function") {throw new TypeError("Error");}// 获取参数var args = [...arguments].slice(1),fn = this;return function Fn() {// 根据调用方式,传入不同绑定值return fn.apply(this instanceof Fn ? this : context,args.concat(...arguments));};};
9. 手写 Object.assign
Object.myAssign = function(target, ...source) {if (target == null) {throw new TypeError('Cannot convert undefined or null to object')}let ret = Object(target)source.forEach(function(obj) {if (obj != null) {for (let key in obj) {if (obj.hasOwnProperty(key)) {ret[key] = obj[key]}}}})return ret}
10. 手写柯里化
function curry(fn, args) {// 获取函数需要的参数长度let length = fn.length;args = args || [];return function() {let subArgs = args.slice(0);// 拼接得到现有的所有参数for (let i = 0; i < arguments.length; i++) {subArgs.push(arguments[i]);}// 判断参数的长度是否已经满足函数所需参数的长度if (subArgs.length >= length) {// 如果满足,执行函数return fn.apply(this, subArgs);} else {// 如果不满足,递归返回科里化的函数,等待参数的传入return curry.call(this, fn, subArgs);}};}// es6 实现function curry(fn, ...args) {return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);}
