1.手写new关键字
注意:new关键字创建实例对象的时候,返回类型必须为Object,如果返回值不为对象(null一样不行),返回无效。
//原生:
function Person(name) {
this.name = name;
return 1;
}
const p = new Person("张三");
console.log(p); // Person { name: '张三' }
//手写代码:
function createObj(...args) {
const obj = {};
const Constructor = args.shift();
obj.__proto__ = Constructor.prototype;
const ret = Constructor.apply(obj, args);
return typeof ret === "object" ? ret : obj;
}
2.手写instanceof
function myInstanceof(instance, constructor) {
let proto = instance.__proto__
while (proto) { //最终为null结束
if (proto === constructor.prototype) {
return true
} else {
proto = proto.__proto__
}
}
return false
}
3.数组扁平化
function flat(arr) {
return result = arr.flat(Infinity);
}
4.节流
简单定义:单位时间内只执行一次函数。
规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
// 时间戳版
function throttle(func, delay) {
let last = 0;
return function () {
const now = Date.now();
if (now >= last + delay) {
func.apply(this, arguments);
last = now;
} else {
console.log("不执行");
}
};
}
// 定时器版
function throttle2(func, delay) {
let timer;
return function () {
if (!timer) {
func.apply(this, arguments);
timer = setTimeout(() => {
timer = null; //记得置空
}, delay);
} else {
console.log("不执行");
}
};
}
5.防抖
简单定义:单位时间内执行最后一个函数。
在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
防抖和节流不同的地方在于,函数在一段时间内的多次调用,仅使得最后一次调用有效。
function debounce(func, delay) {
let timer;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, arguments);
}, delay);
};
}
6.数组随机打乱
//随机性不够大,主要是sort原生函数的原理有关
function shuffle(array) {
array.sort(() => { Math.random() - 0.5 }); // sort会改变原数组
}
//可行随机函数
function shuffle(array) {
let m = array.length,
t,
i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
7.数组去重
//利用遍历+Map
function uniqueArr(arr) {
const map = {};
const result = [];
arr.forEach(el => map[el] || ((map[el] = true), result.push(el)));
return result;
}
//利用Set
function uniqueArr(array) {
return [...new Set(array)];
}
8.函数柯里化
定义:柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
function add(...args) {
const _args = Array.prototype.slice.call(args);
const _adder = function (...args2) {
_args.push(...args2);
return _adder;
};
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
}, 0);
};
return _adder;
}
//test code
add(1, 2, 3); //6
add(1)(2)(3); //6
//参数复用
function uri_curry(protocol) {
return function (hostname, pathname) {
return `${protocol}${hostname}${pathname}`;
};
}
const uri_base = uri_curry("https://");
const uri_baidu = uri_base("www.baidu.com", "/search");
const uri_ali = uri_base("www.ali.com", "/taobao");
const uri_tencent = uri_base("www.tencent.com", "/qq");
//test code
console.log(uri_baidu); //https://www.baidu.com/search
console.log(uri_ali); //https://www.ali.com/taobao
console.log(uri_tencent); //https://www.tencent.com/qq
函数柯里化的三个特点
参数复用提前返回延迟执行延迟执行实际上就是当我们调用这个方法时,不会立即执行,或者说在参数符合规定的时候才会执行我们真正想执行的内容。
如上面参数复用中的add(1)(2)(3), 传入1 2 3的时候,函数并没有执行加法运算,只是将这个值推入队列中,仅当不传入参数时,才真正执行。
8.迭代器Iterator
其实一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是 “可遍历的”(iterable)。
ES6 规定,默认的 Iterator 接口部署在数据结构的 Symbol.iterator 属性,或者说,一个数据结构只要具有 Symbol.iterator 属性,就可以认为是"可遍历的"(iterable)。
function createIterator(items) {
let index = 0;
return {
next: function () {
let done = index >= items.length;
let value = !done ? items[index++] : undefined;
return {
done,
value
};
}
};
}
//test code
const arr = ["a", "b"];
for (let val of arr) {
console.log(val); // 打印 a b
}
arr[Symbol.iterator] = () => createIterator([1, 2, 3]);
for (let val of arr) {
console.log(val); //打印 1 2 3
}
9.实现LRU缓存
LRU全称Least Recently Used(最近最少使用),用来淘汰不常用数据,保留热点数据。
class LRU {
constructor(size) {
this.size = size;
this.map = new Map();
}
get(key) {
const val = this.map.get(key);
if (key === undefined) {
return -1;
} else {
this.map.delete(key);
this.map.set(key, val);
}
}
put(key, value) {
const val = this.map.get(key);
if (val === undefined) {
this.map.size === this.size && this.map.delete(this.map.keys().next().value);
this.map.set(key, value);
} else {
this.map.delete(key);
this.map.set(key, value);
}
}
}
9.call apply bind
Function.prototype.myCall = function (context, ...args) {
context = context || window;
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
};
Function.prototype.myApply = function (context, args) {
context = context || window;
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
};
Function.prototype.myBind = function (context, ...args) {
context = context || window;
return () => {
this.apply(context, args);
};
};
const obj = { name: "jr" };
const fn = function (a, b) {
console.log(this.name, a, b);
};
// test code
fn.myCall(obj, 1, 2);
fn.call(obj, 1, 2);
fn.myApply(obj, [1, 2]);
fn.apply(obj, [1, 2]);
fn.myBind(obj, 1, 2)();
fn.bind(obj, 1, 2)();