# 防抖与节流
# 防抖 (debounce)
将多次高频操作优化为只在最后一次执行 使用场景:用户输入,只需在输入完成后做一次输入校验即可
function debounce(fn, delay){
let timerId = null
return function(){
const context = this
if(timerId){window.clearTimeout(timerId)}
timerId = setTimeout(()=>{
fn.apply(context, arguments)
timerId = null
},delay)
}
}
const debounced = debounce(()=>console.log('hi'))
debounced()
debounced()
# 节流(throttle):
每隔一段时间后执行一次 使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500 ms 执行一次即可。
function throttle(fn, delay){
let canUse = true
return function(){
if(canUse){
fn.apply(this, arguments)
canUse = false
setTimeout(()=>canUse = true, delay)
}
}
}
const throttled = throttle(()=>console.log('hi'))
throttled()
throttled()
# 手写 AJAX
ajax 是一种异步通信的方法,从服务端获取数据,达到局部刷新页面的效果。
- 创建 XMLHttpRequest 对象
- 调用 open 方法传入三个参数 请求方式(GET/POST)、url、同步异步(true/false)
- 监听 onreadystatechange 事件,当 readystate 等于 4 时返回 responseText
- 调用 send 方法传递参数
const ajax = (method, url, data, success, fail) => {
var request = new XMLHttpRequest()
request.open(method, url);
request.onreadystatechange = function () {
if(request.readyState === 4) {
if(request.status >= 200 && request.status < 300 || request.status === 304) {
success(request)
}else{
fail(request)
}
}
};
request.send();
}
# 深拷贝、浅拷贝
# 浅拷贝
以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响
- Object.assign
- 展开运算符(...)
function shallowClone(obj){
let cloneObj = {};
for(let i in obj){
cloneObj[i] = obj[i]
}
return cloneObj
}
# 深拷贝
完全拷贝一个新对象,修改时原对象不再受到任何影响
- JSON.parse(JSON.stringify(obj)): 性能最快
- 具有循环引用的对象时,报错
- 当值为函数、undefined、或 symbol 时,无法拷贝
- 递归进行逐一赋值
function deepClone(obj){
if( obj === null ){ return null}
if( obj instanceof RegExp ){ return new RegExp(obj)}
if( obj instanceof Date ){ return new Date(obj)}
if( typeof obj === 'object'){
var result = Array.isArray(obj) ? [] : {}
for(var i in obj){
result[i] = typeof obj[i] === 'object' ? deepClone(obj[i] : obj[i])
}
} else{
var result = obj
}
rerturn result
}
# 数组去重
- 使用 Set
- 利用 for 嵌套 for,然后 splice 去重
- 利用 indexOf 去重
- 利用 includes
- 利用 sort()
- 利用 filter
- 利用 hasOwnProperty
- 使用 WeakMap
[...new Set(arr)]
function unique (arr) {
return Array.from(new Set(arr))
}
// fun2 :双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。
function unique (arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) {
arr.splice(j, 1);
j--;
}
}
} return arr;
}
// fun3: 新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。
function unique (arr) {
var array = [];
for (var i = 0; i < arr.length; i++) {
if (array.indexOf(arr[i]) === -1) {
array.push(arr[i])
}
}
return array;
}
// fun4:利用includes 检测数组是否有某个值
function unique (arr) {
var array = [];
for (var i = 0; i < arr.length; i++) {
if (!array.includes(arr[i])) { /
array.push(arr[i]);
}
}
return array
}
// fun5: 利用sort()排序方法,然后根据排序后的结果进行遍历及相邻元素比对。
function unique (arr) {
arr = arr.sort()
var arrry = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i - 1]) {
arrry.push(arr[i]);
}
}
return arrry;
}
// fun6: 利用filter
function unique(arr) {
return arr.filter(function(item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
// fun7: 利用hasOwnProperty
function unique (arr) {
var obj = {};
return arr.filter(function (item, index, arr) {
return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
})
}
// fun8: 使用 WeakMap
function unique(arr) {
var map = new Map()
for (let i = 0; i < arr.length; i++) {
let number = arr[i]
if (number === undefined) { continue }
if (map.has(number)) {
continue
}
map.set(number, true)
}
return [...map.keys()]
}
# 数组扁平化
- 调用 ES6 中的 flat 方法
- 利用 reduce 函数迭代
- 扩展运算符
- 普通递归
let ary = [1, [2, [3, [4, 5]]], 6];// -> [1, 2, 3, 4, 5, 6]
// flat 方法
ary = ary.flat(Infinity);
// reduce()
function flatten (arr) {
return arr.reduce((result, item) => {
return result.concat(Array.isArray(item) ? flatten(item) : item);
}, []);
}
//扩展运算符: 只要有一个元素有数组,那么循环继续
while (ary.some(Array.isArray)) {
ary = [].concat(...ary);
}
// 普通递归
let result = [];
function flatten (ary) {
for (let i = 0; i < ary.length; i++) {
let item = ary[i];
if (Array.isArray(ary[i])) {
fn(item);
} else {
result.push(item);
}
}
}
# Promise
function myPromise(constructor){
let self=this;
self.status="pending" //定义状态改变前的初始状态
self.value=undefined;//定义状态为resolved的时候的状态
self.reason=undefined;//定义状态为rejected的时候的状态
function resolve(value){
//两个==="pending",保证了了状态的改变是不不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//两个==="pending",保证了了状态的改变是不不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
} }
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved": onFullfilled(self.value); break;
case "rejected": onRejected(self.reason); break;
default:
}
}
// 测试
var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
# Promise.all
Promise.prototype.myAll
Promise.myAll = function(list){
const results = []
let count = 0
return new Promise((resolve,reject) =>{
list.map((item, index)=> {
item.then(result=>{
results[index] = result
count += 1
if (count >= list.length) { resolve(results)}
}, reason => reject(reason) )
})
})
}
# 发布订阅
实现思路
- 创建一个 eventHub
- 创建一个事件中心(Map)
- on 方法用来把函数 fn 都加到事件中心中(订阅者注册事件到调度中心)
- emit 方法取到 arguments 里第一个当做 event,根据 event 值去执行对应事件中心中的函数(发布者发布事件到调度中心,调度中心处理代码)
- off 方法可以根据 event 值取消订阅(取消订阅)
const eventHub = {
map: {
// click: [f1 , f2]
},
on: (name, fn)=>{
eventHub.map[name] = eventHub.map[name] || []
eventHub.map[name].push(fn)
},
emit: (name, data)=>{
const q = eventHub.map[name]
if(!q) return
q.map(f => f.call(null, data))
return undefined
},
off: (name, fn)=>{
const q = eventHub.map[name]
if(!q){ return }
const index = q.indexOf(fn)
if(index < 0) { return }
q.splice(index, 1)
}
}
eventHub.on('click', console.log)
eventHub.on('click', console.error)
setTimeout(()=>{
eventHub.emit('click', 'frank')
},3000)
# 使用 class
class EventHub {
map = {}
on(name, fn) {
this.map[name] = this.map[name] || []
this.map[name].push(fn)
}
emit(name, data) {
const fnList = this.map[name] || []
fnList.forEach(fn => fn.call(undefined, data))
}
off(name, fn) {
const fnList = this.map[name] || []
const index = fnList.indexOf(fn)
if(index < 0) return
fnList.splice(index, 1)
}
}
// 使用
const e = new EventHub()
e.on('click', (name)=>{
console.log('hi '+ name)
})
e.on('click', (name)=>{
console.log('hello '+ name)
})
setTimeout(()=>{
e.emit('click', 'frank')
},3000)