call

  1. 首先判断this是否是一个方法,不是的话,就抛出TypeError的错误
  2. 判断context是否存在,不存在就默认为window,若context为原始值时,需要把原始值包装成对象,如String,Number,Boolean
  3. 为避免函数名与context发生冲突,用Symbol来类型作为唯一标识符
  4. 将函数作为context的属性执行,改变函数内部的this指向,原理是谁调用,this就指向谁
  5. 删除context上的属性
  6. 返回结果

    1. Function.prototype.myCall = function(context, ... args) {
    2. if (typeof this !== "function") {
    3. throw new TypeError(`${this} is not a function`);
    4. }
    5. const isInvalid = context === null || context === undefined;
    6. context = isInvalid ? window : new Object(context);
    7. const key = Symbol();
    8. context[key] = this;
    9. const result = context[key](... args);
    10. delete context.key;
    11. return result;
    12. };

    apply

  7. 首先判断this是否为function

  8. 判断context是否存在,不存在默认为window,如context为原始值,需要转化为对应的对象
  9. 判断第二个参数是否存在,如果为伪数组,用Array.from转换为真数组
  10. 避免命名冲突,用Symbol做唯一值
  11. 将函数作为context的属性执行
  12. 删掉context上的属性
  13. 返回执行结果

    1. Function.prototype.myApply = function(context) {
    2. if (typeof this !== "function") {
    3. return new TypeError(`${this} is not a function`);
    4. }
    5. const isInvalid = context === null || context === undefined;
    6. context = isInvalid ? window : new Object(context);
    7. const key = Symbol();
    8. context[key] = this;
    9. const args = arguments[1];
    10. const result = args ? context[key](... Array.from(args)) : context[key]();
    11. delete context[key];
    12. return result;
    13. };

    bind

  14. bind是返回一个新的函数,所以首先要return一个bindFn

  15. bindFn内部可以借助call/apply实现this的指向问题
  16. 注意的点是,bind可以是绑定到普通function上,也可以绑定到new 方式上
  17. 所以需要this instanceof bindFn说明是绑定到new方式上,this就指向该function的实例,否则则指向该context对象上
  18. 修改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; };
  1. <a name="EhwK2"></a>
  2. # new
  3. 19. new返回的是一个对象,包含prototype
  4. 19. 所以先建一个空对象
  5. 19. 然后将Con的prototype赋值给该对象
  6. 19. 指向构造函数Con,为Obj添加属性
  7. 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; };

  1. <a name="MiHXL"></a>
  2. # instanceof
  3. 左边是一个对象,有__proto__(getPrototype获取)属性,右边是个构造函数,有prototype属性
  4. ```css
  5. const myInstanceof = function(left, right){
  6. const l_p = Object.getPrototypeof(left);
  7. const r_p = right.prototype;
  8. while(true){
  9. if(l_p === null){
  10. return false;
  11. }
  12. if(l_p === r_p){
  13. return true;
  14. }
  15. l_p = Object.getProttypeof(l_p);
  16. }
  17. };

拷贝

浅拷贝

Object: JSON.parse(JSON.stringify(obj)), …
数组: concat, …, slice

深拷贝

  1. const isObject = function(target) {
  2. return target === null || typeof target === "object";
  3. }
  4. const mapTag = '[object Map]';
  5. const setTag = '[object Set]';
  6. const arrayTag = '[object Array]';
  7. const objectTag = '[object Object]';
  8. const argsTag = '[object Arguments]';
  9. const boolTag = '[object Boolean]';
  10. const dateTag = '[object Date]';
  11. const numberTag = '[object Number]';
  12. const stringTag = '[object String]';
  13. const symbolTag = '[object Symbol]';
  14. const errorTag = '[object Error]';
  15. const regexpTag = '[object RegExp]';
  16. const funcTag = '[object Function]';
  17. const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];
  18. const getType =function(target){
  19. return Object.prototype.toString.call(target);
  20. };
  21. const cloneObject = (target)=>{
  22. const result = Array.isArray(target)?[]:{};
  23. Object.keys(target).forEach(k=>{
  24. const item = target[k];
  25. result[k] = clone(item,map);
  26. });
  27. return result;
  28. };
  29. const cloneSymbol = (target)=>{
  30. return Object(Symbol.prototype.valueOf.call(target));
  31. }
  32. const clone = function(target,map=new WeakMap()){
  33. if(!isObject(target)){
  34. return target;
  35. }
  36. //循环引用
  37. if(map.get(target)){
  38. return map.get(target);
  39. }
  40. let result;
  41. const type = getType(target);
  42. switch(type){
  43. case boolTag:
  44. case numberTag:
  45. case stringTag:
  46. case errorTag:
  47. case dateTag:
  48. result = new target.constructor(target);
  49. break;
  50. case symbolTag:
  51. result = cloneSymbol(target);
  52. return;
  53. case regexpTag:
  54. //TODO
  55. return;
  56. case funcTag:
  57. //TODO, new Function
  58. return;
  59. case objectTag:
  60. //TODO
  61. result = cloneObject(target);
  62. break;
  63. default:
  64. result = null;
  65. }
  66. map.set(target,result);
  67. return result;
  68. };

继承

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

  1. <a name="YaMUU"></a>
  2. ## 构造器继承
  3. **核心:**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

  1. <a name="sKY3G"></a>
  2. ## 组合继承
  3. **核心:**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”

  1. <a name="jJYZq"></a>
  2. ## 原型链继承
  3. **核心:**创建一个过渡函数对象,返回一个新的该函数对象的实例,实现了prototype的浅拷贝<br />**缺点:**子类实例共享问题

function inheritObject = function(o){ //生命一个过度的函数对象 const Function = function(){}; //过度函数对象的原型继承父类原型 Function.prototype = o; //返回过渡函数的实例 return new Function(); }

var obj = { name: “bella”, age: 18 };

  1. <a name="Pbirm"></a>
  2. ## 寄生继承
  3. **核心:**基于原型继承方式,再封装一个方法,然后扩展

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; };

  1. <a name="Nh0Ej"></a>
  2. ## 组合寄生继承
  3. **核心:** 结合寄生和组合继承方式, 复制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; };

  1. <a name="AjjFU"></a>
  2. ## ES6 Class
  3. **核心: **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 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  1. <a name="wuTqe"></a>
  2. ## ES6 Extend
  3. **核心:** Object.create,Object.create同理与原型继承方式,然后传入的第二个参数就是修改constructor,其实就是组合寄生继承方式的语法糖的实现。
  1. subClass.prototype = Object.create(superClass && superClass.prototype, {
  2. constructor: {
  3. value: subClass,
  4. writable: true,
  5. configurable: true
  6. }
  7. });
  1. <a name="Kd6de"></a>
  2. # 节流

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;

};

  1. <a name="Omo1K"></a>
  2. # 防抖

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; };

  1. const debounce = function(...args){
  2. const context = this;
  3. //如果需要立马执行,并且没有执行过,就立马执行
  4. let now = !timmer;
  5. if(immediate && now){
  6. immediateExec(context, ... args);
  7. return;
  8. }
  9. if(!timer){
  10. startTimer(context, ... args);
  11. }
  12. };
  13. debounce.cancel = clearTimer;
  14. return debounce;

};

  1. <a name="DCDWO"></a>
  2. # 函数柯里化

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); }; } };

  1. return curry;

};

const add = (a, b, c, d) => { return a + b + c + d; }; const curAdd = myCurry(add); console.log(curAdd(1)(2)(3, 4));

  1. <a name="djnvY"></a>
  2. # 数组扁平化
  3. **核心**: 使用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];

  1. }, []);

};

  1. <a name="evYpz"></a>
  2. # 实现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); };

  1. <a name="YAX6m"></a>
  2. # 优雅处理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); ```

函数式编程

特点:
函数是一等公民,函数可以作为输入和输出
纯函数,相同的输入只有相同的输出
引用透明,相同的输入只有相同的输出,函数内部不依赖外部状态,如一些全局变量
惰性求职,一个表达式的变量,不是声明的时候被计算的,而是被调用的时候被计算

https://hejialianghe.github.io/jsadvanced/asyncpro.html#_3-8-web-workers%E7%9A%84%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%9C%BA%E5%88%B6