面试yan-原理性函数

abort操作

JavaScript中实现sleep睡眠函数的几种简单方法
js大数运算

// 订阅模式
// bind apply call
// current uncurrent 函数科利华 函数反科利华
// object.create new instanceOf
// 深冻结 deepFreeze
// 数组的flat
// compose
// reduce
// 深拷贝
// 对象的深拷贝的方式方式
// js处理扁平数组和树结构相互转换
// 队列和锁的问题

// 判断js类型的几种方式
// 防抖截流
// useState
// useEffect
// React.PureComponent 实现React.memo
// js方法构造器和不构造器的时候,返回不同的结果

订阅模式
class EventEmitTest {
	constructor() {
		this.cache = {};
	}
	on(type, fn) {
		if (!this.cache[type]) {
			this.cache[type] = [];
		}
		this.cache[type].push(fn);
	}
	once(type, fn) {
		if (!this.cache[type]) {
			this.cache[type] = [];
		}
		this.cache[type].push(() => {
			fn();
			this.cache[type] = null;
		});
	}
	off(type) {
		this.cache[type] = null;
	}
	emit(type, ...rest) {
		this.cache[type].forEach(item => item.call(this, ...rest));
	}

}

current uncurrent

函数科利华
function test(fun, ...arr) {// 这个 arr是可以作为变量修改的
    const lg = fun.length;
    return (...args2) => {
        arr = arr.concat(args2);
        if(arr.length >= lg) {
            return fun(...arr)
        } else {
            return test(fun, ...arr);
        }
    }
}
----------------
const add = (a, b, c, d, e) => {
    return a + b + c + d + e;
};
console.log(test(add,1,2,3)(4,5));
// 实战一
let r = curring(add)(1)(2)(3)(4); // [1,2,3,4,5]
// 实战二
// 使用场景(curring实战)
const checkType = (type, content) => {
    return Object.prototype.toString.call(content) === `[object ${type}]`;
};
let types = ["Number", "String", "Boolean"];
let utils = {};
types.forEach(type => {
    utils["is" + type] = curring(checkType)(type); // 先传入一个参数
});
console.log(utils.isString('hello'));
函数反科利华

// 方法中的this其实也是方法可以直接使用var that = this; that()去执行

Function.prototype.uncurrent = function () {
    const self = this;
    return function() {
        const [ctx, ...arg] = arguments;
        return self.apply(ctx, arg);
        //相当于Array.prototype.push.apply(obj, 2);
    };
}
// 测试一下
var push = Array.prototype.push.uncurring();
var obj = {
  "length": 1,
  "0" : 1
};
push(obj, 2);
console.log( obj ); //{0: 1,1: 2, length: 2 }

深拷贝

最简单的深度克隆,面试写出这种一般就可以了
function deepCopy(value, hash = new WeakMap){
  if(!value || typeof value !== 'object')
  return value;
  if (hash.has(value)) {
      return hash.get(value)
  }
  let newObject = Array.isArray(value) ? [] : {};
  hasn.set(value, newObject);
  //遍历对象中的key Object.keys 对数组一样可以用
  Object.keys(value).forEach(key => {
    newObject[key] = value[key];
  })
  return newObject;
}

