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>
# new
19. new返回的是一个对象,包含prototype
19. 所以先建一个空对象
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属性
```css
const 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:
//TODO
return;
case funcTag:
//TODO, new Function
return;
case objectTag:
//TODO
result = 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); ```
函数式编程
特点:
函数是一等公民,函数可以作为输入和输出
纯函数,相同的输入只有相同的输出
引用透明,相同的输入只有相同的输出,函数内部不依赖外部状态,如一些全局变量
惰性求职,一个表达式的变量,不是声明的时候被计算的,而是被调用的时候被计算