1. proxy实现vue3双向绑定
const $app = document.querySelector('#app')
let activeEffect
const bucket = new WeakMap()
// track 表示追踪
function track(target, key) {
// activeEffect无值意味着没有执行effect函数, 无法收集依赖, 直接return
if (!activeEffect) return
// 每个target在bucket中都是一个Map类型: key => effect
let depsMap = bucket.get(target)
// 第一次拦截, depsMap不存在, 先创建联系
if (!depsMap) {
bucket.set(target, (depsMap = new Map()))
}
// 根据当前读取的key, 尝试读取key的effect函数
let deps = depsMap.get(key)
if (!deps) {
// deps本质是个Set结构,即一个key可以存在多个effect函数,被多个effect所依赖
depsMap.set(key, (deps = new Set()))
}
// 第一步: 收集依赖, 在读取key时, 将effect函数存储起来
deps.add(activeEffect)
}
// trigger执行依赖
function trigger(target, key) {
// 读取depsMap 其结构是key => effects
const depsMap = bucket.get(target)
if (!depsMap) return
// 真正读取依赖当前属性值key的effects
const effect = depsMap.get(key)
// 挨个执行
effect && effect.forEach(fn => fn())
}
// 统一对外暴露响应式函数
function reactive(state) {
return new Proxy(state, {
get(target, key) {
const value = target[key]
track(target, key)
return value
},
set(target, key, newValue) {
target[key] = newValue
trigger(target, key)
}
})
}
const effect = function (fn) {
activeEffect = fn
fn()
}
const nameObj = reactive({
name: 'fatfish'
})
const ageObj = reactive({
age: 100
})
effect(() => {
$app.innerHTML = `hello ${nameObj.name},are you ${ageObj.age} years old?`
})
setTimeout(() => {
nameObj.name = 'Vue3'
}, 1000);
setTimeout(() => {
ageObj.age = 18
}, 2000);
2. 手写instanceof方法
// 如果 target 为基本数据类型直接返回 false
// 判断 Fn.prototype 是否在 target 的隐式原型链上
const _instanceof = (target, fn) => {
if ((typeof target !== 'object' && typeof target !== 'function') || target === null) return false
let proto = target.__proto__
while (true) {
if (proto === null) return false
if (proto === fn.prototype) return true
proto = proto.__proto__
}
}
function A() {}
const a = new A()
console.log(_instanceof(a, A)); // true
console.log(_instanceof(1, A)); // false
const obj = {name: 'tom'}
console.log(122, Object.getPrototypeOf(obj) === obj.__proto__);
3. 手写数组map方法
// map 中的 exc 接受三个参数,分别是: 元素值、元素下标和原数组
// map 返回的是一个新的数组,地址不一样
// 这里不能直接使用箭头函数,否则无法访问到 this
Array.prototype._map = function (exc) {
const result = []
this.forEach((item, index, arr) => {
result[index] = exc(item, index, arr)
})
return result
}
const a = new Array(2).fill(2)
console.log(a.map((item, index, arr) => item*index + 1)); // [1,3]
console.log(a._map((item, index, arr) => item*index + 1)); // [1,3]
4. 数组filter方法
// filter 中的 exc 接受三个参数,与map一致,主要实现的是数组的过滤功能,会根据 exc 函数的返回值来判断是否“留下”该值。
// filter 返回的是一个新的数组,地址不一致。
Array.prototype._filter = function (exc) {
const result = []
this.forEach((item, index, arr) => {
if (exc(item, index, arr)) {
result.push(item)
}
})
return result
}
const b = [1, 3, 4, 5, 6, 2, 5, 1, 8, 20]
console.log(b._filter(item => item % 2 === 0)); // [4, 6, 2, 8, 20]
5. 手写数组reduce方法
// reduce 接受两个参数,第一个为 exc 函数,第二个为初始值,如果不传默认为 0
// reduce 最终会返回一个值,当然不一定是 Number 类型的,取决于你是怎么计算的,每次的计算结果都会作为下次 exc 中的第一个参数
Array.prototype._reduce = function (exc, initial = 0) {
let result = initial
this.forEach(item => {
result = exc(result, item)
})
return result
}
const b = [1, 3, 4, 5, 6, 2, 5, 1, 8, 20]
console.log(b.reduce((pre, cur) => pre + cur, 0));
console.log(b._reduce((pre, cur) => pre + cur, 0));
6. 手写对象create方法
// Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的隐式原型(__proto__)
// __proto__属性指向的就是他的构造函数的prototype
Object.prototype._create = function (proto) {
const Fn = function () {}
Fn.prototype = proto
return new Fn()
}
function A() {}
const obj = Object.create(A)
const obj2 = Object._create(A)
console.log(obj.__proto__ === A); // true
console.log(obj2.__proto__ === A); // true
7. 手写函数call方法
// call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
// 修改this指向, 相当于给ctx新增一个函数(this指向的函数), 函数可以引用ctx的属性
Function.prototype._call = function (ctx = window, ...args) {
ctx.fn = this
const result = ctx.fn(...args)
delete ctx.fn
return result
}
const obj = {
name: '11',
fun() {
console.log(this.name);
}
}
const obj2 = { name: '22' }
obj.fun() // 11
obj.fun.call(obj2) // 22
obj.fun._call(obj2) // 22
8. 手写原型apply方法
// apply() 方法使用一个指定的 this 值和单独给出的一个或多个数组参数来调用一个函数。
Function.prototype._apply = function (ctx = window, args = []) {
ctx.fn = this
const result = ctx.fn(...args)
delete ctx.fn
return result
}
const obj = {
name: '11',
fun(age) {
console.log(this.name,age);
}
}
const obj2 = { name: '22' }
obj.fun() // 11
obj.fun.apply(obj2,[18]) // 22
obj.fun._apply(obj2,[18]) // 22
9. 手写函数bind方法
// bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
Function.prototype._bind = function (ctx, ...args) {
const _self = this
const bindFn = (...reset) => {
return _self.call(ctx, ...args, ...reset)
}
return bindFn
}
const obj = {
name: '11',
fun() {
console.log(this.name);
}
}
const obj2 = { name: '22' }
obj.fun()
const fn = obj.fun.bind(obj2)
const fn2 = obj.fun._bind(obj2)
fn()
fn2()
10. 手写new关键字
// new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。
const _new = function(constructor) {
const obj = {}
obj.__proto__ = constructor.prototype
const result = constructor.call(obj)
return typeof result === 'object' && result !== null ? result : obj
}
11. 寄生组合式继承
function Parent(name) {
this.name = name
}
Parent.prototype.getName = function () {
return this.name
}
function Son(name,age) {
Parent.call(this, name)
this.age = age
}
Son.prototype.getAge = function () {
return this.age
}
// create => obj.constructor.prototype === proto => new Parent()
Son.prototype.__proto__ = Object.create(Parent.prototype)
const son1 = new Son('shao', 20)
console.log(son1.getName());
console.log(son1.getAge());
12. 发布订阅者模式
// 发布订阅者模式
class EventEmitter {
constructor() {
// key: 事件名, value: callback[] 回调数组
this.events = {}
}
on(name, callback) {
if (typeof callback !== 'function') return console.error('请传入正确的回调函数');
if (this.events[name]) {
this.events[name].push(callback)
} else {
this.events[name] = [callback]
}
}
once(name, callback) {
if (typeof callback !== 'function') return console.error('请传入正确的回调函数');
const onceCallback = (...args) => {
callback(...args)
this.off(name)
}
this.on(name, onceCallback)
}
emit(name, ...args) {
const events = this.events[name]
if (!events) return console.warn(`${name}事件不存在`);
for (const event of events) {
event(...args);
}
}
off(name, callback) {
if (!this.events[name]) return console.warn(`${name}事件不存在`);
if (!callback) {
// 没有callback 就删除整个事件
delete this.events[name]
}
this.events[name] = this.events[name].filter(item => item !== callback)
}
}
13. 观察者模式
// 观察者模式
class Observerd {
constructor() {
this.observerList = []
}
addObserver(observer) {
this.observerList.push(observer)
}
notify() {
this.observerList.forEach(observer => observer.update())
}
}
class Observer {
constructor(doSome) {
this.doSome = doSome
}
update() {
console.log(this.doSome);
}
}
const ob1 = new Observer('我是ob1')
const ob2 = new Observer('我是ob2')
const xiaoBaiShu = new Observerd()
xiaoBaiShu.addObserver(ob1)
xiaoBaiShu.addObserver(ob2)
xiaoBaiShu.notify()
14. 手写promise
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
function MyPromise(fn) {
// 保存初始化状态
var self = this;
// 初始化状态
this.state = PENDING;
// 用于保存 resolve 或者 rejected 传入的值
this.value = null;
// 用于保存 resolve 的回调函数
this.resolvedCallbacks = [];
// 用于保存 reject 的回调函数
this.rejectedCallbacks = [];
// 状态转变为 resolved 方法
function resolve(value) {
// 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
// 保证代码的执行顺序为本轮事件循环的末尾
setTimeout(() => {
// 只有状态为 pending 时才能转变,
if (self.state === PENDING) {
// 修改状态
self.state = RESOLVED;
// 设置传入的值
self.value = value;
// 执行回调函数
self.resolvedCallbacks.forEach(callback => {
callback(value);
});
}
}, 0);
}
// 状态转变为 rejected 方法
function reject(value) {
// 保证代码的执行顺序为本轮事件循环的末尾
setTimeout(() => {
// 只有状态为 pending 时才能转变
if (self.state === PENDING) {
// 修改状态
self.state = REJECTED;
// 设置传入的值
self.value = value;
// 执行回调函数
self.rejectedCallbacks.forEach(callback => {
callback(value);
});
}
}, 0);
}
// 将两个方法传入函数执行
try {
fn(resolve, reject);
} catch (e) {
// 遇到错误时,捕获错误,执行 reject 函数
reject(e);
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
// 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
onResolved =
typeof onResolved === "function"
? onResolved
: function (value) {
return value;
};
onRejected =
typeof onRejected === "function"
? onRejected
: function (error) {
throw error;
};
// 如果是等待状态,则将函数加入对应列表中
if (this.state === PENDING) {
this.resolvedCallbacks.push(onResolved);
this.rejectedCallbacks.push(onRejected);
}
// 如果状态已经凝固,则直接执行对应状态的函数
if (this.state === RESOLVED) {
onResolved(this.value);
}
if (this.state === REJECTED) {
onRejected(this.value);
}
};
15. 手写Promise.all/any/race/allSettled/finally
// 实现Promise.all/race/allSettled/any
// Promise 身上的这些方法返回的都是一个 Promise
// Promise.resolve 接受一个 Promise,若非 promise 则将其变成功状态的 Promise
// Promise.all 有一个失败则返回失败的结果,全部成功返回全成功的数组
Promise.all = function (promiseList = []) {
return new Promise((resolve, reject) => {
if (promiseList.length === 0) return resolve([])
const result = []
let count = 0
for (let i = 0; i < promiseList.length; i++) {
Promise.resolve(promiseList[i]).then(res => {
result[i] = res
count++
// 不能直接通过 result.length 进行比较,因为会存在下标大的先赋值
// 例如 i = 3 第一个返回结果,此时数组变为[empty,empty,empty,res]
if (count === promiseList.length) {
resolve(result)
}
}).catch(e => {
reject(e)
})
}
})
}
// Promise.any 和 Promise.all 相反,全部失败返回失败的结果数组,有一个成功则返回成功结果
// AggregateError,当多个错误需要包装在一个错误中时,该对象表示一个错误。
Promise.any = function (promiseList = []) {
return new Promise((resolve, reject) => {
if (promiseList.length === 0) return resolve([])
const result = []
let count = 0
for (let i = 0; i < promiseList.length; i++) {
Promise.resolve(promiseList[i]).then(res => {
resolve(res)
}).catch(e => {
result[i] = e
count++
if (count === promiseList.length) {
reject(new AggregateError(result))
}
})
}
})
}
// Promise.race 返回第一个成功或失败的结果
Promise.race = function (promiseList = []) {
return new Promise((resolve, reject) => {
if (promiseList.length === 0) return resolve([])
for (let i = 0; i < promiseList.length; i++) {
Promise.resolve(promiseList[i]).then(res => {
resolve(res)
}).catch(e => {
reject(e)
})
}
})
}
// Promise.allSettled 无论成功与否都返回,但是会添加一个 status 字段用于标记成功/失败
Promise.allSettled = function (promiseList = []) {
return new Promise((resolve,reject) => {
if (!promiseList.length) return resolve([])
const result = []
let count = 0
const addRes = (i, data) => {
result[i] = data
count++
if (count === promiseList.length) {
resolve(result)
}
}
for (let i = 0; i < promiseList.length; i++) {
Promise.resolve(promiseList[i]).then(res => {
addRes(i, {status: 'fulfilled', data: res})
}).catch(e => {
addRes(i, {status: 'rejected', data: e})
})
}
})
}
// Promise.finally 调用finally传入的callback函数,callback不论返回什么,都转换为Promise对象,并且与当前调用对象的状态值是一样的。
Promise.finally = function (callback) {
return this.then(value => {
return Promise.resolve(callback()).then(() => value)
}, error => {
return Promise.resolve(callback()).then(() => throw error)
})
}
16. LRU页面置换算法
// LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法[2],选择最近最久未使用的页面予以淘汰。该算法赋予每个页面[3]一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
// LRU页面置换算法选择最近最久未使用的页面予以淘汰
// class写法
class LRUCache {
constructor(capacity) {
// 存储对象
this.map = new Map()
// 最大储存量
this.capacity = capacity
}
get(key) {
if (this.map.has(key)) {
const value = this.map.get(key)
// 删了重新添加, 更新存储位置
this.map.delete(key)
this.map.set(key, value)
return value
}
return -1
}
put(key, value) {
if (this.map.has(key)) {
this.map.delete(key)
}
this.map.set(key, value)
// 超过最大存储量
if (this.map.size > this.capacity) {
// 删除排最后的值
this.map.delete(this.map.keys().next().value)
}
}
}
17. Generator 实现async await
// Generator 函数不同于普通函数,是可以暂停执行的,所以函数名之前要加星号 *,以示区别。
// yield是JS为了解决异步调用的命令。表示程序执行到这里会交出执行权,等待结果返回。它需要在协程Generator 函数中运行。
// 整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明。
function* getResult() {
yield new Promise((resolve, reject)=>{
setTimeout(() => {
resolve(1)
console.log(1);
}, 1000);
})
yield new Promise((resolve, reject)=>{
setTimeout(() => {
resolve(2)
console.log(2);
}, 500);
})
yield new Promise((resolve, reject)=>{
setTimeout(() => {
resolve(3)
console.log(3);
}, 100);
})
}
const gen = getResult()
// gen.next() = {value: yield 返回的数据, done: 迭代器是否走完}
// gen.next().value 就是每一次 yield 之后返回的 Promise
// 嵌套调用
gen.next().value.then(()=>{
gen.next().value.then(()=>{
gen.next()
})
})
// 递归封装
function co(fn) {
const next = fn.next()
// 递归停止条件: 当迭代器到最后一个yield
if (next.done) {
return
}
next.value.then(()=>{
co(fn)
})
}
co(getResult())
18. 虚拟dom 转真实dom
const vnode = {
tag: 'DIV',
attrs: {
id: 'app'
},
children: [{
tag: 'SPAN',
children: [{
tag: 'A',
children: []
}]
},
{
tag: 'SPAN',
children: [{
tag: 'A',
children: []
},
{
tag: 'A',
children: []
}]
}]
}
function render(vnode, container) {
return container.appendChild(_render(vnode));
}
function _render(vnode) {
if (typeof vnode === 'number') {
vnode = String(vnode);
}
//处理文本节点
if (typeof vnode === 'string') {
const textNode = document.createTextNode(vnode)
return textNode;
}
//处理组件
if (typeof vnode.tag === 'function') {
const component = createComponent(vnode.tag, vnode.attrs);
setComponentProps(component, vnode.attrs);
return component.base;
}
//普通的dom
const dom = document.createElement(vnode.tag);
if (vnode.attrs) {
Object.keys(vnode.attrs).forEach(key => {
const value = vnode.attrs[key];
setAttribute(dom, key, value); // 设置属性
});
}
vnode.children.forEach(child => render(child, dom)); // 递归渲染子节点
return dom; // 返回虚拟dom为真正的DOM
}
//实现dom挂载到页面某个元素
const ReactDOM = {
render: (vnode, container) => {
container.innerHTML = '';
return render(vnode, container);
}
}
19. 手写ES5 Set
class mySet {
constructor(iterable) {
// 使用数组来存储Set的每一项元素
this.value = []
// 判断是否使用new调用
this.assert(this instanceof mySet, 'Constructor mySet requires "new"')
// 过滤掉null和undefined
if (this.isDef(iterable)) {
// 是可迭代对象才进行下一步forOf元素添加
this.assert(this.isIterable(iterable), `${iterable} is not iterable`)
// 循环可迭代对象,初始化
this.forOf(iterable, (value) => {
this.add(value)
})
}
}
// 获取s.size时候会调用 size函数,返回value数组的长度
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/get
get size() {
return this.value.length
}
// 使用数组的includes方法判断是否包含value
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
// [ NaN ].includes(NaN)会返回true,正好Set也只能存一个NaN
has(value) {
return this.value.includes(value)
}
// 通过has方法判断value是否存在,不存在则添加进数组,最后返回Set本身,支持链式调用
add(value) {
if (!this.has(value)) {
this.value.push(value)
}
return this
}
// 在删除之前先判断value是否存在用之当做返回值,存在则通过splice方法移除
delete(value) {
let result = this.has(value)
if (result) {
this.value.splice(this.value.indexOf(value), 1)
}
return result
}
// 重新赋值一个空数组,即实现clear方法
clear() {
this.value = []
}
// 通过forOf遍历 values返回的迭代对象,实现forEach
forEach(callback, thisArg) {
this.forOf(this.values(), (value) => {
callback.call(thisArg, value, value, this)
})
}
// 返回一个迭代对象,该对象中的值是Set中的value
keys() {
return new Iterator(this.value)
}
// 同keys
values() {
return this.keys()
}
// 返回一个迭代对象,不同keys和values的是其值是[value, value]
entries() {
return new Iterator(this.value, (value) => [value, value])
}
// 条件进行判断,抛出错误
assert(condition, msg) {
if (!condition) throw new Error(msg)
}
// 过滤掉null和undefined
isDef(value) {
return value != void 0
}
// 简单判断value是否是迭代器对象
isIterable(value) {
return isDef(value) && typeof value[Symbol.iterator] === 'function'
}
// 模拟for of行为, 对迭代器对象进行遍历操作
forOf(iterable, callback, ctx) {
let result
iterable = iterable[Symbol.iterator]()
result = iterable.next()
while (!result.done) {
callback.call(ctx, result.value)
result = iterable.next()
}
}
// 返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。
[Symbol.iterator]() {
return this.values()
}
}
class Iterator {
constructor(arrayLike, iteratee = (value) => value) {
this.value = Array.from(arrayLike)
this.nextIndex = 0
this.len = this.value.length
this.iteratee = iteratee
}
next() {
let done = this.nextIndex >= this.len
let value = done ? undefined : this.iteratee(this.value[this.nextIndex++])
return {done, value}
}
[Symbol.iterator]() {
return this
}
}
20. 手写ES6 Map
class myMap {
constructor(iterable) {
// 使用数组来存储Map的每一项元素
this.value = []
// 判断是否使用new调用
this.assert(this instanceof myMap, 'Constructor myMap requires "new"')
// 过滤掉null和undefined
if (this.isDef(iterable)) {
// 是可迭代对象才进行下一步forOf元素添加
this.assert(this.isIterable(iterable), `${iterable} is not iterable`)
// 循环可迭代对象,初始化
this.forOf(iterable, (value) => {
this.add(value[0], value[1])
})
}
}
has(key) {
return this.value.some(item => item.key === key)
}
// 添加map键值对
set(key, value) {
const oldValue = this.get(key)
if (!oldValue) {
this.value.push({key, value})
} else {
oldValue.value = value
}
return this
};
// 根据key获取value
get(key) {
return this.value.find(item => item.key === key) || null
};
// 根据key删除
delete(key) {
this.value = this.value.filter(item => item.key !== key)
};
// 重新赋值一个空数组,即实现clear方法
clear() {
this.value = []
}
// 获取map键值对个数
size() {
return this.value.length;
};
// 判断map是否为空
isEmpty() {
return this.value.length <= 0;
};
// 返回一个迭代对象,该对象中的值是Map中的key
keys() {
const keys = this.value.map(item => item.key)
return new Iterator(keys)
}
// 返回一个迭代对象,该对象中的值是Map中的value
values() {
const values = this.value.map(item => item.value)
return new Iterator(values)
}
// 返回一个迭代对象,不同keys和values的是其值是[value, value]
entries() {
return new Iterator(this.value, (item) => [item.key, item.value])
}
// 通过forOf遍历 values返回的迭代对象,实现forEach
forEach(callback, thisArg) {
this.forOf(this.entries(), (item) => {
callback.call(thisArg, item.value, item.key, this)
})
}
// 条件进行判断,抛出错误
assert(condition, msg) {
if (!condition) throw new Error(msg)
}
// 过滤掉null和undefined
isDef(value) {
return value != void 0
}
// 简单判断value是否是迭代器对象
isIterable(value) {
return isDef(value) && typeof value[Symbol.iterator] === 'function'
}
// 模拟for of行为, 对迭代器对象进行遍历操作
forOf(iterable, callback, ctx) {
let result
iterable = iterable[Symbol.iterator]()
result = iterable.next()
while (!result.done) {
callback.call(ctx, result.value)
result = iterable.next()
}
}
}
class Iterator {
constructor(arrayLike, iteratee = (value) => value) {
this.value = Array.from(arrayLike)
this.nextIndex = 0
this.len = this.value.length
this.iteratee = iteratee
}
next() {
let done = this.nextIndex >= this.len
let value = done ? undefined : this.iteratee(this.value[this.nextIndex++])
return {done, value}
}
[Symbol.iterator]() {
return this
}
}
var map = new myMap();
map.set("xxyang", 100);
map.set("xxyang", 90);
map.set("xp", "dz");
console.log(map.get('xxyang'));//90
console.log(map.get('xp')); //dz
console.log(map.size());//2
map.delete("xxyang");
console.log(map.size());//1
console.log(map.get("xxyang"));//null
21. 封装原生ajax
/**
* 能够发送get请求以及post请求.
* option 参数对象
* option.type 请求方式get/post
* option.url 请求地址
* option.data 请求参数,规定是key1=value1&key2=value2这种格式
* option.success 请求成功后要执行的回调函数
*/
function ajax(option) {
let promise = new Promise(function (resolve, reject) {
let xhr = new XMLHttpRequest();
if (option.type == 'get') {
option.url += '?';
option.url += option.data;
}
xhr.open(option.type, option.url);
xhr.responseType = option.responseType || 'json';
if (option.type == 'post') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
xhr.onload = function () {
option.success(xhr.response); //实参
}
if (option.type == 'get') {
xhr.send();
} else {
xhr.send(option.data);
}
// 设置状态的监听函数
xhr.onreadystatechange = function () {
if (this.readyState !== 4) return;
// 当请求成功或失败时,改变 promise 的状态
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 设置错误监听函数
xhr.onerror = function () {
reject(new Error(this.statusText));
};
})
return promise
}
22. async-pool 控制并发请求
// asyncPool 控制并发请求
// 手写实现
async function asyncPool(poolLimit, array, iteratorFn) {
const ret = [] // 存储所有异步任务
const executing = [] // 存储正在执行的异步任务
for (const item of array) {
// 调用iteratorFn函数创建异步任务
const p = Promise.resolve().then(() => iteratorFn(item))
ret.push(p) // 保存异步任务
// 当poolLimit值小于或等于总任务个数时, 进行并发控制
if (poolLimit <= array.length) {
// 当任务完成后,从正在执行的任务数组中一处已完成的任务
const e = p.then(() => executing.splice(executing.indexOf(e), 1))
executing.push(e) // 保存正在执行的异步任务
if (executing.length >= poolLimit) {
await Promise.race(executing) // 等待较快的任务执行完成
}
}
}
return Promise.all(ret)
}
// asyncPool的基本使用
const timeout = i => new Promise(resolve => setTimeout(() =>{console.log(i); resolve(i)}, i));
asyncPool(2, [1000, 5000, 3000, 2000], timeout).then(res => {
console.log(res);
})
// asyncPool 这个函数接受三个参数
// poolLimit(数字类型):表示限制的并发数;
// array(数组类型):表示任务数组;
// iteratorFn(函数类型):表示迭代函数,用于实现对每个任务项进行处理,该函数会返回一个 Promise 对象或异步函数。
23. 批量请求函数, 限制并发量
/**
* 实现一个批量请求函数, 能够限制并发量
* 好多请求, 耗时不同, 按照顺序输出, 尽可能保证快, 写一个函数
* @param {Array} reqs 请求数组
* @param {Number} max 最大并发量
* @param {Function} callback
* @returns
*/
const sendRequests = (reqs, max, callback = () => { }) => {
let waitList = [];
let currentNum = 0; // 当前接口下标
let reqDoneNum = 0; // 请求完成接口下标
const results = new Array(reqs.length).fill(false); // 请求结果
/**
* 初始化请求
* @returns
*/
const init = () => {
reqs.forEach(async (reqUrl, index) => {
if (currentNum >= max) {
await new Promise(resolve => waitList.push(resolve))
}
reqHandler(reqUrl, index)
});
}
/**
* 处理请求逻辑
* @param {type} 参数
* @returns {type} 返回值
*/
const reqHandler = async (reqUrl, index) => {
currentNum++;
try {
const result = await fetch(reqUrl)
results[index] = result;
} catch (error) {
results[index] = error
} finally {
currentNum--;
reqDoneNum++;
if (waitList.length) {
waitList[0]();
waitList.shift();
}
if (reqDoneNum === reqs.length) {
callback(results)
}
}
}
init()
}
const allRequest = [
"https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=1",
"https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=2",
"https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=3"
];
sendRequests(allRequest, 2, (res) => console.log(res))
24. fetchWithRetry 自动重试函数
// 实现一个函数, fetchWithRetry 会自动重试3次,任意一次成功直接返回
const fetchWithRetry = async (
url,
options,
retryCount = 0,
) => {
const {MAXRET = 3, ...remainingOptions} = options;
try {
return await fetch(url, remainingOptions);
} catch (error) {
// 如果重试次数没有超过,那么重新调用
if (retryCount < maxRetries) {
return fetchWithRetry(url, options, retryCount + 1);
}
// 超过最大重试次数
throw error;
}
}
// 补充超时和取消
// 创建一个 reject 的 promise
// `timeout` 毫秒
const throwOnTimeout = (timeout) =>
new Promise((_, reject) =>
setTimeout(() =>
reject(new Error("Timeout")),
timeout
),
);
const fetchWithTimeout = (
url,
options = {},
) => {
const {timeout, ...remainingOptions} = options;
// 如果超时选项被指定,那么 fetch 调用和超时通过 Promise.race 竞争
if (timeout) {
return Promise.race([
fetch(url, remainingOptions),
throwOnTimeout(timeout),
]);
}
return fetch(url, remainingOptions);
}
// 取消
const fetchWithCancel = (url, options = {}) => {
const controller = new AbortController();
const call = fetch(
url,
{...options, signal: controller.signal},
);
const cancel = () => controller.abort();
return [call, cancel];
};
30. 闭包--函数柯里化
// 将一个多参数函数转化为多个嵌套的单参数函数。
const curry = function (targetFn) {
return function fn(...rest) {
// 函数的length 一般等于参数的个数
if (targetFn.length === rest.length) {
return targetFn.apply(null, rest);
} else {
// 这里把参数传个新的函数, 下一次调用就带有之前的参数
return fn.bind(null, ...rest);
}
};
};
// 用法
function add(a, b, c, d) {
return a + b + c + d;
}
console.log('柯里化:', curry(add)(1)(2)(3)(4));
31. sleep函数
// 1. 由于js是单线程可以直接用循环阻塞进程
function sleep(delay) {
var start = Date.now()
while (Date.now() - start < delay) {
continue
}
}
function test() {
console.log('while111');
sleep(2000)
console.log('while222');
}
// 2. 使用定时器执行callback
function sleep(delay, callback) {
setTimeout(callback, delay);
}
sleep(2000, () => {
console.log('setTimeout');
})
// 3. 使用Promise
function sleep(delay) {
return new Promise(resolve => setTimeout(resolve, delay);)
}
sleep(1000).then(() => {
console.log('Promise');
})
// 4. 使用generator
function* sleep(delay) {
yield new Promise((resolve, reject) => {
setTimeout(resolve, delay)
})
}
sleep(1000).next().value.then(() => {
console.log('generator');
})
// 5. 使用async await
function sleep(delay) {
return new Promise((resolve) => {
setTimeout(resolve, delay)
})
}
async function test() {
console.log('async await111');
await sleep(2000)
console.log('async await222');
}
32. once函数
// once 函数, 函数返回结果会被缓存下来,只会计算一次。
const once = (fn) => {
let res, isFirst = true
return function (...args) {
if (!isFirst) return res
res = fn.apply(this, args)
isFirst = false
return res
}
}
const f = (x) => x
once(3) // 3
once(4) // 3
33. 累加函数
// 累加函数
function sum(...args) {
let params = args
return function _sum (...newArgs) {
if (newArgs.length === 0) {
return params.reduce((pre, cur) => pre + cur, 0)
} else {
params = [...params, ...newArgs]
return _sum
}
}
}
console.log(sum(1, 2)(3)()); // 6
console.log(sum(1)(2)(3)()); // 6
console.log(sum(1, 2, 4)(4)()); // 11
34. repeat重复调用函数
// repeat 根据间隔时间,调用次数, 重复调用函数
function repeat(fn, times, delay) {
return async function (...args) {
for (let i = 0; i < times; i++) {
await new Promise((resolve, reject) => {
setTimeout(() => {
fn.call(this, ...args)
resolve()
}, delay);
})
}
}
}
const repeatFn = repeat(console.log, 4, 1000)
// 函数调用4次, 每次间隔1s, 打印hello
repeatFn('hello')
35. 去除字符串最少字符
/**
* 去除字符串中出现次数最少的字符,不改变原字符串的顺序
* @param {type} 参数
* @returns {type} 返回值
*/
const changeStr = (str) => {
let obj = {};
const _str = str.split("");
_str.forEach(item => {
if (obj[item]) {
obj[item]++;
} else {
obj[item] = 1;
}
});
var _obj = Object.values(obj).sort()
var min = _obj[0]
for (let key in obj) {
if (obj[key] <= min) {
str = str.replace(new RegExp(key, 'g'),"")
}
}
return str
}
console.log(changeStr("aaabbbcceeff"));
37. 构造函数代数输出题
function Foo() {
Foo.a = function () {
console.log(1);
}
this.a = function () {
console.log(2)
}
}
Foo.prototype.a = function () {
console.log(3);
}
Foo.a = function () {
console.log(4);
}
Foo.a(); // 先找自己再找prototype
let obj = new Foo(); // 实例化重写了Foo.a
obj.a(); // 调this.a
Foo.a();
// 4 2 1
38. 异步函数代码输出顺序
// 同步,promise, process.nexTick, settimeout, async/await代码输出顺序
// 先执行完全部同步任务, promise里面的代码没被resolve和reject包裹都是同步
// 再执行微任务,process.nexTick(需要node环境)、promise、async/await,async/await会等待执行完再往下继续(但是await下一行的代码会等待外面的同步执行完再执行), process.nexTick 早于 promise 执行,
// 最后执行宏任务: settimeout,setInterval, 请求, 回调
async function async1() {
console.log('1');
await async2();
console.log('2');
}
async function async2() {
console.log('3');
}
console.log('4');
setTimeout(function () {
console.log('5');
}, 0);
async1();
new Promise(function (resolve) {
console.log('6');
resolve();
}).then(function () {
console.log('7');
});
process.nextTick(() => console.log('9'))
console.log('8');
// 413682975
39. 箭头函数this指向
const obj = {
fn1: () => console.log(this),
fn2: function () {console.log(this)}
}
obj.fn1();
obj.fn2();
const x = new obj.fn1(); // undefined/window
const y = new obj.fn2(); // obj
40. 坑爹 var 打印结果题
// 0.
var name = '123';
var obj = {
name: '456',
print: function () {
function a() {
console.log(this.name);
}
a();
}
}
obj.print(); // undefined
// 1.
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x)
console.log(b.x)
// 结果:undefined{n:2}
// 首先,a 和 b 同时引用了{n:2}对象,接着执行到 a.x = a = {n:2}语句,
// 尽管赋值是从右到左的没错,但是.的优先级比=要高,所以这里首先执行 a.x,
// 相当于为a(或者 b)所指向的{n:1}对象新增了一个属性 x,即此时对象将变为{n:1;x:undefined}。
// 之后按正常情况,从右到左进行赋值,此时执行 a ={n:2}的时候,a 的引用改变,指向了新对象{n:2},
// 而 b 依然指向的是旧对象。之后执行a.x = {n:2}的时候,并不会重新解析一遍 a,而是沿用最初解析 a.x 时候的 a,
// 也即旧对象,故此时旧对象的 x 的值为{n:2},旧对象为 {n:1;x:{n:2}},它被 b引用着。后面输出 a.x 的时候,
// 又要解析 a 了,此时的 a 是指向新对象的 a,而这个新对象是没有 x 属性的,故访问时输出 undefined;而访问 b.x 的时候,将输出旧对象的x的值,即{n:2}。
// 2.
var obj = {'2': 3, '3': 4, 'length': 2, 'splice': Array.prototype.splice, 'push': Array.prototype.push}
obj.push(1)
obj.push(2)
console.log(obj)
// 结果:[,,1,2], length 为 4伪数组(ArrayLike)
// 3.
var a = 10;
(function () {
console.log(a)
a = 5
console.log(window.a)
var a = 20;
console.log(a)
})()
// 分别为 undefined 10 20,
// 原因是作用域问题,在内部声名 var a = 20;相当于先声明 var a;
// 然后再执行赋值操作,这是在IIFE内形成的独立作用域,如果把 var a=20 注释掉,
// 那么 a 只有在外部有声明,显示的就是外部的A变量的值了。结果A会是1055
// 4.
// 改造下面的代码,使之输出 0 - 9,写出你能想到的所有解法。
for (var i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, 1000)
}
// 答:
// 解法一:
for (let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, 1000)
}
// 解法二:
for (var i = 0; i < 10; i++) {
((i) => {
setTimeout(() => {
console.log(i);
}, 1000)
})(i)
}
41. 删除链表的一个节点
/**
* @param {ListNode} head
* @param {number} val
* @return {ListNode}
*/
var deleteNode = function (head, val) {
// 定义虚拟节点
const res = new ListNode(-1);
// 虚拟节点连接到head
res.next = head;
// 定义p指针,最开始指向虚拟节点
let p = res;
// 从虚拟节点开始遍历链表
while (p?.next) {
// 如果下一个值等于val,则删除下一个值
if (p.next.val === val)
p.next = p.next.next;
p = p.next;
}
return res.next;
};
42. 二叉树
// 二叉树是树结构中一种典型的树状结构,每个节点最多只能有两个子节点,一个是左侧子节点,一个是右侧子节点。
// 二叉树中又有一种比较典型的结构,称为二叉搜索树(BST),它允许左侧节点存储的值比父节点小,右侧存储的值比父节点大(或相等)。
// 二叉树深度遍历
class Node {
constructor(element, parent) {
this.parent = parent // 父节点
this.element = element // 当前存储内容
this.left = null // 左子树
this.right = null // 右子树
}
}
class BST {
constructor(compare) {
this.root = null // 树根
this.size = 0 // 树中的节点个数
this.compare = compare || this.compare
}
compare(a, b) {
return a - b
}
add(element) {
if (this.root === null) {
this.root = new Node(element, null)
this.size++
return
}
// 获取根节点 用当前添加的进行判断 放左边还是放右边
let currentNode = this.root
let compare
let parent = null
while (currentNode) {
compare = this.compare(element, currentNode.element)
parent = currentNode // 先将父亲保存起来
// currentNode要不停的变化
if (compare > 0) {
currentNode = currentNode.right
} else if (compare < 0) {
currentNode = currentNode.left
} else {
currentNode.element = element // 相等时 先覆盖后续处理
}
}
let newNode = new Node(element, parent)
if (compare > 0) {
parent.right = newNode
} else if (compare < 0) {
parent.left = newNode
}
this.size++
}
// 前序遍历
preorderTraversal(visitor) {
const traversal = node => {
if (node === null) return
visitor.visit(node.element)
traversal(node.left)
traversal(node.right)
}
traversal(this.root)
}
// 中序遍历
inorderTraversal(visitor) {
const traversal = node => {
if (node === null) return
traversal(node.left)
visitor.visit(node.element)
traversal(node.right)
}
traversal(this.root)
}
// 后序遍历
posterorderTraversal(visitor) {
const traversal = node => {
if (node === null) return
traversal(node.left)
traversal(node.right)
visitor.visit(node.element)
}
traversal(this.root)
}
// 反转二叉树:无论先序、中序、后序、层级都可以反转
invertTree() {
const traversal = node => {
if (node === null) return
let temp = node.left
node.left = node.right
node.right = temp
traversal(node.left)
traversal(node.right)
}
traversal(this.root)
return this.root
}
}
// 二叉树层序遍历, 每层的节点放到一个数组里
// 给定一个二叉树,返回该二叉树层序遍历的结果,(从左到右,一层一层地遍历)
//例如:
//给定的二叉树是{3, 9, 20,#,#, 15, 7},
//该二叉树层序遍历的结果是[[3], [9, 20], [15, 7]]
var levelOrder = function (root) {
let res = [];
if (root === null) return res;
let list = [];
list.push(root);
while (list.length) {
let curLen = list.length;//上一轮剩下的节点,全属于下一层
let newLevel = [];
for (let i = 0; i < curLen; i++) {//同层所有节点
let node = list.shift();//出列
newLevel.push(node.val);//push进newLevel数组
//左右子节点push进队列
if (node.left) list.push(node.left);
if (node.right) list.push(node.right);
}
res.push(newLevel);//push到res数组
};
return res;
};
console.log(levelOrder(res));
// 二叉树的中序遍历
//递归实现
var inorderTraversal = function (root, array = []) {
if (root) {
inorderTraversal(root.left, array);
array.push(root.val);
inorderTraversal(root.right, array);
}
return array;
};
// 前序遍历
//递归实现
var preorderTraversal = function (root, array = []) {
if (root) {
array.push(root.val);
inorderTraversal(root.left);
inorderTraversal(root.right);
}
return array;
};
// 后序遍历
//递归实现
var postorderTraversal = function (root, array = []) {
if (root) {
inorderTraversal(root.left);
inorderTraversal(root.right);
array.push(root.val);
}
return array;
};
// 重建二叉树
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
//pre和vin分别为前序遍历和中序遍历的数组
var buildTree = function (pre, vin) {
if (pre.length === 0) {return null;}
if (pre.length === 1) {return new TreeNode(pre[0]);}
let value = pre[0];
let index = vin.indexOf(value);
//根据根节点分隔中序遍历的左右子树
let vinLeft = vin.slice(0, index);
let vinRight = vin.slice(index + 1);
//根据中序遍历结果分隔前序遍历的左右子树
let preLeft = pre.slice(1, index + 1);
let preRight = pre.slice(idnex + 1);
let node = new TreeNode(value);
node.left = buildTree(preLeft, vinLeft);
node.right = buildTree(preRight, vinRight);
return node;
};
// 对称的二叉树
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isSymmetric = function (root) {
return isSymmetricalTree(root, root)
};
function isSymmetricalTree(node1, node2) {
if (!node1 && !node2) {
return true;
}
if (!node1 || !node2) {
return false;
}
if (node1.val != node2.val) {
return false;
}
return isSymmetricalTree(node1.left, node2.right) && isSymmetricalTree(node1.right, node2.left)
}
// 二叉树的镜像
var mirrorTree = function (root) {
if (root) {
[root.right, root.left] = [root.left, root.right];
mirrorTree(root.right);
mirrorTree(root.left);
}
return root;
};
43. 多叉树,获取每一层的节点之和
// 多叉树, 获取每一层的节点之和
const res = {
value: 2,
children: [
{value: 6, children: [{value: 1}]},
{value: 3, children: [{value: 2}, {value: 3}, {value: 4}]},
{value: 5, children: [{value: 7}, {value: 8}]}
]
};
const layerSum = function (root) {
let result = [], index = 0;
const level = (root, index) => {
if (!root) return;
if (!result[index]) result[index] = 0;
result[index] += root.value;
if (root.children) root.children.forEach(child => level(child, index + 1))
};
level(root, index);
return result;
};
console.log(levelOrder(res));
46. 让 a==1&&a==2&&a==3 为true
// 如何让 a == 1 && a == 2 && a == 3 返回 true
// 方案一: 利用隐式转换会调用 valueOf
const a = {
value: 1,
valueOf() {
return this.value++
}
}
// 方案二: 在对象 valueOf 函数不存在的情况下会调用 toString 方法
const a = {
value: 1,
toString() {
return this.value++
}
}
// 方案一: 利用Object.defineProperty 在全局 window 上挂载一个 a 属性
let _a = 1
Object.defineProperty(window, 'a', {
get() {
return _a++
}
})
console.log(a == 1 && a == 2 && a == 3); // true
47. 实现 (5).add(3).minus(2) 功能
// 实现 (5).add(3).minus(2) 功能。例: 5 + 3 - 2,结果为 6答:
Number.prototype.add = function (n) {
return this.valueOf() + n;
};
Number.prototype.minus = function (n) {
return this.valueOf() - n;
};
48. 递归实现 flatten 扁平化函数
// 使用迭代的方式实现 flatten 函数。
var arr = [1, 2, 3, [4, 5], [6, [7, [8]]]]
/**
* 使用递归的方式处理
* wrap 内保存结果 ret
* 返回一个递归函数
* @returns
*/
function wrap() {
var ret = [];
return function flat(a) {
for (var item of a) {
if (item.constructor === Array) {
ret.concat(flat(item))
} else {
ret.push(item)
}
}
return ret
}
}
console.log(wrap()(arr));
49. 多维数组扁平化、去重、排序
// 编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
// 答:使用 Set 方法去重,flat(Infinity)扁平化
const newArr = Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>a-b)
console.log(newArr);
// [1,2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14]
50. 洗牌函数,乱序排列
// 洗牌函数, 乱序排列, 保持公平性
const shuffle = (arr) => {
const result = [...arr]
for (let i = result.length; i > 0; i--) {
const index = Math.floor(Math.random() * i)
[result[index], result[i - 1]] = [result[i - 1], result[index]]
}
return result
}
const arr = [1, 2, 3, 4, 5]
console.log(shuffle(arr));
51. 数字翻转函数
// 用 JavaScript 写一个函数,输入 int 型,返回整数逆序后的字符串。
// 如:输入整型 1234,返回字符串“4321”。要求必须使用递归函数调用,
// 不能用全局变量,输入函数必须只有一个参数传入,必须返回字符串。
function fun(num) {
let num1 = num / 10;
let num2 = num % 10;
if (num1 < 1) {
return num;
} else {
num1 = Math.floor(num1);
return `${num2}${fun(num1)}`;
}
}
var a = fun(22345);
console.log(a);
console.log(typeof a);
54. 最小的k个数, 快速排序
const getLeastNumbers = (arr, k) => {
return arr.sort((a, b) => a - b).slice(0, k)
};
// 快速排序
function quickSort(arr) {
if (arr.length <= 1) return arr
var index = Math.floor(arr.length/2)
var middle = arr[index]
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] < middle) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat([middle],quickSort(right))
}
console.log(quickSort([32,45,37,16,2,87])); //弹出[2,16,32,37,45,87]
55. 二维数组全排列[['A', 'B', ...], [1, 2],..]输出['A1', 'A1', ....]
let res = arr.reduce((prev, cur) => {
if (!Array.isArray(prev) || !Array.isArray(cur)) return
if (prev.length === 0) return cur
if (cur.length === 0) return prev
const emptyVal = []
prev.forEach(val => {
cur.forEach(item => {
emptyVal.push(`${val}${item}`)
})
})
return emptyVal
},[])
console.log(res);
56. 把两个数组 ['A1', 'A2'] 和 ['A'],合并为 ['A1', 'A2', 'A']
把两个数组 ['A1', 'A2'] 和 ['A'],合并为 ['A1', 'A2', 'A']
// 请把两个数组 ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'] 和 ['A', 'B', 'C', 'D'],
// 合并为 ['A1', 'A2', 'A', 'B1', 'B2', 'B', 'C1', 'C2', 'C', 'D1', 'D2', 'D']。
function concatArr(arr1, arr2) {
const arr = [...arr1];
let currIndex = 0;
for (let i = 0; i < arr2.length; i++) {
const RE = new RegExp(arr2[i])
while (currIndex < arr.length) {
++currIndex
if (!RE.test(arr[currIndex])) {
arr.splice(currIndex, 0, arr2[i])
break;
}
}
}
return arr
}
var a1 = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2']
var a2 = ['A', 'B', 'C', 'D']
const arr = concatArr(a1, a2)
console.log(a1)
// ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2']
console.log(a2)
// ['A', 'B', 'C', 'D']
console.log(arr)
// ['A1', 'A2', 'A', B1', 'B2', 'B', C1', 'C2', 'C', D1','D2','D']
57. 随机生成长度10整数, 排列成[[2,3,5],[10,12],[30]]
随机生成长度10整数, 排列成[[2,3,5],[10,12],[30]]
// 随机生成一个长度为 10 的整数类型的数组,
// 例如 [2, 10, 3, 4, 5, 11, 10, 11, 20],将其排列成一个新数组,要求新数组形式如下,
// 例如 [[2, 3, 4, 5], [10, 11], [20]]。
function formArray(length, base = 100) {
const arr = Array.from({length: num}, () => Math.floor(Math.random() * base));
const sortedArr = Array.from(new Set(arr)).sort((a, b) => a - b);
const map = new Map();
sortedArr.forEach((v) => {
const key = Math.floor(v / 10);
const group = map.get(key) || [];
group.push(v);
map.set(key, group);
});
return [...map.values()];
}
const res = formArray(10, 20);
console.log(res);
58. 返回字符串参数的所有排列组合
返回字符串参数的所有排列组合
// 要求以数组的形式返回字符串参数的所有排列组合。
// 注意:
// 字符串参数中的字符无重复且仅包含小写字母
// 返回的排列组合数组不区分顺序
const _permute = string => {
const result = []
const map = new Map()
const dfs = (path) => {
if (path.length == string.length) {
result.push(path)
return
}
for (let i = 0; i < string.length; i++) {
if (map.get(string[i])) continue
map.set(string[i], true)
path += string[i]
dfs(path)
path = path.substring(0, path.length - 1)
map.set(string[i], false)
}
}
dfs('')
return result
}
console.log(_permute('abc'));
59. 通过数值找数组名称
通过数值找数组名称
// 比如这个函数输入一个1,那么要求函数返回A
const A = [1, 2, 3];
const B = [4, 5, 6];
const C = [7, 8, 9];
const test = (num) => {
const newArr = [A, B, C];
// return newArr.filter(item => item.includes(num))
let i = 0;
while (i < newArr.length) {
if (newArr[i].includes(num)) return newArr[i]
i++
}
return []
}
console.log(test(5));
60. 返回相加数字的下标的数组
返回相加数字的下标的数组
// 给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]答:
function anwser(arr, target) {
let map = {}
for (let i = 0; i < arr.length; i++) {
map[arr[i]] = i
}
for (let i = 0; i < arr.length; i++) {
var d = target - arr[i]
if (map[d]) {
return [i, map[d]]
}
}
return new Error('404 not found')
}
61. 字符串S查找字符串T, 返回下标
字符串S查找字符串T, 返回下标
// 实现一个字符串匹配算法,从长度为 n 的字符串 S中,查找是否存在字符串 T,T 的长度是 m,若存在返回所在位置。
const find = (S, T) => {
if (S.length < T.length)
return -1
for (let i = 0; i < S.length; i++) {
if (S.slice(i, i + T.length) === T)
return i
}
return -1
}
62. 将所有 0 移动到数组的末尾,保持非零顺序
将所有 0 移动到数组的末尾,保持非零顺序
// 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
// 示例:输入: [0,1,0,3,12] 输出: [1,3,12,0,0]
// 代码说明: 必须在原数组上操作,不能拷贝额外的数组。 尽量减少操作次数答:
function zeroMove(array) {
let len = array.length;
let j = 0;
for (let i = 0; i < len - j; i++) {
if (array[i] === 0) {
array.push(0);
array.splice(i, 1);
i--;
j++;
}
}
return array;
}
63. 打印出 1 - 10000 之间的所有对称数
打印出 1 - 10000 之间的所有对称数
// 打印出 1 - 10000 之间的所有对称数例如:121、1331 等
function getNum (num) {
const arr = [...Array(num).keys()]
return arr.filter(x => {
return x.toString().length > 1 && x === Number(x.toString().split('').reverse().join(''))
})
}
64. 将数组中的元素向右移动 k 个位置
将数组中的元素向右移动 k 个位置
// 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
// 示例:输入: [-1, -100, 3, 99] 和 k = 2 输出: [3, 99, -1, -100]
// 解释: 向右旋转 1 步: [99, -1, -100, 3]
// 向右旋转 2 步: [3, 99, -1, -100]答:
function rotate(arr, k) {
const len = arr.length
const step = k % len
return arr.slice(-step).concat(arr.slice(0, len - step))
}
// rotate([1, 2, 3, 4,5, 6], 7) => [6, 1, 2, 3, 4, 5]
65. 存 1 到 12 月份的销售额
存 1 到 12 月份的销售额
// 某公司 1 到 12 月份的销售额存在一个对象里面如下:{1:222, 2:123, 5:888},
// 请把数据处理为如下结构:[222, 123, null, null, 888, null, null, null, null, null, null, null]。
function getSale (obj) {
return Array.from({length:12}).map((_,index) => obj[index + 1] || null)
}
let obj = {1: 222, 2: 123, 5: 888};
console.log(getSale(obj))
66. 找出两个有序数组的中位数
找出两个有序数组的中位数
// 给定两个大小为 m 和 n 的有序数组 nums1 和nums2。请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log(m+n))。
// 示例 1:nums1 = [1, 3] nums2 = [2]中位数是 2.0
// 示例 2:nums1 = [1, 2] nums2 = [3, 4]中位数是(2 + 3) / 2 = 2.5答:
const findMedianSortedArrays = function (nums1: number[], nums2: number[]) {
const lenN1 = nums1.length;
const lenN2 = nums2.length;
const median = Math.ceil((lenN1 + lenN2 + 1) / 2);
const isOddLen = (lenN1 + lenN2) % 2 === 0;
const result = new Array < number > (median);
let i = 0;
// pointer for nums1let j = 0;
// pointer for nums2
for (let k = 0; k < median; k++) {
if (i < lenN1 && j < lenN2) {// tslint:disable-next-line:prefer-conditional-expressionif (nums1[i] < nums2[j]) {result[i + j] = nums1[i++];
} else {
result[i + j] = nums2[j++];
}
} else if (i < lenN1) {
result[i + j] = nums1[i++];
} else if (j < lenN2) {
result[i + j] = nums2[j++];
}
if (isOddLen) {
return (result[median - 1] + result[median - 2]) / 2;
} else {
return result[median - 1];
};
}
67.青蛙跳台
青蛙跳台
function jump(n){
if (n == 0){
return 0;
}
if (n == 1){
return 1 ;
}
if (n == 2){
return 2;
}
if (n>=3){
return jump(n-1) + jump(n-2);
}
}
68.获取数组[-2,3,9-1,2-1,5,-5-2]最大子集数组的和
获取数组[-2,3,9-1,2-1,5,-5-2]最大子集数组的和
function getNums(nums) {
const len = nums
let max = nums[0]
for (let i =0; i < len; i++) {
if (i >0 && nums[i - 1] > 0) {
nums[i] +=nums[i - 1]
}
if (nums[i] > max) {
max = nums[i]
}
}
return max
}
69. 数组排序
- 选择排序
利用 选择排序 排序
function SelectSort(ary){
for(var i = 0; i < ary.length - 1; i++){
for(var j = i + 1; j < ary.length; j++){
if(ary[i] > ary[j]){
var tmp = ary[i];
ary[i] = ary[j];
ary[j] = tmp;
}
}
}
return ary;
}
- 插入排序
利用 插入排序 排序
function InsertSort(ary){
var temp;
for (var i = 1; i < ary.length; i++) {
for (var j = i-1; j >=0; j--) {
if (ary[j+1] < ary[j]) {
temp = ary[j+1];
ary[j+1] = ary[j];
ary[j] = temp;
} else if (ary[j+1] >= ary[j]) {
break;
}
}
}
return ary;
}
- 快速排序
利用 快速排序 排序
var quickSort = function(ary) {
if (ary.length <= 1) {
return ary;
}
var pivotIndex = Math.floor(ary.length / 2);
var pivot = ary.splice(pivotIndex, 1)[0];
var left = [];
var right = [];
for (var i = 0; i < ary.length; i++) {
if (ary[i] < pivot) {
left.push(ary[i]);
} else {
right.push(ary[i]);
}
}
return quickSort(left).concat([pivot], quickSort(right));
};
- 希尔排序
利用 希尔排序 排序
function ShellSort(ary) {
var len = ary.length,
temp,
gap = 1;
while(gap < len/3) { //动态定义间隔序列
gap =gap*3+1;
}
for (gap; gap > 0; gap = Math.floor(gap/3)) {
for (var i = gap; i < len; i++) {
temp = ary[i];
for (var j = i-gap; j >= 0 && ary[j] > temp; j-=gap) {
ary[j+gap] = ary[j];
}
ary[j+gap] = temp;
}
}
return ary;
}
- 计数排序
利用 计数排序 排序
function CountSort(ary){
let obj={};
for(let i=0; i<ary.length; i++){
if(!obj[ary[i]]){
obj[ary[i]]=1;
}else{
obj[ary[i]]++;
}
}
let index=0;
//遍历对象属性名,按顺序放回覆盖原数组
for(let key in obj){
while(obj[key]>0){
ary[index]=Number(key);
obj[key]--;
index++
}
}
return ary;
}
- 冒泡排序
利用 冒泡排序 排序
//冒泡排序
function maopao(arr) {
const array = [...arr]
let isOk = true
for (let i = 0, len = array.length; i < len - 1; i++) {
isOk = true
for (let j = i + 1; j < len; j++) {
if (array[i] > array[j]) {
[array[i], array[j]] = [array[j], array[i]]
isOk = false
}
}
if (isOk) {
break
}
}
return array
}
70. 数组去重
- 双重for循环
利用 双重for循环 数组去重
function newArrFn (arr) {
// 创建一个新的空数组
let newArr = []
for(let i = 0;i<arr.length;i++){
// 设置一个开关,如果是true,就存进去,不是就不存
let flag = true
for(let j = 0;j<newArr.length;j++){
console.log('循环比较次数:'); //40
// 原数组和新数组作比较,如果一致,开关变为 false
arr[i] === newArr[j] ? flag = false : flag
};
flag ? newArr.push(arr[i]) : newArr
};
return newArr
}
- for + indexOf
利用 for + indexOf 数组去重
function newArrFn2() {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
console.log('for结合indexOf()的循环次数'); //10
// newArr.indexOf(arr[i]) === -1 ? newArr.push(arr[i]) : newArr;
//ES6新方法 includes():返回布尔值,表示是否找到了参数字符串
newArr.includes(arr[i]) ? newArr : newArr.push(arr[i]);
}
return newArr;
}
- includes
利用 includes 数组去重
function newArrFn3() {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
//ES6新方法 includes():返回布尔值,表示是否找到了参数字符串
newArr.includes(arr[i]) ? newArr : newArr.push(arr[i]);
}
return newArr;
}
- Set
利用 Set 数组去重
function newArrFn4() {
// console.log(new Set(arr));
let newArr = ([...new Set(arr)])
return newArr;
}
- Set + Array.from
利用 Set + Array.from 数组去重
function newArrFn5() {
// let newArr = ([...new Set(arr)])
let newArr = Array.from(new Set(arr))
return newArr;
}
- sort
利用 sort 数组去重
function newArrFn6() {
arr = arr.sort(); //数组排序 缺点:会修改数组的数据位置
let newArr=[];
for(let i=0;i<arr.length;i++){
console.log('循环的次数');
arr[i]===arr[i+1]?newArr:newArr.push(arr[i])
}
return newArr;
}
- filter + indexOf
利用 filter + indexOf 数组去重
function newArrFn (arr) {
// 利用indexOf检测元素在数组中第一次出现的位置是否和元素现在的位置相等,
// 如果相等,说明数组中没有重复的
return Array.prototype.filter.call(arr, function (item, index) {
return arr.indexOf(item) === index
})
}
- for + object
利用 for + object 数组去重
function newArrFn (arr) {
// 利用对象属性名不能重复这一特点
// 如果对象中不存在,就可以给 push 进去
let newArr = []
let obj = {}
for(let i = 0;i<arr.length;i++){
if (!obj[arr[i]]) {
newArr.push(arr[i])
obj[arr[i]] = 1
} else {
obj[arr[i]] ++
}
};
return newArr
}
- for + splice
利用 for + splice 数组去重
function newArrFn (arr) {
for(let i = 0; i<arr.length; i++){
for(let j = i + 1; j<arr.length; j++){
if (arr[i] === arr[j]) {
arr.splice(j,1);
j--
}
};
}
return arr
}
- filter + indexOf
利用 filter + indexOf 数组去重
function newArrFn (arr) {
return arr.filter((item, index) => {
return arr.indexOf(item) === index
})
}
- Map
利用 Map 数组去重
function newArrFn (arr) {
let newArr = []
let map = new Map()
for(let i = 0;i<arr.length;i++){
// 如果 map里面不包含,就设置进去
if (!map.has(arr[i])) {
map.set(arr[i], true)
newArr.push(arr[i])
}
};
return newArr
}
- reduce
利用 reduce 数组去重
function newArrFn (arr) {
let newArr = []
return arr.reduce((prev, next,index, arr) => {
// 如果包含,就返回原数据,不包含,就把新数据追加进去
return newArr.includes(next) ? newArr : newArr.push(next)
}, 0)
}
- 数组快慢指针原地去重
数组快慢指针原地去重
// 有序数组原地去重
// 快慢指针
const res = [0, 0, 1, 1, 2, 2, 2, 2, 4, 4, 5, 5, 6]
const removeDuplicates = (nums) => {
let slow = 0, fast = 1;
if (nums.length === 0) return
while (fast < nums.length) {
if (nums[fast] !== nums[slow]) {
slow++;
nums[slow] = nums[fast];
}
fast++;
}
return nums.slice(0, slow + 1);
}