1、数组去重
去除数组参数中的重复项并返回该数组。
(1)简单数据数组去重
Set
- set是一种集合数据结构,集合内部的元素是唯一的,不可重复。
- 无法去重数据:对象
// 先将数组转化为set,去除重复数据
// 再通过Array.from将类数组对象set,转化为数组
const _deleteRepeat = array => {
return Array.from(new Set(array)) // 也可以使用 return [...new Set(array)]
}
_deleteRepeat([1, 1, true, true, 'true', 'true', null, undefined, undefined, null, NaN, NaN, {}, {}])
// [1, true, 'true', null, undefined, NaN, {}, {}]
indexOf、includes
- 遍历一次原数组,将原数组的数据逐个插入新数组,插入时判断是否是重复数据。
- 无法去重数据:对象
- 如果使用indexOf,那么NaN无法去重
- [NaN].indexOf(NaN) // -1
- indexOf使用严格相等比较:NaN不等于任何值
- [NaN].includes(NaN) // true
- includes使用的SameValueZero比较算法:NaN被认为等于NaN,Set内部也是使用这个算法,因此Set可以去重NaN
const _deleteRepeat = array => {
// 使用filter筛选数组:查找第一次出现的数据下标等于当前下标
return array.filter((item, index) => {
return array.indexOf(item, 0) === index
})
}
_deleteRepeat([1, 1, true, true, 'true', 'true', null, undefined, undefined, null, NaN, NaN, {}, {}])
// [1, true, 'true', null, undefined, {}, {}]
-----------------------------------------------------------------------------------------------
const _deleteRepeat = array => {
return array.reduce((pre, next ) => {
// 这里也可以使用indexOf判断next是否在pre数组中
return pre.includes(next) ? pre : pre.concat(next)
}, [])
}
_deleteRepeat([1, 1, true, true, 'true', 'true', null, undefined, undefined, null, NaN, NaN, {}, {}])
// [1, true, 'true', null, undefined, NaN, {}, {}]
---------------------------------------------------------------------------------------------
const _deleteRepeat = array => {
const newArr = []
array.forEach(item => {
// 这里也可以使用indexOf判断
if(!newArr.includes(item)) {
newArr.push(item)
}
})
return newArr
}
_deleteRepeat([1, 1, true, true, 'true', 'true', null, undefined, undefined, null, NaN, NaN, {}, {}])
// [1, true, 'true', null, undefined, NaN, {}, {}]
splice
- 双重循环,寻找重复数据,并删除
- 无法去重数据:对象,NaN(因为严格相等比较:NaN不等于任何值)
const _deleteRepeat = array => {
for(let i = 0; i < array.length ; i++) {
// 寻找元素右侧是否有重复数据,有的话删除数据
for(let j = i + 1; j < array.length; j++) {
if(array[i] === array[j]) {
array.splice(j, 1);
j--;
}
}
}
return array
}
_deleteRepeat([1, 1, true, true, 'true', 'true', null, undefined, undefined, null, NaN, NaN, {}, {}])
// [1, true, 'true', null, undefined, NaN, NaN, {}, {}]
sort
- 先排序,其次比较相邻数据是否相等
- 无法去重数据:对象,NaN(因为严格相等比较:NaN不等于任何值)
const _deleteRepeat = array => {
array.sort()
for(let i = 0; i < array.length - 1; i++) {
if(array[i] === array[i + 1]) {
array.splice(i + 1, 1);
i--;
}
}
return array
}
_deleteRepeat([1, 1, true, true, 'true', 'true', null, undefined, undefined, null, NaN, NaN, {}, {}])
// [ 1, NaN, NaN, {}, {}, null, true, 'true', undefined ]
(2)对象数组去重(数组元素有对象)
将对象转化为JSON字符串
- 将数组中的对象先转化为JSON字符串,之后可以采用上述目录(一)中的方法 进行处理
- 无法去重对象:NaN,NaN序列化之后的值是'null',
- 数组不可包含undefined,undefined序列化之后的值仍是undefined,不能被JSON.parse解析
const _deleteRepeat = (array) => {
return Array.from(new Set(array.map(JSON.stringify))).map(JSON.parse);
};
_deleteRepeat([1, 1, true, true, 'true', 'true', null, null, NaN, NaN, { a: { b: 1 } }, { a: { b: 2 } }, { a: { b: 1 } }])
// [ 1, true, 'true', null, { a: { b: 1 } }, { a: { b: 2 } } ]
2、new的实现
function myNew (Func, ...args) {
const obj = {}
obj.__proto__ = Func.prototype
const result = Func.apply(obj, args)
return result instanceof Object ? result : obj
}
3、构造函数拦截(不使用new调用)
function Person (name) {
// 拦截构造函数,判断是否使用new调用的构造函数
// 使用new则new.target为构造函数,否则为undefined
// 类只能使用new创建实例
if (!new.target)
{
throw new Error('请使用new调用构造函数')
}
this.name = name
}
4、instanceof实现
function myInstanceOf (target, origin) {
if (typeof target !== 'object' || target === null) return false
let proto = Object.getPrototypeOf(target)
while (proto)
{
if (proto === origin.prototype)
{
return true
}
proto = Object.getPrototypeOf(proto)
}
return false
}
console.log(myInstanceOf([], Object))
5、Object.create实现
function create (obj) {
function F () { }
F.prototype = obj
F.prototype.constructor = F
return new F()
}
6、call、apply、bind实现
var name = '汤姆'
function Say (...args) {
console.log(`我叫${this.name},${args}`)
}
Person = {
name: '大黄蜂'
}
Function.prototype.MyCall = function (context) {
if (typeof this !== 'function')
{
throw new Error('请使用函数调用call')
}
// say方法调用的MyCall,因而this指向say方法
// console.log(this === Person.say) // true
// console.log([...arguments]); // [ { name: '大黄蜂' }, 1, 2, 3 ]
// 在新的上下文上扩展这个方法,并调用
context = context || window
context.fn = this
const result = context.fn(...[...arguments].slice(1))
delete context.fn
return result
}
Say.MyCall(Person, '你说的对Call')
Function.prototype.myApply = function (context) {
if (typeof this !== 'function')
{
throw new Error('请使用函数调用apply')
}
context = context || window
context.fn = this
const result = context.fn(...arguments[1])
delete context.fn
return result
}
Say.myApply(Person, ['你说的对Apply'])
Function.prototype.myBind = function (context) {
if (typeof this !== 'function')
{
throw new Error('请使用函数调用Bind')
}
context = context || window
const args = [...arguments].slice(1)
const fn = this
return function Fn () {
// bind返回值是一个函数,函数是可以new的,new会改变当前的this指向
if (this instanceof Fn)
{
return new fn(...arguments)
}
return fn.apply(context, args.concat(...arguments))
}
}
const bound = Say.myBind(Person, 'bind参数1')
bound(('bind参数2'))
bound(('bind参数3'))
new bound('bind参数4') // this指向Say构造函数的实例(一个空对象)
7、防抖和节流
// 防抖,指定秒数内重新计时
function debounce (func, wait) {
let timer = null
return function () {
if (timer)
{
clearTimeout(timer)
timer = null
}
timer = setTimeout(() => {
func.apply(this, arguments)
}, wait);
}
}
// 节流,指定秒数执行一次
function throttle (func, delay) {
let timer = null
return function () {
if (timer) return
timer = setTimeout(() => {
timer = null
func.apply(this, arguments)
}, delay);
}
}
8、类型判断typeof
function myTypeOf (value) {
if (value === null)
{
return null + ''
}
if (typeof value === 'object')
{
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
} else
{
return typeof value;
}
}
9、浅拷贝与深拷贝
// 对象浅拷贝
const obj = {}
const obj1 = { a: 1, b: 2 }
const obj2 = { c: 3 }
const o = { ...obj1, ...obj2 }
Object.assign(obj, obj1, obj2)
Object.create(obj1)
// 数组浅拷贝
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const a = [...arr1, ...arr2]
const a1 = arr1.slice()
const a2 = arr1.concat(arr2)
// 手动实现浅拷贝
function shadowCopy (object) {
if (!object || typeof object !== 'object') return
const newObj = {}
for (let key in object)
{
if (object.hasOwnProperty(key))
newObj[key] = object[key]
}
return newObj
}
// 深拷贝
JOSN.parse(JSON.stringify(obj)) // 无法拷贝undefined、Symbol、Bigint、function,无法处理循环引用
function deepClone (obj, cache = new WeakMap()) {
if (!obj || typeof obj !== "object") return obj;
// 检查是否已经拷贝过该对象,避免循环引用导致无限递归
if (cache.has(obj)) return cache.get(obj)
let cloneObj = Array.isArray(obj) ? [] : {}
cache.set(obj, cloneObj)
if (Array.isArray(obj))
{
obj.forEach(((item, index) => {
cloneObj[index] = deepClone(item, cache)
}))
}
if (obj instanceof Object)
{
for (let key in obj)
{
if (obj.hasOwnProperty(key))
{
cloneObj[key] = deepClone(obj[key], cache)
}
}
}
return cloneObj;
}
10、最大子序和
// 动态规划法
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function (nums) {
let pre = 0; let max = nums[0]
nums.forEach(num => {
pre = Math.max(pre + num, num)
max = Math.max(max, pre)
})
return max
};
11、爬楼梯
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function(n) {
let pre = 1, preOfPre = 1, current = 1;
for(let i = 2; i <= n; i++){
current = pre + preOfPre;
preOfPre = pre;
pre = current;
}
return current
};
12、字符串相加
/**
* @param {string} num1
* @param {string} num2
* @return {string}
*/
var addStrings = function(num1, num2) {
let i = num1.length - 1, j = num2.length -1, add = 0, ans = '';
while(i >= 0 || j >= 0 || add != 0) {
const x = i >= 0 ? num1.charAt(i) - '0' : 0;
const y = j >= 0 ? num2.charAt(j) - '0' : 0;
res = x + y + add;
ans += res % 10;
add = Math.floor(res / 10);
i--;
j--;
}
return ans.split('').reverse().join('')
};
13、爱吃香蕉的柯柯
/**
* @param {number[]} piles
* @param {number} h
* @return {number}
*/
var minEatingSpeed = function (piles, h) {
let l = 1;
let r = Math.max(...piles);
while (l <= r)
{
const mid = Math.floor(l + (r - l) / 2);
if (pass(mid))
{
r = mid - 1;
} else
{
l = mid + 1;
}
}
return l;
function pass (mid) {
let t = 0;
for (let i = 0; i < piles.length; i++)
{
t += Math.ceil(piles[i] / mid);
}
return t <= h;
}
};
14、有效的字母异位词
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isAnagram = function(s, t) {
if(s.length !== t.length || s === t) return false
const sArr = s.split('').sort()
const tArr = t.split('').sort()
return sArr.join('') === tArr.join('')
};
15、只出现一次的数字
/**
* @param {number[]} nums
* @return {number}
*/
var singleNumber = function (nums) {
let ans = 0;
for (const num of nums)
{
ans ^= num;
}
return ans
};
16、promise
// 模拟实现Promise
// Promise利用三大手段解决回调地狱:
// 1. 回调函数延迟绑定
// 2. 返回值穿透
// 3. 错误冒泡
// 定义三种状态
const PENDING = 'PENDING'; // 进行中
const FULFILLED = 'FULFILLED'; // 已成功
const REJECTED = 'REJECTED'; // 已失败
class Promise {
constructor(exector) {
// 初始化状态
this.status = PENDING;
// 将成功、失败结果放在this上,便于then、catch访问
this.value = undefined;
this.reason = undefined;
// 成功态回调函数队列
this.onFulfilledCallbacks = [];
// 失败态回调函数队列
this.onRejectedCallbacks = [];
const resolve = value => {
// 只有进行中状态才能更改状态
if (this.status === PENDING)
{
this.status = FULFILLED;
this.value = value;
// 成功态函数依次执行
this.onFulfilledCallbacks.forEach(fn => fn(this.value));
}
}
const reject = reason => {
// 只有进行中状态才能更改状态
if (this.status === PENDING)
{
this.status = REJECTED;
this.reason = reason;
// 失败态函数依次执行
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
try
{
// 立即执行executor
// 把内部的resolve和reject传入executor,用户可调用resolve和reject
exector(resolve, reject);
} catch (e)
{
// executor执行出错,将错误内容reject抛出去
reject(e);
}
}
then (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected :
reason => { throw new Error(reason instanceof Error ? reason.message : reason) }
const self = this;
return new Promise((resolve, reject) => {
if (self.status === PENDING)
{
self.onFulfilledCallbacks.push(() => {
try
{
// 模拟微任务
setTimeout(() => {
const result = onFulfilled(self.value);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch (e)
{
reject(e);
}
});
self.onRejectedCallbacks.push(() => {
try
{
setTimeout(() => {
const result = onRejected(self.reason);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch (e)
{
reject(e);
}
})
} else if (self.status === FULFILLED)
{
try
{
setTimeout(() => {
const result = onFulfilled(self.value);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
});
} catch (e)
{
reject(e);
}
} else if (self.status === REJECTED)
{
try
{
setTimeout(() => {
const result = onRejected(self.reason);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch (e)
{
reject(e);
}
}
});
}
catch (onRejected) {
return this.then(null, onRejected);
}
static resolve (value) {
if (value instanceof Promise)
{
return value;
} else
{
// 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED
return new Promise((resolve, reject) => resolve(value));
}
}
static reject (reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
static all (promiseArr) {
const len = promiseArr.length
const values = new Array(len)
let count = 0
return new Promise((resolve, reject) => {
promiseArr.forEach((p, i) => {
Promise.resolve(p).then((res) => {
values[i] = res
count++
if (len === count)
{
resolve(values)
}
}).catch(err => {
reject(err)
})
})
})
}
static race (promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(p => {
Promise.resolve(p).then(
val => resolve(val),
err => reject(err),
)
})
})
}
}
// 使用promise封装一个加载图片的方法
function loadImg (url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve(img)
}
img.error = () => {
reject('图片加载失败')
}
img.src = url
})
}
// promise的限制并发数
var urls = [
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting1.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting2.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting3.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting4.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting5.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn6.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn7.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn8.png",
];
function limitLoad (urls, func, limit) {
let sequence = [].concat(urls);
// 初始化 promises 这个"容器"
let promises = sequence.splice(0, limit).map((url, index) => {
return func(url).then(() => {
// 返回下标是为了知道数组中是哪一项最先完成
return index;
});
});
// 注意这里要将整个变量过程返回,这样得到的就是一个Promise,可以在外面链式调用
return sequence
.reduce((pList, url) => {
return pList
.then(() => {
return Promise.race(promises); // 返回已经完成的下标
})
.then(fastestIndex => { // 获取到已经完成的下标
// 将"容器"内已经完成的那一项替换
promises[fastestIndex] = func(url).then(
() => {
return fastestIndex; // 要继续将这个下标返回,以便下一次变量
}
);
})
.catch(err => {
console.error(err);
});
}, Promise.resolve())
}
limitLoad(urls, loadImg, 2)
.then(() => {
console.log("图片全部加载完毕");
})
.catch(err => {
console.error(err);
});
// 把传入的数组按顺序先后执行(红绿灯)
function red () {
console.log("red");
}
function green () {
console.log("green");
}
function yellow () {
console.log("yellow");
}
const light = function (timer, cb) {
return new Promise(resolve => {
setTimeout(() => {
cb()
resolve()
}, timer)
})
}
const step = function () {
Promise.resolve().then(() => {
return light(3000, red)
}).then(() => {
return light(2000, green)
}).then(() => {
return light(1000, yellow)
}).then(() => {
return step()
})
}
step();