call
- 首先判断this是否是一个方法,不是的话,就抛出TypeError的错误
 - 判断context是否存在,不存在就默认为window,若context为原始值时,需要把原始值包装成对象,如String,Number,Boolean
 - 为避免函数名与context发生冲突,用Symbol来类型作为唯一标识符
 - 将函数作为context的属性执行,改变函数内部的this指向,原理是谁调用,this就指向谁
 - 删除context上的属性
 返回结果
Function.prototype.myCall = function(context, ... args) {if (typeof this !== "function") {throw new TypeError(`${this} is not a function`);}const isInvalid = context === null || context === undefined;context = isInvalid ? window : new Object(context);const key = Symbol();context[key] = this;const result = context[key](... args);delete context.key;return result;};
apply
首先判断this是否为function
- 判断context是否存在,不存在默认为window,如context为原始值,需要转化为对应的对象
 - 判断第二个参数是否存在,如果为伪数组,用Array.from转换为真数组
 - 避免命名冲突,用Symbol做唯一值
 - 将函数作为context的属性执行
 - 删掉context上的属性
 返回执行结果
Function.prototype.myApply = function(context) {if (typeof this !== "function") {return new TypeError(`${this} is not a function`);}const isInvalid = context === null || context === undefined;context = isInvalid ? window : new Object(context);const key = Symbol();context[key] = this;const args = arguments[1];const result = args ? context[key](... Array.from(args)) : context[key]();delete context[key];return result;};
bind
bind是返回一个新的函数,所以首先要return一个bindFn
- bindFn内部可以借助call/apply实现this的指向问题
 - 注意的点是,bind可以是绑定到普通function上,也可以绑定到new 方式上
 - 所以需要this instanceof bindFn说明是绑定到new方式上,this就指向该function的实例,否则则指向该context对象上
 - 修改bindFn原型指向,使其指向到function自身的原型 ``` Function.prototype.myBind = function(context, … baseArgs) { if (typeof this !== “function”) { throw new TypeError(“Function.prototype.bind - what is trying to be bound is not callable”); } const fn = this; //创建一个纯净的函数用来存放prototype const NOP = function() {}; const fBind = function(… args) { //this instanceof NOP用来判断是否使用new来调用bind,如果是,this就指向其实例,如果不是就指向指定的对象 //为什么这里是this instanceof NOP,这里本身应该判断this instanceof fBound,但是fBound又是NOP的衍生,所以判断this instanceof NOP也是可以的 return fn.apply(this instanceof NOP ? this : context, [… baseArgs, … args]); }; //箭头函数没有this.prototype if (this.prototype) { NOP.prototype = this.prototype; } fBind.prototype = new NOP(); return fBind; };
 
<a name="EhwK2"></a># new19. new返回的是一个对象,包含prototype19. 所以先建一个空对象19. 然后将Con的prototype赋值给该对象19. 指向构造函数Con,为Obj添加属性19. 如果Con有返回值,返回值是一个对象或者function的时候,就返回结果,否则返回该对象
const createNew = function(Con, … args) { const obj = Object.create(null); Object.prototype.setPrototypeof(obj,Con.prototype); const result = Con.call(obj, … args); return result instanceof Object ? result : obj; };
<a name="MiHXL"></a># instanceof左边是一个对象,有__proto__(getPrototype获取)属性,右边是个构造函数,有prototype属性```cssconst myInstanceof = function(left, right){const l_p = Object.getPrototypeof(left);const r_p = right.prototype;while(true){if(l_p === null){return false;}if(l_p === r_p){return true;}l_p = Object.getProttypeof(l_p);}};
拷贝
浅拷贝
Object: JSON.parse(JSON.stringify(obj)), …
数组: concat,  …, slice
深拷贝
const isObject = function(target) {return target === null || typeof target === "object";}const mapTag = '[object Map]';const setTag = '[object Set]';const arrayTag = '[object Array]';const objectTag = '[object Object]';const argsTag = '[object Arguments]';const boolTag = '[object Boolean]';const dateTag = '[object Date]';const numberTag = '[object Number]';const stringTag = '[object String]';const symbolTag = '[object Symbol]';const errorTag = '[object Error]';const regexpTag = '[object RegExp]';const funcTag = '[object Function]';const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];const getType =function(target){return Object.prototype.toString.call(target);};const cloneObject = (target)=>{const result = Array.isArray(target)?[]:{};Object.keys(target).forEach(k=>{const item = target[k];result[k] = clone(item,map);});return result;};const cloneSymbol = (target)=>{return Object(Symbol.prototype.valueOf.call(target));}const clone = function(target,map=new WeakMap()){if(!isObject(target)){return target;}//循环引用if(map.get(target)){return map.get(target);}let result;const type = getType(target);switch(type){case boolTag:case numberTag:case stringTag:case errorTag:case dateTag:result = new target.constructor(target);break;case symbolTag:result = cloneSymbol(target);return;case regexpTag://TODOreturn;case funcTag://TODO, new Functionreturn;case objectTag://TODOresult = cloneObject(target);break;default:result = null;}map.set(target,result);return result;};
继承
https://juejin.cn/post/6844903984335945736#heading-19
类继承
类继承实现的原理:subClass.prototype = new ParentClass()
缺点: 
- 无法传值,
 - 子类共享父类属性,如果父类属性是引用类型,子类修改父类属性之后,其他子类也会受到影响 ``` function SuperClass(name, age){ this.name = name; this.age = age; };
 
SuperClass.prototype.getName = ()=>{console.log(this.name)}
function SubClass(sub){ this.sub = sub; }
SubClass.prototype.getSub=()=>{console.log(this.sub);} SubClass.prototype = new SuperClass();
var sub = new SubClass(“bella”, 18);
console.log(sub instanceof SuperClass)//true console.log(sub instanceof SubClass)//true console.log(SubClass instanceof SuperClass)//false
<a name="YaMUU"></a>## 构造器继承**核心:**SuperClass.call(this,...args),调用父类构造器<br />**缺点:**只能继承构造器内部的属性和方法,原型链上的不能继承
function SuperClass=function(superName){ this.superName = superName; }
SuperClass.prototype.getSuper = function(){ console.log(this.superName); }
function SubClass = function(sub,superName){ SuperClass.call(this,superName); this.sub = sub; }
var sub1 = new SubClass(“sub1”, “super2”); var sub2 = new SubClass(“sub2”, “super2”);
console.log(sub1 instanceof SuperClass); //true console.log(sub2 instanceof SuperClass); //true
sub1.getSuper() //TypeError
<a name="sKY3G"></a>## 组合继承**核心:**SubClass.prototype = new SuperClass() && SuperClass.call(this,...args)<br />**缺点:**SuperClass被执行两次
function SuperClass(superName){ this.superName = superName; } SuperClass.prototype.getSuper = function(){ console.log(this.superName); };
function SubClass(sub,superName){ SuperClass.call(SubClass, superName); this.sub = sub; } SubClass.prototype = new SuperClass();
var sub1 = new SubClass(“sub1”, “super1”); var sub2 = new SubClass(“sub2”, “super2”);
console.log(sub1 instanceof SuperClass); //true console.log(sub2 instanceof SuperClass); //true
sub1.getSuper() // “super1”
<a name="jJYZq"></a>## 原型链继承**核心:**创建一个过渡函数对象,返回一个新的该函数对象的实例,实现了prototype的浅拷贝<br />**缺点:**子类实例共享问题
function inheritObject = function(o){ //生命一个过度的函数对象 const Function = function(){}; //过度函数对象的原型继承父类原型 Function.prototype = o; //返回过渡函数的实例 return new Function(); }
var obj = { name: “bella”, age: 18 };
<a name="Pbirm"></a>## 寄生继承**核心:**基于原型继承方式,再封装一个方法,然后扩展
function inheritObject = function(o){ const Function = function(){}; Function.prototype = o; return new Function(); }
function inherit = function(o){ const obj = inheritObject(o); // 拓展新对象 o.getName = function(name) { console.log(name) } // 返回拓展后的新对象 return o; };
<a name="Nh0Ej"></a>## 组合寄生继承**核心:** 结合寄生和组合继承方式, 复制superClass的prototype,然后把sub.prototype.construtor指向subClass<br />优点:不用执行superClass的构造器
function inheritObject = function(o){ const Function = function(){}; Function.prototype = o; return new Function(); }
function inherit = function(subClass, superClass){ const sub = inheritObject(superClass.prototype); sub.constructor = subClass; subClass.prototype = sub; return sub; };
<a name="AjjFU"></a>## ES6 Class**核心: **Object.defineProperty, 给target添加方法或者属性
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
作者:Neal_yang 链接:https://juejin.cn/post/6844903984335945736 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
<a name="wuTqe"></a>## ES6 Extend**核心:** Object.create,Object.create同理与原型继承方式,然后传入的第二个参数就是修改constructor,其实就是组合寄生继承方式的语法糖的实现。
subClass.prototype = Object.create(superClass && superClass.prototype, {constructor: {value: subClass,writable: true,configurable: true}});
<a name="Kd6de"></a># 节流
const throttle=function(fn,delay,{head=true,tail=true}=options){ let timer = null; const immediateExec = (context, args) => { fn.call(context, … args); }; const startTimer = (context, args) => { timer = setTimeout(() => { clearTimeout(timer); timer = null; immediateExec(context, … args); }, delay); }; const clearTimer = () => { clearTimeout(timer); timer = null; }; let pre = new Date(); const throttle = function(…args){ const context = this; const now = new Date(); //第一次执行,有头或者无头,取决于Head pre = head ? now : pre; //立马执行 const remaining = delay - (now - pre); if(remaining<=0 || remaining > delay){ if(timer) clearTimer(); immediateExec(context, args); return; } //最后一次 if(!!tail && !timer){ startTimer(context, … args); } }; throttle.cancel = function(){}; return throttle;
};
<a name="Omo1K"></a># 防抖
const debounce = function(fn, delay, immediate){ const immediateExec = (context, args) => { fn.call(context, … args); }; const startTimer = (context, args) => { timer = setTimeout(() => { clearTimeout(timer); timer = null; immediateExec(context, … args); }, delay); }; const clearTimer = () => { clearTimeout(timer); timer = null; };
const debounce = function(...args){const context = this;//如果需要立马执行,并且没有执行过,就立马执行let now = !timmer;if(immediate && now){immediateExec(context, ... args);return;}if(!timer){startTimer(context, ... args);}};debounce.cancel = clearTimer;return debounce;
};
<a name="DCDWO"></a># 函数柯里化
const myCurry = function(fn) { if (fn.length <= 1) { return fn; } const curry = (… args) => { console.log(args.length); if (args.length === fn.length) { fn(… args); } else { return (… newArgs) => { return curry(… args, … newArgs); }; } };
return curry;
};
const add = (a, b, c, d) => { return a + b + c + d; }; const curAdd = myCurry(add); console.log(curAdd(1)(2)(3, 4));
<a name="djnvY"></a># 数组扁平化**核心**: 使用reduce
var selfFlat = function(deep = 0) { const arr = Array.prototype.slice.call(this); if (deep === 0) { return arr; } return arr.reduce((target, item) => { if (Array.isArray(item)) { return [… target, … selfFlat.call(item, deep - 1)]; } return [… target, item];
}, []);
};
<a name="evYpz"></a># 实现Object.assign
const isNull = (value) => { return value === null || value === undefined; }; const isComplexDataType = (v) => { return v === null || typeof v === “object” || typeof v === “function”; }; const selfAssign = function(target, … sources) { if (isNull(target)) { return new TypeError(target); } sources.reduce((res, item) => { if (!isComplexDataType(res)) { res = new Object(res); } if (isNull(item)) { return res; } [… Object.keys(item), … Object.getOwnPropertySymbols(item)].forEach(key => { res[key] = item[key]; }); }, target); };
<a name="YAX6m"></a># 优雅处理try/catch
const errorCaptured = function(asyncFunc,…args){ try { let res = await asyncFunc(…args); return [null,res]; } catch (error) { return [error,null]; } }; let [err,res] =await errorCaptured(asyncFunc,args); ```
函数式编程
特点:
函数是一等公民,函数可以作为输入和输出
纯函数,相同的输入只有相同的输出
引用透明,相同的输入只有相同的输出,函数内部不依赖外部状态,如一些全局变量
惰性求职,一个表达式的变量,不是声明的时候被计算的,而是被调用的时候被计算
