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类型的几种方式
1、 typeof
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
方法1:
function useDebouncedEffect(fn, ms, deps) {
useEffect(() => {
let clean = null;
const timer = setTimeout(() => {
clean = fn();
}, ms);
return () => {
clearTimeout(timer);
if (clean) clean();
};
}, deps);
}
方法2:
function 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)