#### 常用的对象的深拷贝的方式方式
[JavaScript浅拷贝与深拷贝的几种方法](https://blog.csdn.net/qq_43807473/article/details/123788420)1)jquery 的$.extend(true, target, obj)2)newobj = Object.create(sourceObj)// 但是这个是有个问题就是 newobj的更改不会影响到 sourceobj但是 sourceobj的更改会影响到newObj
 (3) newobj = JSON.parse(JSON.stringify(sourceObj))  这样子IE7以下是不支持的
        这种方法可以变相的实现深拷贝,但是这种方法也有其限制:
缺点:
    * 首先,数组中的项如果是undefined,那么转换后将变为null
    * 如果数组的项为对象,那么对象之间不可相互引用。会造成循环引用,无法JSON序列化
Object. 直接方法
不需要说的apply,arguments,bind,call,caller,constructor,
create方法:这个的方法的出现为了简化创建对象的时候必须用函数的构造器

#### 深度克隆,还有不完善的地方,就是Date和Regexp的处理
const deepClone = (value ,hash = new WeakMap) => {
    if(value == null) return value; // 排除掉null 和undefine 的情况
    if(typeof value !== 'object') return value; // 这里包含了函数类型
    if(value instanceof RegExp) return new RegExp(value);
    if(value instanceof Date) return new Date(value);
    // .....
    // 拷贝的人可能是一个对象 或者是一个数组 (循环)  for in 
    let instance = new value.constructor; // 根据当前属性构造一个新的实例
    if(hash.has(value)){ // 先去hash中查看一下是否存在过 ,如果存在就把以前拷贝的返回去 
        return hash.get(value); // 返回已经拷贝的结果
    }
    hash.set(value,instance);// 没放过就放进去
    // 用一个对象来记忆
    for(let key in value){ // 一层
        if(value.hasOwnProperty(key)){ // 将hash 继续向下传递 保证这次拷贝能拿到以前拷贝的结果
            instance[key] = deepClone(value[key],hash); // 产生的就是一个新的拷贝后的结果
        }// 过滤掉原型链上的属性
    }
    return instance
};
// 对RegExp的实现方式不完美
Date.prototype.clone=function(){ 
     return new Date(this.valueOf()); 
}
RegExp.prototype.clone = function() {
    var pattern = this.valueOf();
    var flags = '';
    flags += pattern.global ? 'g' : '';
    flags += pattern.ignoreCase ? 'i' : '';
    flags += pattern.multiline ? 'm' : '';
    return new RegExp(pattern.source, flags);
};    

同步队列

// 简单写法 队列一次性的,不考虑后续添加
function test(nums) {
	const deque = [];
	const current = 0;
    const handle = (time) => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve();
            }, time * 1000);
        });
    }
   
    const start = () => {
        handle(nums[current]).then(() => {
             if(current < urls.length) {
             	 current++;
                  start();
              }
		});
    }
    start();
}
并发队列

因为有个并列的队列,所以需要一个对象去控制loading是否正在执行,而上边的队列是线性的,不存在并行的问题
除了用 loading去执行,还有一种用Promise.race的去实现的方法。

简单写法:如果一次性的不考虑后续添加
function test(urls) {
    const max = 3;
    let current = max;
    const handle = (current) => {
        return fetch(urls[current]);
    }
    const start = (index) => {
        handle(index).then(() => {
            if (current++ < urls.length) {
                start(current);
            }
        });
    }
    if (let i = 0; i < max; i++) {
        start(i);
    }
}
---------------------------------------------
function test(data) {
    let deque = [];
    let cache = [];
    const add = (param) => {
        if(Array.isArray(param)) {
            param.forEach(item => {
                if(deque.length > 2) {
                    cache.push(data);
                } else {
                    deque.push(data);
                }
            });
        } else {
            if(deque.length > 2) {
                cache.push(data);
            } else {
                deque.push(data);
            }
        }
    }
    const handle = (val) => {
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve(val);
            }, val * 1000);
        })
    }
    const start = () => {
        deque.forEach((item, itemIndex) => {
            if(item.loading) {
                return;
            }
            item.loading = true;
            handle(item).then(val => {
                deque.splice(itemIndex, 1, 0);
                const tem = cache.pop();
                tem && deque.push(tem);
                start();
            });
        })
    }
    add(data);
    start();
    return {
        add: add
    }
}
---------------------------------------------
### function回调+promise+队列+(每次需要返回一个promise)

