提取url参数,并转化为对象形式。
const url = 'https://alibaba.com?a=1&b=2&c=3#hash'
function queryURLParams(URL) {
const url = URL.split('?')[1]
const urlSearchParams = new URLSearchParams(url)
const params = Object.fromEntries(urlSearchParams.entries())
// const arr = [
// ["0", "a"],
// ["1", "b"],
// ["2", "c"],
// ];
// const obj = Object.fromEntries(arr);
// console.log(obj); // { 0: "a", 1: "b", 2: "c" }
return params
}
console.log(queryURLParams(url))
数组扁平化
// 方法一
arr.flat(Infinity)
// 方法二
const flatten = function(arr) {
return [].concat(...arr.map(v => Array.isArray(v) ? flatten(v) : v))
}
const arr = [1, 2, [3, 4, [5, 6, [7, 8]], 9, 10, [11, 12, [13, 14]]]]
console.log(flatten(arr))
生成随机数组
// 方法一
function randomArr(arr) {
// 范围:[0, 1)
return arr.sort(() => Math.random() - 0.5)
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(randomArr(arr))
// 方法二
function sort(arr) {
for (let i = 0; i < arr.length; i++) {
const randomIndex = parseInt(Math.random() * arr.length) // 范围:[0, arr.length)
;[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]]
}
return arr
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
sort(arr)
两数之和
// 两数之和
function twoSum(nums, target) {
for (let i = 0; i < nums.length; i++) {
const num = nums[i]
const targetIndex = nums.indexOf(target - num)
if (targetIndex > -1 && targetIndex !== i) {
return [i, targetIndex]
}
}
}
const nums = [2, 7, 11, 15]
console.log(twoSum(nums, 9))
质数(只能被1和自身整除的数为质数)
判断一个数是不是质数(只能被1和自身整除的数为质数)
在一般领域,对正整数n,如果用2到 根号n 之间的所有整数去除,均无法整除,则n为质数。 质数大于等于2 不能被它本身和1以外的数整除
// 判断一个数是不是质数(只能被1和自身整除的数为质数)
function isPrimeNumber(num) {
if (num === 1) return false
if (num === 2) return true
for (let i = 2; i < Math.sqrt(num) + 1; i++) {
if (num % i === 0) {
// 在 [2, num) 中找到了可以被整除的数
return false
}
}
return true
}
console.log(isPrimeNumber(97));
打印100以内的所有质数
function printPrimeNum(num) {
const isPrimeNumbers = []
for (let i = 2; i <= num; i++) {
let isPrime = true
for (let j = 2; j < i; j++) {
if (i % j === 0) {
// 不是素数
isPrime = false
break
}
}
if (isPrime) {
isPrimeNumbers.push(i)
}
}
return isPrimeNumbers
}
console.log(printPrimeNum(100))
数组去重
// 数组去重
const arr = [1, 2, 3, 5, 2, 4, 1, 3, 10, 6, 2]
// 方法一 new Set()
const distinct = arr => Array.from(new Set(arr))
// 方法二 filter + indexOf
const distinct = arr => arr.filter((item, index, arr) => arr.indexOf(item) === index)
console.log(distinct(arr));
计算斐波那契数列
// 1, 2, 3, 5, 8, 13, 21, ....
// fn(n) = fn(n - 1) + fn(n - 2),fn(1) = 1,fn(2) = 2
function fibonacci(n) {
// if (n === 1) return 1
// if (n === 2) return 2
if (n <= 2) return n
return fibonacci(n - 1) + fibonacci(n - 2)
}
console.log(fibonacci(10));
计算阶乘
// n! = n * (n - 1)!
// n! = n * (n - 1) * ... * 3 * 2 * 1
// 1! = 1
function factorial(n) {
// 返回的是n的阶乘
if (n === 1) return 1
return n * factorial(n - 1)
}
console.log(factorial(9));
数组排序
1. 快速排序
利用二分法 + 递归的原理
function quickSort(arr) {
// 退出递归的条件:数组中只剩下一个元素时 返回数组
if (arr.length <= 1) return arr
const middleIndex = Math.floor(arr.length / 2)
const middle = arr[middleIndex]
const left = []
const right = []
const center = []
for (let i = 0; i < arr.length; i++) {
if (arr[i] < middle) {
left.push(arr[i])
} else if (arr[i] > middle) {
right.push(arr[i])
} else {
center.push(arr[i])
}
}
return quickSort(left).concat(center, quickSort(right))
}
const arr = [3, 1, 4, 9, 10, 1, 3, 2, 5, 9, 6]
console.log(quickSort(arr))
2. 插入排序
以下是插入排序的基本思想步骤:
- 从第一个元素开始,该元素可以认为已经被排序;
- 取出下一个元素,在已经排序的元素序列中从后向前扫描;
- 如果该元素(已排序)大于新元素,将该元素移到下一位置;
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
- 将新元素插入到该位置后;
- 重复步骤2~5,直到所有元素均排序完毕。
function insertSort(arr) {
for (let i = 1; i < arr.length; i++) {
let j = i - 1
const current = arr[i]
// 必须使用变量将当前值保存起来,不然后面数组下标变化,导致出现问题。
while (j >= 0 && arr[j] > current) {
arr[j + 1] = arr[j]
j--
}
arr[j + 1] = current
}
return arr
}
const arr = [2, 1, 4, 3, 8, 9, 5, 7, 10, 2, 33]
console.log(insertSort(arr))
3. 冒泡排序
function bubbleSort(arr) {
// 不易理解
// for (let i = 0; i < arr.length - 1; i++) {
// for (let j = i + 1; j < arr.length; j++) {
// if (arr[i] > arr[j]) {
// [arr[i], arr[j]] = [arr[j], arr[i]]
// }
// }
// }
// 原数组:[4, 3, 2, 1]
// [4, 3, 2, 1] i = 0, j = 0 --> [3, 4, 2, 1]
// i = 0, j = 1 --> [3, 2, 4, 1]
// i = 0, j = 2 --> [3, 2, 1, 4]
// [3, 2, 1, 4] i = 1 j = 0, 1 --> [2, 3, 1, 4], [2, 1, 3, 4]
// [2, 1, 3, 4] i = 2 j = 0 --> [1, 2, 3, 4]
// [1, 2, 3, 4] i = 3 --> [1, 2, 3, 4]
// 外层遍历控制行,内层遍历控制列
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
}
}
}
return arr
}
const arr = [2, 1, 4, 3, 8, 9, 5, 7, 10, 2, 33, 8]
console.log(bubbleSort(arr))
4. 选择排序
// 思想:查找最小值,记录索引。将未排序列最小值与已排序列的后一位进行交换。
function selectionSort(arr) {
let minIndex
for (let i = 0; i < arr.length; i++) {
minIndex = i
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j
}
}
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]
}
return arr
}
const arr = [2, 1, 4, 3, 8, 9, 5, 7, 10, 2, 33, 8]
console.log(selectionSort(arr))
深拷贝、浅拷贝、赋值
1. 深拷贝
// 方法一
JSON.parse(JSON.stringify())
// 方法二(基础版本)
function deepClone(target, map = new WeakMap()) {
if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof target === 'object') {
// 对于引用数据类型,创建一个对象将所有字段依次添加到对象上并返回
const obj = Array.isArray(target) ? [] : {}
// 检查map中有无克隆过的对象
// 有 - 直接返回
// 没有 - 将当前对象作为key,克隆对象作为value进行存储
// 继续克隆
if (map.get(target)) {
return map.get(target)
}
map.set(target, obj)
for (const key in target) {
obj[key] = deepClone(target[key], map)
}
return obj
} else {
// 对于基本数据类型直接返回
return target
}
}
2. 浅拷贝
// 方法一
const obj = Object.assign({}, target)
// 方法二
const a = { ...obj }
// 方法三
Array.prototype.concat()
const arr2 = arr.concat();
// 方法三
Array.prototype.slice()
const arr3 = arr.slice();
把类数组转换为数组
//通过Array.from方法来实现转换
Array.from(arrayLike)
// 通过扩展运算符
[...arrayLike]
//通过call调用数组的slice方法来实现转换
Array.prototype.slice.call(arrayLike)
//通过call调用数组的splice方法来实现转换
Array.prototype.splice.call(arrayLike,0)
//通过apply调用数组的concat方法来实现转换
Array.prototype.concat.apply([],arrayLike)
括号匹配
const str1 = '{[]}' // true
const str2 = '{[[]}' // false
const str3 = '{[]}[' // false
const str4 = '{[()]}' // true
function isValid(str) {
if (str.length % 2 !== 0) return false
const stack = []
const map = {
')': '(',
']': '[',
'}': '{'
}
for (const s of str) {
if (s === '{' || s === '[' || s === '(') {
// 只要是 {、[、( 就入栈
stack.push(s)
} else {
// 遇到 }、]、) 就匹配出栈
if (stack.pop() !== map[s]) return false
}
}
if (stack.length === 0) return true
}
console.log(isValid(str1))
console.log(isValid(str2))
console.log(isValid(str3))
console.log(isValid(str4))
翻转字符串
// 方法一
function reverseString(str) {
return str.split('').reverse().join('')
}
// 方法二
function reverseString(str) {
let result = ''
const len = str.length
for (let i = len - 1; i >= 0; i--) {
result += str[i]
}
return result
}
const str = 'hello world !'
console.log(reverseString(str))
生成随机16进制颜色
// 方法一 padStart, 补全字符串
function randomHexColor() {
// [0, 256) 向下取整后为 [0, 255)
const red = Math.floor(Math.random() * 256).toString(16).padStart(2, '0')
const green = Math.floor(Math.random() * 256).toString(16).padStart(2, '0')
const blue = Math.floor(Math.random() * 256).toString(16).padStart(2, '0')
return `#${red}${green}${blue}`
}
// 方法二
function randomHexColor() {
return `#${Math.random().toString(16).substring(2, 8)}`
}
// 方法三
function randomHexColor() {
const red = Math.floor(Math.random() * 256)
const green = Math.floor(Math.random() * 256)
const blue = Math.floor(Math.random() * 256)
const opacity = Math.random().toFixed(2)
return `rgba(${red}, ${green}, ${blue}, ${opacity})`
}
console.log(randomHexColor());
获取指定范围内的随机整数
Math.floor // 向下取整
Math.ceil // 向上取整
Math.round // 四舍五入
function rangeRandomNum(min, max) {
// [min, max]
return Math.round(Math.random() * (max - min)) + min
// [min, max)
return Math.floor(Math.random() * (max - min)) + min
// (min, max]
return Math.ceil(Math.random() * (max - min)) + min
// (min, max)
return Math.round(Math.random() * (max - min - 2)) + min + 1
}
console.log(rangeRandomNum(3, 9))
实现 call、apply、bind
Function.prototype.myCall = function(context, ...args) {
// 1. 判断调用myCall的是否为函数
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.myCall - 调用myCall的对象必须是函数!')
}
// 如果没有传入上下文对象,则默认为全局对象
// ES11 引入了 globalThis,它是一个统一的全局对象 (window对象)
context = context || globalThis
// 用Symbol来创建唯一的一个function,防止命名冲突。
const fn = Symbol()
// this 是调用myCall的函数,将函数绑定到上下文对象的新属性上
context[fn] = this
// 传入myCall的多个参数
const result = context[fn](...args)
console.log(context);
// 将增加在上下文对象上的fn方法删除
delete context.fn
return result
}
Function.prototype.myApply = function(context, args = []) {
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.myApply - 调用myApply的对象必须是函数!')
}
if (!Array.isArray(args)) {
throw new TypeError('Function.prototype.myApply - 第二个参数必须为数组!')
}
context = context || globalThis
const fn = Symbol()
context[fn] = this
const result = context[fn](...args)
delete context.fn
return result
}
Function.prototype.myBind = function(context, ...args) {
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.myBind - 调用myBind的对象必须是函数!')
}
// 将要被调用的函数用常量保存起来
const _this = this
// 返回一个匿名函数
return function() {
const fn = Symbol()
context[fn] = _this
// 整合参数并执行
const result = context[fn](...args, ...arguments)
delete context[fn]
return result
}
}
实现 new
function myNew(constructor, ...args) {
// 创建一个空对象
const obj = {}
// 改变 obj 的原型
obj.__proto__ = constructor.prototype
// 使用 obj 来调用构造函数,并传入参数。
const result = constructor.apply(obj, args)
// 构造函数如果有返回值,返回值为引用数据类型,直接返回
// 构造函数如果没有返回值,返回 obj
return typeof result === 'object' ? result : obj
}
const p2 = myNew(Person, 'ps2', 16)
实现发布订阅模式(EventEmitter)、观察者模式(Observer)
// 参考文章
// https://juejin.cn/post/7052637219084828680
class Observer {
constructor() {
this.events = {}
}
on(type, handler) {
if (!this.events[type]) {
this.events[type] = []
}
this.events[type].push(handler)
}
once(type, handler) {
const onceCallback = data => {
handler(data) // 执行回调函数
this.off(type, onceCallback)
}
this.on(type, onceCallback)
}
// 一、只有 type 删除整个type事件
// 二、存在 handler 只删除里面有 handler 的
off(type, handler) {
if (!this.events[type]) return
if (!handler) {
delete this.events[type]
} else {
this.events[type] = this.events[type].filter(cb => cb !== handler)
}
}
emit(type, params) {
if (!this.events[type]) return
this.events[type].forEach(handler => {
handler(params)
})
}
}
const ob = new Observer()
function handlerA(data) {
console.log('getList', data);
}
function handlerB(data) {
console.log('getData', data);
}
ob.on('getListA', handlerA)
ob.on('getListA', handlerB)
ob.on('getData', handlerB)
// ob.off('getData')
// ob.off('getListA', handlerA)
ob.emit('getListA', '这是测试数据A')
ob.emit('getData', '这是测试数据B')
console.log(ob);
实现Promise
1. 基础版
class MyPromise {
// 定义三种状态
static PENGING = 'pending';
static FULFILLED = 'fulfilled';
static REJECT = 'reject';
constructor(executor) {
// 初始化状态
this.status = MyPromise.PENGING;
// 初始化值
this.value = null;
this.reason = null;
// 成功的回调队列
this.onFulfilledCallback = [];
// 失败的回调队列
this.onRejectedCallback = [];
// 执行并绑定this
executor(this.reslove.bind(this), this.reject.bind(this));
}
reslove(value) {
// 状态一旦变更就不会再发生变化。
if (this.status !== MyPromise.PENGING) return;
this.value = value;
this.status = MyPromise.FULFILLED;
this.onFulfilledCallback.length && this.onFulfilledCallback.shift()();
}
reject(reason) {
// 状态一旦变更就不会再发生变化。
if (this.status !== MyPromise.PENGING) return;
this.reason = reason;
this.status = MyPromise.REJECT;
this.onRejectedCallback.length && this.onRejectedCallback.shift()();
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled !== 'function' ? () => {} : onFulfilled;
onRejected = typeof onRejected !== 'function' ? () => {} : onRejected;
return new MyPromise(() => {
if (this.status === MyPromise.PENGING) {
this.onFulfilledCallback.push(() =>
setTimeout(() => onFulfilled(this.value))
);
this.onRejectedCallback.push(() =>
setTimeout(() => onRejected(this.reason))
);
} else if (this.status === MyPromise.FULFILLED) {
onFulfilled(this.value);
} else if (this.status === MyPromise.REJECT) {
onRejected(this.reason);
}
});
}
}
const p1 = new MyPromise((resolve, reject) => {
resolve('p1成功');
console.log('p1-console');
setTimeout(() => {
resolve('p1成功-setTimeout');
reject('p1-失败-setTimeout');
console.log('p1-console-setTimeout');
}, 100);
});
p1.then(
value => {
console.log('value: ', value);
},
reason => {
console.log('reason: ', reason);
}
);
2. 实现all函数
成功的时候返回一个成功的数组;失败的时候则返回最先被reject失败状态的值。
static all(list) {
return new MyPromise((resolve, reject) => {
const result = []
let count = 0
for (let i = 0; i < list.length; i++) {
const p = list[i]
p.then(value => {
result[i] = value
count += 1
if (count === list.length) {
resolve(result)
}
}, reason => {
reject(reason)
})
}
})
}
3. 实现race函数
只要有一个完成,不管成功还是失败,都返回
static race(list) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < list.length; i++) {
const p = list[i]
p.then(value => {
resolve(value)
}, reason => {
reject(reason)
})
}
})
}
完整代码
class MyPromise {
// 定义三种状态
static PENGING = 'pending';
static FULFILLED = 'fulfilled';
static REJECT = 'reject';
constructor(executor) {
// 初始化状态
this.status = MyPromise.PENGING;
// 初始化值
this.value = null;
this.reason = null;
// 成功的回调队列
this.onFulfilledCallback = [];
// 失败的回调队列
this.onRejectedCallback = [];
// 执行并绑定this
executor(this.reslove.bind(this), this.reject.bind(this));
}
reslove(value) {
// 状态一旦变更就不会再发生变化。
if (this.status !== MyPromise.PENGING) return;
this.value = value;
this.status = MyPromise.FULFILLED;
this.onFulfilledCallback.length && this.onFulfilledCallback.shift()();
}
reject(reason) {
// 状态一旦变更就不会再发生变化。
if (this.status !== MyPromise.PENGING) return;
this.reason = reason;
this.status = MyPromise.REJECT;
this.onRejectedCallback.length && this.onRejectedCallback.shift()();
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled !== 'function' ? () => {} : onFulfilled;
onRejected = typeof onRejected !== 'function' ? () => {} : onRejected;
return new MyPromise(() => {
if (this.status === MyPromise.PENGING) {
this.onFulfilledCallback.push(() =>
setTimeout(() => onFulfilled(this.value))
);
this.onRejectedCallback.push(() =>
setTimeout(() => onRejected(this.reason))
);
} else if (this.status === MyPromise.FULFILLED) {
onFulfilled(this.value);
} else if (this.status === MyPromise.REJECT) {
onRejected(this.reason);
}
});
}
// 成功的时候返回一个成功的数组;失败的时候则返回最先被reject失败状态的值。
static all(list) {
return new MyPromise((resolve, reject) => {
const result = []
let count = 0
for (let i = 0; i < list.length; i++) {
const p = list[i]
p.then(value => {
result[i] = value
count += 1
if (count === list.length) {
resolve(result)
}
}, reason => {
reject(reason)
})
}
})
}
static race(list) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < list.length; i++) {
const p = list[i]
p.then(value => {
resolve(value)
}, reason => {
reject(reason)
})
}
})
}
}
const p1 = new MyPromise((resolve, reject) => {
// resolve('p1成功');
// console.log('p1-console');
setTimeout(() => {
resolve('p1成功-setTimeout');
reject('p1-失败-setTimeout');
console.log('p1-console-setTimeout');
}, 100);
});
const p2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p2成功');
// reject('p2失败')
}, 200);
});
const p3 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p3成功');
}, 30);
});
// MyPromise.all([p1, p2, p3]).then(res => {
// console.log('all-成功', res);
// }, reason => {
// console.log('all-失败', reason)
// })
MyPromise.race([p1, p2, p3]).then(res => {
console.log('race-成功', res);
}, reason => {
console.log('race-失败', reason)
})