# 函数柯里化
function curry(fn) {
return function sub(...args1) {
if (fn.length <= args1.length) {
return fn.apply(this, args1)
} else {
return function (...args2) {
return sub.apply(this, args1.push(...args2))
}
}
}
}
# 深浅拷贝
# 浅拷贝
- Object.assign
Object.assign({}, obj)
- 解构
let a = {...obj}
- Array
let res = [1,2,3].slice(0)
let res = [].concat([1,2,3])
# 深拷贝
- JSON
JSON.parse(JSON.stringify(target))
- 手写【未解决循环引用等问题】
function deepClone(target) {
let res
// 判断 target类型
if (getType(target) === "[object Object]") {
res = {}
} else if (getType(target) === "[object Array]") {
res = []
} else {
return target
}
// 递归判断内部
for (let i in target) {
if (getType(target[i]) === "[object Object]"
|| getType(target[i]) === "[object Array]") {
res[i] = deepClone(target[i])
} else {
res[i] = target[i]
}
}
return res
}
function getType(tar) {
return Object.prototype.toString.call(tar)
}
# 防抖与节流
# 防抖
function debounce(fn, delay) {
let timer
return function (...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args)
}, delay);
}
}
# 节流
function throttle(fn, delay) {
let timer = null
let now = Date.now()
let last = 0
return function (...args) {
if (now - last > delay) {// 第一次触发或者已经超过delay后再触发
last = now
fn.apply(this, args)
} else if (timer === null) {// 定时、延迟运行+给null
timer = setTimeout(() => {
timer = null
fn.apply(this, args)
}, delay);
}
}
}
# call/bind/apply
# call
Function.prototype.myCall = function (context = globalThis) {
if (typeof this !== 'function') throw new TypeError('Error')
context.fn = this
const args = Array.from(arguments).slice(1)
const result = context.fn(...args)
delete context.fn
return result
};
// 实验
this.s = "out"
function pp(a, b) {
console.log("pp" + a + b + this.s)
}
let qq = {
s: "qqq"
}
pp("a", "b") // ppabout
pp.call(qq, "a1", "a2") // ppa1a2qqq
pp.myCall(qq, "a11", "a22") // ppa11a22qqq
# apply
Function.prototype.myApply = function (context = globalThis, arr) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context.fn = this
let result
if (arr) {
// 需要判断 arr 存在且是数组
if (Object.prototype.toString.call(arr) !== '[object Array]') throw new TypeError("Error")
result = context.fn(...arr)
} else {
result = context.fn()
}
delete context.fn
return result
};
// 实验
this.s = "out"
function pp(a, b) {
console.log("pp" + a + b + this.s)
}
let qq = {
s: "qqq"
}
pp("a", "b") // ppabout
pp.apply(qq, ["a1", "a2"]) // ppa1a2qqq
pp.myApply(qq, ["a11", "a22"]) // ppa11a22qqq
# bind
Function.prototype.myBind = function (context = globalThis) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
fn = this
const args = Array.from(arguments).slice(1)
return function F() {
// 这里处理一下,因为arguments带有我们不需要的callee、Symbol(Symbol.iterator)等
let fArgs = Array.from(arguments)
// 如果是 new F 的话,this的指向就不是传入的context了
if (this instanceof F) {
return fn.apply(this, args.concat(fArgs))
} else {
return fn.apply(context, args.concat(fArgs))
}
}
}
// 实验
this.s = "out"
function pp(a, b) {
console.log(...arguments,"pp" + a + b + this.s)
}
let qq = {
s: "qqq"
}
pp.bind(qq, "a1", "a2")("arg1", "arg2") // a1 a2 arg1 arg2 ppa1a2qqq
pp.myBind(qq, "a11", "a22")("arg1", "arg2") // a11 a22 arg1 arg2 ppa11a22qqq
# Promise
# Promise.all
function myPromiseAll(arr) {
return new Promise((resolve, reject) => {
// 存储结果
const res = []
// 存储完成的 Pormise 数量
let count = 0
let len = arr.length
// 循环 arr
for (let i in arr) {
// 赋值时如果count==len说明全部结束
// 如果有reject直接reject
Promise.resolve(arr[i]).then(data => {
res[i] = data
if (++count === len) {
resolve(res)
}
}).catch(err => {
reject(err)
})
}
})
}
/*---------- 测试 ----------*/
let p1 = Promise.resolve(1),
p2 = Promise.resolve(2),
p3 = Promise.resolve(3)
myPromiseAll([p1, p2, p3]).then(res => {
console.log(res) //[1, 2, 3]
}).catch(err => {
console.log(err);
})
# Promise.race
注意不能用 .catch()
,不然的话会输出 2
,因为微任务队列里 p1 的 catch 会排在 p2 的 then 后面
function myPromiseRace(arr) {
return new Promise((resolve, reject) => {
for (let item of arr) {
Promise.resolve.then(
res => { resolve(res) },
err => { reject(err) }
)
}
})
}
/*---------- 测试 ----------*/
let p1 = Promise.resolve(1),
p2 = Promise.resolve(2),
p3 = Promise.resolve(3)
myPromiseRace([p1, p2, p3]).then(res => {
console.log(res)
}).catch(err => {
console.log(err);
})
输出1
# JSONP
/**
*
* 最终想要的连接效果:
* <script src='http://localhost:8080/api/jsonp?id=1&cb=jsonpCallback' type='text/javascript'></script>
* 步骤:
* 1. 前端和后端约定 cb 字段用来存放前端执行回调的函数名,假如是 jsonpCallback
* 2. 后端根据params的参数获取指定内容之后,可能返回如 jsonpCallback({id:123})这样的内容
* 3. 前端接收后,执行 window.jsonpCallback({id:123})
*/
const JSONP = ({
url,
params = {},
callbackkey = 'cb', // 与后台约定的回调函数是用哪个字段
callback // 拿到数据之后执行的回调函数
}) => {
// 回调执行的函数
const callBackName = "jsonpCallback"
// cb=jsonpCallback 放入链接的参数中
params[callbackkey] = callBackName
// 后端返回 jsonpCallback(xxx) 之后执行内容
window[callBackName] = callback
const paramStr = Object.keys(params).map((key) => {
return `${key}=${params[key]}`
}).join('&')
const script = document.createElement('script')
script.setAttribute('src', `${url}?${paramStr}`)
document.append(script)
}
# new
function mynew(func, ...args) {
const obj = {}
// 原型链指向修整
obj.__proto__ = func.prototype
// 这里要执行 func,并且以obj为this,带着参数执行
let result = func.apply(obj, args)
// 如果 result(也就是func执行的返回结果)非对象(可能是值类型),那么我们不能要这个结果,就返回obj这个指向func的空对象
if (typeof result === 'object' && result !== null) {
return result
} else {
return obj
}
}
# 数组去重
# Set
[...new Set([1,1,2])]
# 手写
function quchong(arr) {
const res = []
for (let item of arr) {
if (res.indexOf(item) === -1) {
res.push(item)
}
}
return res
}
# 数组扁平化
function flat(arr) {
const res = []
const func = (inarr) => {
for (let item of inarr) {
if (Array.isArray(item)) {
func(item)
} else {
res.push(item)
}
}
}
func(arr)
return res
}
flat([1, [2, [3, [4, [5]]]]])// [1, 2, 3, 4, 5]
# Ajax
const getData = (url) => {
return new Promise((resolve, reject) => {
// 设置 XMLHttpRequest 请求
const xhr = new XMLHttpRequest();
// 设置请求方法和 url
xhr.open('GET', url);
// 设置请求头
xhr.setRequestHeader('Accept', 'application/json');
// 设置请求的时候,readyState 属性变化的一个监控
xhr.onreadystatechange = (res) => {
// 如果请求的 readyState 不为 4,说明还没请求完毕
if (xhr.readyState !== 4) {
return;
}
// 如果请求成功(200),那么 resolve 它,否则 reject 它
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.responseText));
}
};
// 发送请求
xhr.send();
})
};
补充:Ajax 状态
- 0 - 未初始化。尚未调用
open()
方法 - 1 - 启动。已经调用
open()
方法,但尚未调用send()
方法。 - 2 - 发送。已经调用
send()
方法,但尚未接收到响应。 - 3 - 接收。已经接收到部分响应数据。
- 4 - 完成。已经接收到全部响应数据,而且已经可以在客户端使用了。