```javascript
class Test {
    constructor() {
        this.queue = [];
        this.cache = [];
    }
    handle(item) {
        const func = () => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log(item);
                    resolve();
                }, item * 1000)
            })
        }
        return func;
    }
    add(item) {
        const func = this.handle(item);
        if (this.queue.length < 3) {
            this.queue.push(func);
            this.start(this.queue);
        } else {
            this.cache.push(func);
        }
        return new Promise((resolve, reject) => {
            func.resolve = resolve;
        });
    }
    cacheAdd(itemHandle) {
        if (this.queue.length < 3) {
            this.queue.push(itemHandle);
            this.start(this.queue);
        } else {
            this.cache.push(itemHandle);
        }
    }
    start(param) {
        param.forEach((itemPro, itemProIndex) => {
            if (itemPro.loading) return;
            itemPro.loading = true;
            itemPro().then(() => {
                this.queue.splice(itemProIndex, 1);
                itemPro.resolve();
                if (this.cache.length) {
                    const newItem = this.cache.shift();
                    this.cacheAdd(newItem);
                }
            });
        });
    }
}

// 考察点function + promise + queue + 锁
// 队列每次只能3个,每次执行后返回一个promise
let test = new Test();
function mock() {
    for (let item of [10, 15, 3, 4, 5, 6, 7, 1, 2]) {
        test.add(item).then(() => {
            console.log(item + "end");
        });
    }
}
mock();
// 队列里10, 15, 3
// 3秒后打印出
// 3
// 3end // 3出队列,队列里10, 15, 准备进4
// 7秒(3+4)后打印出
// 4
// 4end // 4出队列,队列里10, 15,准备进5
// 10秒(10秒)后打印出
// 10
// 10end // 10出队列,队列里5, 15,准备进6
// 12秒(3+4+5)后打印出
// 5
// 5end // 5出队列,队列里6, 15,准备进7
// 15秒(15)后打印出
// 15
// 15end // 15出队列,队列里6, 7,准备进1
// 16秒(10+6)后打印出
// 6
// 6end // 6出队列,队列里1, 7,准备进2
// 16秒(15+1)后打印出
// 1
// 1end // 1出队列,队列里2, 7
// 18秒(10+6+2)后打印出
// 2
// 2end // 2出队列,队列里7
// 22秒(3+4+5+7)后打印出
// 7
// 7end // 7出队列

bind apply call

bind使用方法

—bind使用方式
在这里插入图片描述

obj.myFun.call(db);    // 德玛年龄 99
obj.myFun.apply(db);    // 德玛年龄 99
obj.myFun.bind(db)();   // 德玛年龄 99
mock bind实现
Bind可以有科利华的效果,bind返回的结果当构造函数使用的时候注意
Function.prototype.mockbind = (ctx, ...args) {
    let fun = this;
    function result(...args2) {
        const context = this instanceof result ? this : ctx;
        return fun.apply(context, args.concat(args2));
    }
    // 为啥要做这个呢,总不能返回的函数和原一点关系都没有吧,强行继承
    result.prototype = Object.create(fun.prototype);
    return result;
}
mock call apply

公用的处理逻辑
let [ctx, …arg] = arguments;
call和apply 有 ctx.fun = this;
bind则有let that = this; that.apply(ctx,);

// call
function newCall() {
    let [ctx, ...args] = arguments;
    if (!ctx) {
        ctx = window;
    }
    ctx.func = this;
    let result = ctx.func(...args);
    delete ctx.func; // 为什么赋值后在删除,因为函数执行后的this指向函数作用域
    return result;
}
// call
Function.propotype.myCall = function (obj, ...args) {
   let key = Symbol();
    obj[key] = this;
    return obj[key](args)
}
// apply
function newApply() {
    let [ctx, args] = arguments;
    if (!ctx) {
        ctx = window;
    }
    ctx.func = this;
    let result;
    // 这里因为args可能为undefined无法通过...运算符,所以必须区分
    if (args) {
        result = ctx.func(...args);
    } else {
        result = ctx.func();
    }
    delete result.func;
    return result;
}

ps:
var ss = [];
function run () {
let […tt] = arguments;
console.log(tt); // 即使什么都不传,这里的tt仍然为[]
}
run();


object.create new instanceOf

create 的原理

// Object.create的原理,就是实现继承组合继承,返回父对象实例,赋给子对象的prototye属性。

function create(parentProto,{constructor: Tiger}){
   // 前两句是为了模拟父对象的实例,强行模拟了一个父对象。
   function Fn(){}
   Fn.prototype = parentProto;
   let fn = new Fn();
   fn.constructor = Tiger
   return fn;
}

new 的原理
function mockNew(A){
  let obj = {}
  let returnVal = A.call(obj);
  if (result instanceof Object) {
    return returnVal;
  }
  obj.__proto__ = A.prototype
  return obj;
}
// 这里判断的依据
function One(name, age) {
  this.name = name;
  this.age = age;
  // let a = new One1(); console.log(a);
  // return 'aaa'; 
  // 打印 { name: undefined, age: undefined }
  // return {}
  // 打印 {}
  // reuturn funciton(){}
  // 打印 [Function (anonymous)]
}
instanceof和 typeof实现原理 博客2

instanceof 原理 __proto__

function mockInstanceof(leftV, rightV) {
    let left = leftV.__proto__;
    while(true) {
        if(left === null) {
            return false;
        }
        if(left === rightV.prototype) {
            return true;
        }
        left = left.__proto__;
    }
}


深冻结 deepFreeze

浅冻结和深冻结

添加链接描述

  // 深冻结函数
function deepFreeze(obj){
  Object.keys(obj).forEach(item => {
    if (typeof item === 'object' && item !== null){
      deepFreeze(item)
    }
  })
  return Object.freeze(obj);
}
obj2 = {
  internal: {}
};
deepFreeze(obj2);
obj2.internal.a = 'anotherValue';
obj2.internal.a; // undefined


数组的flat

数组的flat
Array.prototype.flat = function(depth = 1) {
    if(depth < 1) {
        return this;
    }
    let result = [];
    this.forEach(item => {
        if(Array.isArray(item)) {
            result = result.concat(item.flat(depth-1));
        } else {
            result.push(item);
        }
    });
    depth--;
    return result;
}

在这里插入图片描述


compose

函数的组合 compose
compose的使用
function sum(a,b){
    return a+b;
}
function len(str){
    return str.length;
}
function addCurrency(val){
    return '¥'+ val
}
let r = compose(addCurrency,len,sum)('abc','bed');

// compose
function compose(...funs) {
    const last = funs.pop();
    return (...args) => {
        funs.reduceRight((total, item) => {
            return item(total);
        }, last(...args));
    }
}

const compose = (...args) => (...values)=>{
    let fn = args.pop(); // 先取出最后一项
    return args.reduceRight((prev,current)=> current(prev), fn(...values));
}

reduce

reduce
function reduceTest(fn, init) {
  let result;
  this.forEach((item, itemIndex) => {
    if (itemIndex === 0) {
      if (init) {
        result = fn(init, item, itemIndex)
      } else {
        result = item;
      }
    } else {
      result = fn(result, item, itemIndex);
    }
  });
  return result;
}

console.log([2,3,4].reduceTest((total, item) => {
    total += item;
    return total;
}))

js处理扁平数组和树结构相互转换
js处理扁平数组和树结构相互转换

// 网络大神写的,只遍历两次
function test(dataArr) {
    const cache = {};
    dataArr.forEach(item => {
        cache[item.id] = item;
    });
    const result = [];
    dataArr.forEach(item => {
        if(cache[item.parentId]) {
            cache[item.parentId].children = cache[item.parentId].children || [];
            cache[item.parentId].children.push(item);
        } else {
            result.push(item);
        }
    });
    return result;
}

console.log(toTree([
{ id: 1, name: 'i1' },
{ id: 2, name: 'i2', parentId: 1 },
{ id: 4, name: 'i4', parentId: 3 },
{ id: 3, name: 'i3', parentId: 2 },
{ id: 8, name: 'i8', parentId: 1 },
{ id: 9, name: 'i9', parentId: 10 }
], 1))
输出:
[{
	id:1,
	name: 'i1',
	 children: [
		{
			id: 2,
			name: 'i2'
		}
	]
}]

判断js类型的几种方式
1typeof
2. Constructor
3. Instanceof
4. Object.prototype.toString.call
// instanceof 特别注意
class AA {}
class BB extends AA{}
const aaa = new BB;
console.log(aaa instanceof AA);
console.log(aaa instanceof BB);

数组一边删除一边增加
const arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
arr.forEach((item, itemIndex) => {
  if (item === 'c') {
    arr.splice(itemIndex, 1)
    console.log('delete c', arr)
  }
  if (item === 'f') {
    arr.splice(itemIndex, 1)
    console.log('delete f', arr)
  }
  console.log(item, arr.length);
})
========
a 7
b 7
delete c [ 'a', 'b', 'd', 'e', 'f', 'g' ]
c 6
e 6
delete f [ 'a', 'b', 'd', 'e', 'g' ]
f 5

堆排序
    function heapSort(array) {
      creatHeap(array);
      console.log(array);
      // 交换第一个和最后一个元素,然后重新调整大顶堆
      for (let i = array.length - 1; i > 0; i--) {
        [array[i], array[0]] = [array[0], array[i]];
        adjust(array, 0, i);
      }
      return array;
    }
    // 构建大顶堆,从第一个非叶子节点开始,进行下沉操作
    function creatHeap(array) {
      const len = array.length;
      const start = parseInt(len / 2) - 1;
      for (let i = start; i >= 0; i--) {
        adjust(array, i, len);
      }
    }
    // 将第target个元素进行下沉,孩子节点有比他大的就下沉
    function adjust(array, target, len) {
      for (let i = 2 * target + 1; i < len; i = 2 * i + 1) {
        // 找到孩子节点中最大的
        if (i + 1 < len && array[i + 1] > array[i]) {
          i = i + 1;
        }
        // 下沉
        if (array[i] > array[target]) {
          [array[i], array[target]] = [array[target], array[i]]
          target = i;
        } else {
          break;
        }
      }
    }

防抖节流

防抖的应用场景 搜索框输入查询, 表单验证,按钮提交事件,浏览器窗口缩放
节流的应用场景 按钮点击事件,拖拽事件,onScoll,计算鼠标移动的距离(mousemove)
添加链接描述
添加链接描述
如果按照lodash的实现太复杂了
lodash throttle就是利用debounce

return debounce(func, wait, {
    'leading': leading,
    'maxWait': wait, // 注意这里
    'trailing': trailing
  });

debounce
记住全局两个变量,timeout和result。没有prev这种概念。
setTimeout放later里边。later的参数是context,args。
判断依据是timeout是否存在,


throttle
全局变量2+2context和args.
添加另一个全局变量previous默认为0;
每次清空的时候,timeout清空,context=args=null; previous重置。
throttled添加now变量,remaining变量。
判断依据是remaining是否<=0,判断timeout存在也必须干掉,立刻执行
二级依据是timeout是否存在,
leading
Trailing

简单的截流防抖
const debounceFunc = (func = () => { }, timer = 500, immediate = false) => {
    let flag = null
    return (args) => {
        immediate && !flag && func(args);
        flag && clearTimeout(flag);
        flag = setTimeout((args) => {
            func(args);
            timer = null;
        }, timer, args);
    }
}
---------------------------------------------------------------------------
const throttleFunc = (func = () => { }, timer = 500) => {
    let flag = false
    return (args) => {
        if (flag) return
        flag = true;
        setTimeout(() => {
            func(args)
            flag = false;
        }, timer);
    }
}
防抖
function debounce(func, wait, immediate = true) {
    let result, timeout, ctx, args;
    let later = function () {
        return setTimeout(() => {
            result = func.apply(ctx, args);
            timeout = null;
            if (!timeout) {
                ctx = args = null;
            }
        }, wait);
    }
    function debounced() {
        ctx = this;
        args = arguments;
        if (!timeout) {
            if (immediate) {
                result = func.apply(ctx, arguments);
                immediate = false;
            } else {
                timeout = later();
            }
        } else {
            clearTimeout(timeout);
            timeout = later();
        }
        return result;
    }
    debounced.cancel = function() {
        clearTimeout(timeout);
        timeout = null;
    }
    return debounced;
}
节流
// throttle
function throttle(func, wait, option) {
    let result, timeout, ctx, args;
    let prev;
    let later = function () {
        result = func.apply(ctx, args);
        timeout = null;
        prev = Date.now();
        if (!timeout) {
            ctx = args = null;
        }
    }
    function throttled() {
        ctx = this;
        args = arguments;
        let now = Date.now();
        if (!prev) {
            prev = now;
        }
        let remaining = wait - (now - prev);
        if (!remaining <= 0) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            result = later();
        } else if(!timeout){
            clearTimeout(timeout);
            timeout = setTimeout(later, remaining);
        }
        return result;
    }
    throttled.cancel = function() {
        clearTimeout(timeout);
        timeout = ctx = args = prev =null;
    }
    return debounced;
}
继承公共方法(必看)
方法1: Child.prototype.__proto__ = Parent.prototype;
方法2: Reflect.setPrototypeOf(Child.prototype,Parent.prototype);
方法3: Child.prototype = Object.create(Parent.prototype);
方法4: util.inherits(Child,Parent); // 继承公共属性

class AA {}
class BB extends AA{}
const aaa = new BB;
console.log(aaa instanceof AA);
console.log(aaa instanceof BB);
es5 类的几种继承 点这里
1. 原型链继承(无法实现多继承)
2. 构造继承(不能继承原型上的属性和方法性)
3. 组合继承(父类执行了两次,产生了两个对象)
function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
5. 寄生组合继承 (通过寄生方式,砍掉父类的实例属性)
function A(name){
	B.call(this);
	this.name = name || 'A';
}
function B() {}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
// Object.create 是不会处理constructor的问题
Object.defineProperty 点击这里
Object.defineProperty
enumerable
value
writable
configurable 是否可删除
var data = {
msg: 123
};
let tem = data.msg;
Object.defineProperty(data,'msg',{
  get: function(){
    return tem;
  },
  set: function(val){
       tem = val;
  }
})
console.log(data.msg) // 如果没有tem的话实现不了中间劫持

es6 Proxy对象详解 点击这里
var obj = new Proxy({}, {
  get: function (target, key, receiver) {
    console.log(`getting ${key}!`);
    return Reflect.get(target, key, receiver);
  },
  set: function (target, key, value, receiver) {
    console.log(`setting ${key}!`);
    return Reflect.set(target, key, value, receiver);
  }
});

----------------------------------小知识点

模块

umd规范。https://blog.csdn.net/henrypt/article/details/53483152
umd规范就是cmd+commonjs 集合。
UMD的实现很简单,先判断是否支持NodeJS模块格式(exports是否存在),存在则使用NodeJS模块格式。
再判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。前两个都不存在,则将模块公开的全局(window或global)。
有的地方已经使用module关键字来定义模块。
在这里插入图片描述

commonjs amd cmd

根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。加载模块是同步的,所以只有加载完成才能执行后面的操作。
在这里插入图片描述
1.对于依赖的模块AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过)。
2.CMD推崇依赖就近,AMD推崇依赖前置。

前端大文件上传
普通表单上传
<form action="/index.php" method="POST" enctype="multipart/form-data"> <input type="file" name="myfile”> 
    <input type="submit”> 
</form>
===============
文件编码上传
===============
formData异步上传
let files = e.target.files 
// 获取input的file对象 
let formData = new FormData(); 
formData.append('file', file); 
axios.post(url, formData);
===============
iframe无刷新页面 form 的target 设置一个iframe的id
$("body").append(`<iframe style="display:none;" name="${id}" id="${id}" />`);
$form.attr({ 
    "action": '/index.php’, 
    "method": "post", "enctype”: 
    "multipart/form-data”, 
    "encoding": "multipart/form-data”, 
    "target": id }).submit()

图片转为base64
drag.addEventListener('drop',(e)=>{
    e.preventDefault();
    let fr = new FileReader()
    fr.readAsDataURL(e.dataTransfer.files[0]);
    fr.onload = function(e){
        let img = new Image()
        img.src = e.target.result
        document.body.appendChild(img);
    }
})
大文件上传
slice chunks
chunks.forEach((chunk, index) => {
  let fd = new FormData();
  fd.append("file", chunk);
  // 传递context
  fd.append("context", context);
  // 传递切片索引值
  fd.append("chunk", index + 1);
    
  tasks.push(post("/mkblk.php", fd));
});
// 所有切片上传完毕后,调用mkfile接口
Promise.all(tasks).then(res => {
  let fd = new FormData();
  fd.append("context", context);
  fd.append("chunks", chunks.length);
  post("/mkfile.php", fd).then(res => {
    console.log(res);
  });
});
// 保证切片
装饰器实现原理
getIterator实现原理
自己实现useState
自己实现useEffect
const state = [];
const cursor = 0;

function mockUseState(initval) {
    if (!state[cursor]) {
        state[cursor] = initval;
    }

    function setState(data) {
        state[cursor] = data;
        render();
    }

    return [state[cursor++], setState];
}

function mockUseMemo(render, deps) {
    if (state[cursor]) {
        const [cacheData, cacheDeps] = state[cursor];
        const same = cacheDeps ? cacheDeps.every((item, itemIndex) => item === deps[itemIndex]) : false;
        if (same) {
            return cacheData;
        }
    }
    const data = render();
    // 第一次渲染 或者 不是第一次但是依赖项相同,都返回新的
    state[cursor++] = [data, deps];
    return data;
}

function mockUseCallback(render, deps) {
    if (state[cursor]) {
        const [cacheRender, cacheDeps] = state[cursor];
        const same = cacheDeps ? cacheDeps.every((item, itemIndex) => item === deps[itemIndex]) : false;
        if (same) {
            return cacheRender;
        }
    }
    // 第一次渲染 或者 不是第一次但是依赖项相同,都返回新的
    state[cursor++] = [render, deps];
    return render;
}


function mockUseEffect(render, deps) {
    const cacheDeps = state[cursor];
    const same = cacheDeps ? cacheDeps.every((item, itemIndex) => item === deps[itemIndex]) : false;
    if (!same) {
        render();
        state[cursor] = deps;
    }
    cursor++;
}


useMemo
React.PureComponent 去实现 React.memo
function memo(OldComponent) {
  return class extends React.PureComponent {
    render() {
      return <OldComponent {...this.props} />
    }
  }
}

typescript
//---Partical 泛型中全部属性变为可选的
type Partial<T> = {
 [P in keyof T]?: T[P]
}
// 使用
type PartOfAnimal = Partical<Animal>;
const ww: PartOfAnimal = { name: 'ww' }; // 属性全部可选后,可以只赋值部分属性了
//----Record<K, T> (K 中所有属性值转化为 T 类型)
type Record<K extends keyof any,T> = {
  [key in K]: T
}
// 使用
const obj: Record<string, string> = { 'name': 'mbg', 'tag': '年轻人不讲武德' }
//----Pick<T, K> 将 T 类型中的 K 键列表提取出来,生成新的子键值对类型
type Pick<T, K extends keyof T> = {
  [P in K]: T[P]
}
// 使用 在 T 类型中,去除 T 类型和 U 类型的交集,返回剩余的部分
const bird: Pick<Animal, "name" |"age"> = { name: 'bird', age: 1 }
//---Exclude<T, U>
type Exclude<T, U> = T extends U ? never : T
// use
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;   // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number

// Omit<T, K>适用于键值对对象的 Exclude,它会去除类型 T 中包含 K 的键值对
type Omit = Pick<T, Exclude<keyof T, K>>
// use
const OmitAnimal:Omit<Animal, 'name'|'age'> = { category: 'lion', eat: () => { console.log('eat') } }
//---ReturnType<T>
type ReturnType<T extends (...args: any) => any>
  = T extends (...args: any) => infer R ? R : any;
 // 简化源码
type ReturnType<T extends func> = T extends () => infer R ? R: any;
// use
function foo(x: string | number): string | number { /*..*/ }
type FooType = ReturnType<foo>;  // string | number
//---Required<T>
type Required<T> = {
  [P in keyof T]-?: T[P]
}
// use

typescript
type Pick<T, K extends keyof T> = {
    [key in K]: T[key];
}
type Partial<T> = {
  [P in keyof T]?: T[P]
 }
type Required<T> = {
    [key in keyof T]-?: T[P]
}
type Record<K extends keyof any,T> = {
  [key in K]: T
}
type Extract<T, U> = T extends U ? T : never
type Exclude<T, K> = T extends U ? never : T;
type Omit = Pick<T, Exclude<keyof T, K>>;
type ReturnType<T extends func> = T extends () => infer R ? R: any;
obj序列化
var aa = {
    a: {
        b: 'hello ',
        c: {
            d: 'world'
        }
    },
    e: 'hello world'
}
// { 'a.b': 'hello ', 'a.c.d': 'world', e: 'hello world' }
function test(data) {
    const result = {};
    function handle(obj, path) {
        Object.keys(obj).forEach(key => {
            let tem = path ? path + '.' + key  : key;
            if(obj[key] && typeof obj[key] === 'object') {
                handle(obj[key], tem);
            } else {
                result[tem] = obj[key];
            }
        });
    }
    handle(data, '');
    return result;
}
console.log(test(aa));
对象A属性存在B属性不存在的时候打印
const obj1 = {
    A: {
        A: '341',
        B: 'name',
        D: {
            A: '123',
        },
        E: {
            A: '123',
            B: '457',
            D: {
                A: '123', 
            }
        }
    },
    b: 345,
    B: 'name',
}
// A.D.A
// A.E.D.A
function test(obj, path) {
    let result = '';
    for (let i = 0; i < Object.keys(obj).length; i++) {
        let key = Object.keys(obj)[i];
        result = path ? (path + '.' + key) : key;
        if(key === 'A' && obj['B'] === undefined) {
            console.log(result);
        }
        if(obj[key] && typeof obj[key] === 'object') {
            test(obj[key], result);
        }
    }
    return result;
}
test(obj1);
js方法构造器和不构造器的时候,返回不同的结果
function clear() {
    if (this instanceof clear) {
        console.log('2')
        return {};
    }
    console.log('1')
    return 1;
}

// console.log(clear()) // 1
// console.log(new clear()) // 2
--------------------------------------
function Obj(name) {
    // 浏览器中是window
  if (this === global)
    return {name}
}
Obj.prototype.name = "name2"
var a = Obj("name1")
var b = new Obj;
console.log(a.name); // name1
console.log(b.name); // name2
lodash 的get方法
function get(source, path, defaultValue = undefined) {
    // a[3].b -> a.3.b -> [a,3,b]
    // path 中也可能是数组的路径,全部转化成 . 运算符并组成数组
    const paths = path.replace(/\[(\d+)\]/g, ".$1").split(".");
    console.log(paths) // ["a", "0", "b"]
    let result = source; //这个result一直都是for循环中下一个key的上个节点
    //循环字符串中的数组取最后一个
    for (const p of paths) {
        result = Object(result)[p];
        if (result == undefined) {
            return defaultValue;
        }
    }
    return result;
}
// 测试用例
console.log(get({ a: [{ b: 1 }] }, "a[0].b", 3)); // output: 1
手动实现一个useDebouncedEffect
方法1function useDebouncedEffect(fn, ms, deps) {
  useEffect(() => {
    let clean = null;
    const timer = setTimeout(() => {
      clean = fn();
    }, ms);
    return () => {
      clearTimeout(timer);
      if (clean) clean();
    };
  }, deps);
}
方法2function useDebouncedCallback(fn, ms, deps) {
  const timerRef = useRef(null);
  const funcRef = useRef(null);

  useEffect(() => {
    return () => {
      clearTimeout(timerRef.current);
      funcRef.current = null;
    };
  }, []);

  return useMemo(() => {
    funcRef.current = fn;
    return function (...args) {
      clearTimeout(timerRef.current);
      timerRef.current = setTimeout(() => funcRef.current(...args), ms);
    };
  }, deps);
}
数组-1就是最后一个

[1,10,20,40,60].slice(-1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值