Appearance
全局配置
Appearance
function throttle(fn, delay) {
let flag = true,
timer = null
return function(...args) {
let context = this
if(!flag) return
flag = false
clearTimeout(timer)
timer = setTimeout(function() {
fn.apply(context,args)
flag = true
},delay)
}
}
function debounce(fn, delay) {
let timer = null
return function(...args) {
let context = this
if(timer) clearTimeout(timer)
timer = setTimeout(function(){
fn.apply(context,args)
},delay)
}
}
Function.prototype.myCall = function(context) {
if (typeof context === undefined || typeof context === null) {
context = window
}
const symbol = Symbol()
context[symbol] = this
const args = [...arguments].slice(1)
const result = context[symbol](...args)
delete context[symbol]
return result
}
Function.prototype.myApply = function(context) {
if (typeof context === undefined || typeof context === null) {
context = window
}
const symbol = Symbol()
context[symbol] = this
let result
// 处理参数和 call 有区别
if (arguments[1]) {
result = context[symbol](...arguments[1])
} else {
result = context[symbol]()
}
delete context[symbol]
return result
}
Function.prototype.myBind = function (context) {
if (typeof context === undefined || typeof context === null) {
context = window
}
const _this = this
const args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
// 这边的 apply 严谨点可以自己实现
return _this.apply(context, args.concat(...arguments))
}
}
原型链的向上找,找到原型的最顶端,也就是Object.prototype
function my_instance_of(leftVaule, rightVaule) {
if(typeof leftVaule !== 'object' || leftVaule === null) return false;
let rightProto = rightVaule.prototype,
leftProto = leftVaule.__proto__;
while (true) {
if (leftProto === null) {
return false;
}
if (leftProto === rightProto) {
return true;
}
leftProto = leftProto.__proto__
}
}
要点
function _new() {
let obj = {};
let [constructor, ...args] = [...arguments];
obj.__proto__ = constructor.prototype;
let result = constructor.apply(obj, args);
if (result && typeof result === 'function' || typeof result === 'object') {
return result;
}
return obj;
}
某个时间后就去执行某个函数,使用Promise封装
function sleep(fn, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(fn);
}, time);
});
}
let saySomething = (name) => console.log(`hello,${name}`)
async function autoPlay() {
let demo = await sleep(saySomething('xxx'),1000)
let demo2 = await sleep(saySomething('xxxx'),1000)
let demo3 = await sleep(saySomething('xxxxx'),1000)
}
autoPlay()
function deepClone(obj,hash = new WeakMap()){
if(obj instanceof RegExp) return new RegExp(obj);
if(obj instanceof Date) return new Date(obj);
if(obj === null || typeof obj !== 'object') return obj;
//循环引用的情况
if(hash.has(obj)){
return hash.get(obj)
}
//new 一个相应的对象
//obj为Array,相当于new Array()
//obj为Object,相当于new Object()
let constr = new obj.constructor();
hash.set(obj,constr);
for(let key in obj){
if(obj.hasOwnProperty(key)){
constr[key] = deepClone(obj[key],hash)
}
}
//考虑symbol的情况
let symbolObj = Object.getOwnPropertySymbols(obj)
for(let i=0;i<symbolObj.length;i++){
if(obj.hasOwnProperty(symbolObj[i])){
constr[symbolObj[i]] = deepClone(obj[symbolObj[i]],hash)
}
}
return constr
}
function curry(fn,...args){
let fnLen = fn.length,
argsLen = args.length;
//对比函数的参数和当前传入参数
//若参数不够就继续递归返回curry
//若参数够就调用函数返回相应的值
if(fnLen > argsLen){
return function(...arg2s){
return curry(fn,...args,...arg2s)
}
}else{
return fn(...args)
}
}
function sumFn(a,b,c){return a+ b + c};
let sum = curry(sumFn);
sum(2)(3)(5)//10
sum(2,3)(5)//10
let arr = [1,2,[3,4,[5,[6]]]]
console.log(arr.flat(Infinity))//flat参数为指定要提取嵌套数组的结构深度,默认值为 1
//用reduce实现
function fn(arr){
return arr.reduce((prev,cur)=>{
return prev.concat(Array.isArray(cur)?fn(cur):cur)
},[])
}
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
let arr = [2,3,454,34,324,32]
arr.sort(randomSort)
function randomSort(a, b) {
return Math.random() > 0.5 ? -1 : 1;
}
\d 表示匹配一个数字字符(0-9)。
(?=...) 表示这是一个正向先行断言(positive lookahead),它断言在当前位置后面必须跟着某些特定的字符,但这些字符不会包含在匹配结果中。
(?:\d{3})+ 表示这是一个非捕获组(non-capturing group),它匹配一个或多个连续的三位数字。具体来说:
(?:.\d+|$) 表示这是另一个非捕获组,它匹配小数点后跟一个或多个数字,或者字符串的结尾。具体来说:
// 输入仅支持整数
function thousandthInteger(num) {
return num.toString().replace(/\d(?=(?:\d{3})+$)/g, '$&,');
}
// 输入同时支持整数和小数
function thousandth(num) {
return num.toString().replace(/\d(?=(?:\d{3})+(?:\.\d+|$))/g, '$&,');
}
// 正则表达式 /\d(?=(?:\d{3})+(?:\.\d+|$))/g 中,非捕获组 (?:\d{3})+ 和 (?:\.\d+|$) 用于对匹配进行分组,但不捕获这些组的内容,从而简化了正则表达式的结构,并提高了匹配性能。
关于(?:...) 非捕获组 在正则表达式中,(?:...) 表示一个非捕获组(non-capturing group)。非捕获组的主要作用是对正则表达式的匹配进行分组,但不捕获匹配的内容。这与普通的捕获组 (...) 不同,普通的捕获组会捕获匹配的内容,并将其存储在一个编号的捕获组中,可以在后续的正则表达式操作中引用。
使用非捕获组的好处包括:
- 性能优化:非捕获组不会存储匹配的内容,因此在某些情况下可以提高正则表达式的匹配性能。
- 简化引用:在某些复杂的正则表达式中,使用非捕获组可以减少捕获组的数量,从而简化对捕获组的引用。
// 使用捕获组
let regex1 = /(\d{3})-(\d{2})-(\d{4})/;
let match1 = regex1.exec("123-45-6789");
console.log(match1); // 输出 ["123-45-6789", "123", "45", "6789"]
// 在第一个示例中,正则表达式使用了捕获组 (...),因此匹配的结果包含了三个捕获组的内容:"123"、"45" 和 "6789"。
// 使用非捕获组
let regex2 = /(?:\d{3})-(?:\d{2})-(?:\d{4})/;
let match2 = regex2.exec("123-45-6789");
console.log(match2); // 输出 ["123-45-6789"]
// 在第二个示例中,正则表达式使用了非捕获组 (?:...),因此匹配的结果只包含了整个匹配的内容:"123-45-6789",而没有捕获组的内容。
其他方法
function numberWithCommas(x) {
// 转为字符串,按照.拆分
let arr = (x + '').split(".");
// 整数部分再拆分
let int = arr[0].split('');
// 保存小数部分
const fraction = arr[1] || '';
// 记录返回的结果
let r = '';
let len = int.length;
// 倒序遍历
int.reverse().forEach((v, i) => {
// 非第一位且位值是3的倍数,添加','
if(i !== 0 && i % 3 === 0) {
r = v + ',' + r;
} else {
r = v + r;
}
})
// 返回整数部分 + 小数部分
return r + (!!fraction ? '.' + fraction : '');
}
// 使用示例
var number = 1234567.89;
var formattedNumber = numberWithCommas(number);
console.log(formattedNumber); // 输出: 1,234,567.89
console.log((1234567.8911111).toLocaleString('en-US'));
// 1,234,567.891
console.log((1234567.8911111).toLocaleString('en-US', {
maximumFractionDigits:10
}));
// 1,234,567.8911111
function trim(string){
return string.replace(/^\s+|\s+$/g, '')
}
var request = new XMLHttpRequest()
request.open('GET', 'index/a/b/c?name=xxx', true);
request.onreadystatechange = function () {
if(request.readyState === 4 && request.status === 200) {
console.log(request.responseText);
}};
request.send();
function inheritPrototype(Child, Parent) {
// 创建对象,创建父类原型的一个副本
var prototype = Object.create(Parent.prototype);
// 增强对象,弥补因重写原型而失去的默认的constructor 属性
prototype.constructor = Child;
// 指定对象,将新创建的对象赋值给子类的原型
Child.prototype = prototype;
}
测试用例
// 父类初始化实例属性和原型属性
function Father(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Father.prototype.sayName = function () {
alert(this.name);
};
// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function Son(name, age) {
Father.call(this, name);
this.age = age;
}
// 将父类原型指向子类
inheritPrototype(Son, Father);
// 新增子类原型属性
Son.prototype.sayAge = function () {
alert(this.age);
}
var demo1 = new Son("son1", 21);
var demo2 = new Son("son2", 20);
demo1.colors.push("2"); // ["red", "blue", "green", "2"]
demo2.colors.push("3"); // ["red", "blue", "green", "3"]
function Convert(number, base = 2) {
let rem, res = '', digits = '0123456789ABCDEF', stack = [];
while (number) {
rem = number % base;
stack.push(rem);
number = Math.floor(number / base);
}
while (stack.length) {
res += digits[stack.pop()].toString();
}
return res;
}
let isType = (type) => (obj) => Object.prototype.toString.call(obj) === `[object ${type}]`
// let isArray = isType('Array')
// let isFunction = isType('Function')
// console.log(isArray([1,2,3]),isFunction(Map))
function getType(obj) {
// 使用 Object.prototype.toString.call 获取对象类型
const fullType = Object.prototype.toString.call(obj);
// 提取类型名称,slice(8, -1):从索引 8 开始(即跳过 [object 部分),到倒数第一个字符(即 ] 之前)结束
const type = fullType.slice(8, -1);
return type;
}
// 示例
console.log(getType({})); // Object
console.log(getType([])); // Array
console.log(getType(new Date())); // Date
console.log(getType(null)); // Null
console.log(getType(undefined)); // Undefined
console.log(getType(123)); // Number
console.log(getType('abc')); // String
console.log(getType(true)); // Boolean
console.log(getType(function() {})); // Function
let unique = arr => [...new Set(arr)];
Array.prototype.myReduce = function(fn, initVal) {
let result = initVal,
i = 0;
if(typeof initVal === 'undefined'){
result = this[i]
i++;
}
while( i < this.length ){
result = fn(result, this[i])
}
return result
}
// tasks数组的每一项是一个Promise对象
function limitRunTask(tasks, n) {
return new Promise((resolve, reject) => {
let index = 0, finish = 0, start = 0, res = [];
function run() {
if (finish == tasks.length) {
resolve(res);
return;
}
while (start < n && index < tasks.length) {
// 每一阶段的任务数量++
start++;
let cur = index;
tasks[index++]().then(v => {
start--;
finish++;
res[cur] = v;
run();
});
}
}
run();
})
}
两个信号试着彼此竞争,来影响谁先输出
例如有一个分页列表,快速地切换第二页,第三页; 先后请求 data2 与 data3,分页器显示当前在第三页,并且进入 loading; 但由于网络的不确定性,先发出的请求不一定先响应,所以有可能 data3 比 data2 先返回; 在 data2 最终返回后,分页器指示当前在第三页,但展示的是第二页的数据。
在前端开发中,常见于搜索,分页,选项卡等切换的场景。
当发出新的请求时,取消掉上次请求即可
function cancelablePromise(promiseArg) {
let resolve = null
let reject = null
const wrappedPromise = new Promise((_resolve, _reject) => {
resolve = _resolve
reject = _reject
})
promiseArg && promiseArg.then(
val => {
resolve && resolve(val)
},
error => {
reject && reject(error)
}
)
return {
promise: wrappedPromise,
resolve: (value) => {
resolve && resolve(value)
},
reject: (reason) => {
reject && reject(reason)
},
cancel: () => {
resolve = null
reject = null
}
}
}
function onlyResolvesLast(fn) {
// 保存上一个请求的 cancel 方法
let cancelPrevious = null;
const wrappedFn = (...args) => {
// 当前请求执行前,先 cancel 上一个请求
cancelPrevious && cancelPrevious();
// 执行当前请求
const result = fn.apply(this, args);
// 创建指令式的 promise,暴露 cancel 方法并保存
const { promise, cancel } = cancelablePromise(result);
cancelPrevious = cancel;
return promise;
};
return wrappedFn;
}
const fn = (duration) =>
new Promise(r => {
setTimeout(r, duration);
});
const wrappedFn = onlyResolvesLast(fn);
wrappedFn(500).then(() => console.log(1));
wrappedFn(1000).then(() => console.log(2));
wrappedFn(100).then(() => console.log(3));
// 输出 3
// 简洁版pLimit
// 异步逻辑并行执行,并控制并行数量
// 一个队列来保存任务,有两个时机考虑触发任务执行:
// 1. 开始的时候一次性执行最大并发数的任务
// 2. 然后每执行完一个启动一个新的
const shortPLimit = (concurrency) => {
// 传入并发数量
if (!((Number.isInteger(concurrency) || concurrency === Infinity) && concurrency > 0)) {
throw new TypeError('Expected `concurrency` to be a number from 1 and up');
}
// 添加的并发任务要进行排队,所以我们准备一个 queue
const queue = [];
// 记录当前在进行中的异步任务
let activeCount = 0;
// 下一步处理自然就是把活跃任务数量减一,然后再跑一个任务
const next = () => {
activeCount--;
if (queue.length > 0) {
// 如果队列中还有任务,就再跑一个
queue.shift()();
}
};
// 计数,运行这个函数,改变最后返回的那个 promise 的状态
const run = async (fn, resolve, ...args) => {
activeCount++;
// 运行传入的异步函数
const result = (async () => fn(...args))();
resolve(result);
try {
// 等待异步函数执行完
await result;
} catch {}
// 执行完之后进行下一步处理
next();
};
// 把一个异步任务添加到 queue 中,并且只要没达到并发上限就再执行一批任务
const enqueue = (fn, resolve, ...args) => {
queue.push(run.bind(null, fn, resolve, ...args));
// 为了保证并发数量能控制准确,要等全部的微任务执行完再拿 activeCount 和 queue.length 来判断
(async () => {
await Promise.resolve();
if (activeCount < concurrency && queue.length > 0) {
// 如果活跃任务数量小于并发数量,就再跑一个
queue.shift()();
}
})();
};
// 返回一个添加并发任务的函数,我们把它叫做 generator。
// 依旧希望返回返回任务函数的promise的结果
const generator = (fn, ...args) =>
new Promise((resolve) => {
enqueue(fn, resolve, ...args);
});
return generator;
};
// 完整版pLimit
// 异步逻辑并行执行,并控制并行数量
// 一个队列来保存任务,有两个时机考虑触发任务执行:
// 1. 开始的时候一次性执行最大并发数的任务
// 2. 然后每执行完一个启动一个新的
const pLimit = (concurrency) => {
// 传入并发数量
if (!((Number.isInteger(concurrency) || concurrency === Infinity) && concurrency > 0)) {
throw new TypeError('Expected `concurrency` to be a number from 1 and up');
}
// 添加的并发任务要进行排队,所以我们准备一个 queue
const queue = [];
// 记录当前在进行中的异步任务
let activeCount = 0;
// 下一步处理自然就是把活跃任务数量减一,然后再跑一个任务
const next = () => {
activeCount--;
if (queue.length > 0) {
// 如果队列中还有任务,就再跑一个
queue.shift()();
}
};
// 计数,运行这个函数,改变最后返回的那个 promise 的状态
const run = async (fn, resolve, ...args) => {
activeCount++;
// 运行传入的异步函数
const result = (async () => fn(...args))();
resolve(result);
try {
// 等待异步函数执行完
await result;
} catch {}
// 执行完之后进行下一步处理
next();
};
// 把一个异步任务添加到 queue 中,并且只要没达到并发上限就再执行一批任务
const enqueue = (fn, resolve, ...args) => {
queue.push(run.bind(null, fn, resolve, ...args));
// 为了保证并发数量能控制准确,要等全部的微任务执行完再拿 activeCount 和 queue.length 来判断
(async () => {
await Promise.resolve();
if (activeCount < concurrency && queue.length > 0) {
// 如果活跃任务数量小于并发数量,就再跑一个
queue.shift()();
}
})();
};
// 返回一个添加并发任务的函数,我们把它叫做 generator
const generator = (fn, ...args) =>
new Promise((resolve) => {
enqueue(fn, resolve, ...args);
});
// 为 generator 添加一些属性,方便外部获取当前的状态
Object.defineProperties(generator, {
activeCount: {
get: () => activeCount
},
pendingCount: {
get: () => queue.length
},
// 提供了一个清空任务队列的函数
clearQueue: {
value: () => {
queue.length = 0;
}
}
});
return generator;
};
// 测试代码
const limit = pLimit(2);
function asyncFun(value, delay) {
return new Promise((resolve) => {
console.log('start ' + value);
setTimeout(() => resolve(value), delay);
});
}
(async function () {
const arr = [
limit(() => asyncFun('aaa', 2000)),
limit(() => asyncFun('bbb', 3000)),
limit(() => asyncFun('ccc', 1000)),
limit(() => asyncFun('ccc', 1000)),
limit(() => asyncFun('ccc', 1000))
];
const result = await Promise.all(arr);
console.log(result);
})();
Promise/A+规范:
三种状态 pending| fulfilled(resolved) | rejected 当处于pending状态的时候,可以转移到fulfilled(resolved)或者rejected状态 当处于fulfilled(resolved)状态或者rejected状态的时候,就不可变。
必须有一个支持链式调用的异步 then 方法,then接受两个参数(onFulfilled 用来接收promise成功的值, onRejected 用来接收promise失败的原因)且必须返回一个promise
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function Promise(excutor) {
let that = this; // 缓存当前promise实例对象
that.status = PENDING; // 初始状态
that.value = undefined; // fulfilled状态时 返回的信息
that.reason = undefined; // rejected状态时 拒绝的原因
that.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
that.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
function resolve(value) { // value成功态时接收的终值
if(value instanceof Promise) {
return value.then(resolve, reject);
}
// 实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
setTimeout(() => {
// 调用resolve 回调对应onFulfilled函数
if (that.status === PENDING) {
// 只能由pending状态 => fulfilled状态 (避免调用多次resolve reject)
that.status = FULFILLED;
that.value = value;
that.onFulfilledCallbacks.forEach(cb => cb(that.value));
}
});
}
function reject(reason) { // reason失败态时接收的拒因
setTimeout(() => {
// 调用reject 回调对应onRejected函数
if (that.status === PENDING) {
// 只能由pending状态 => rejected状态 (避免调用多次resolve reject)
that.status = REJECTED;
that.reason = reason;
that.onRejectedCallbacks.forEach(cb => cb(that.reason));
}
});
}
// 捕获在excutor执行器中抛出的异常
// new Promise((resolve, reject) => {
// throw new Error('error in excutor')
// })
try {
excutor(resolve, reject);
} catch (e) {
reject(e);
}
}
Promise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
let newPromise;
// 处理参数默认值 保证参数后续能够继续执行
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected =
typeof onRejected === "function" ? onRejected : reason => {
throw reason;
};
if (that.status === FULFILLED) { // 成功态
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try{
let x = onFulfilled(that.value);
resolvePromise(newPromise, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值
} catch(e) {
reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
}
});
})
}
if (that.status === REJECTED) { // 失败态
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(that.reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
if (that.status === PENDING) { // 等待态
// 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
return newPromise = new Promise((resolve, reject) => {
that.onFulfilledCallbacks.push((value) => {
try {
let x = onFulfilled(value);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
that.onRejectedCallbacks.push((reason) => {
try {
let x = onRejected(reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
};
Promise.resolve(value) 可以将任何值转成值为 value 状态是 fulfilled 的 Promise,但如果传入的值本身是 Promise 则会原样返回它。
Promise.resolve(value) {
if (value && value instanceof Promise) {
return value;
} else if (value && typeof value === 'object' && typeof value.then === 'function') {
let then = value.then;
return new Promise(resolve => {
then(resolve);
});
} else if (value) {
return new Promise(resolve => resolve(value));
} else {
return new Promise(resolve => resolve());
}
}
和 Promise.resolve() 类似,Promise.reject() 会实例化一个 rejected 状态的 Promise。但与 Promise.resolve() 不同的是,如果给 Promise.reject() 传递一个 Promise 对象,则这个对象会成为新 Promise 的值。
Promise.reject = function(reason) {
return new Promise((resolve, reject) => reject(reason))
}
Promise.all = function(promiseArr) {
let index = 0, result = []
return new Promise((resolve, reject) => {
promiseArr.forEach((p, i) => {
Promise.resolve(p).then(val => {
index++
result[i] = val
if (index === promiseArr.length) {
resolve(result)
}
}, err => {
reject(err)
})
})
})
}
Promise.race 会返回一个由所有可迭代实例中第一个 fulfilled 或 rejected 的实例包装后的新实例。
Promise.race = function(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(p => {
Promise.resolve(p).then(val => {
resolve(val)
}, err => {
reject(err)
})
})
})
}
冒泡排序的原理如下,从第一个元素开始,把当前元素和下一个索引元素进行比较。如果当前元素大,那么就交换位置,重复操作直到比较到最后一个元素,那么此时最后一个元素就是该数组中最大的数。下一轮重复以上操作,但是此时最后一个元素已经是最大数了,所以不需要再比较最后一个元素,只需要比较到 length - 2 的位置。
function bubble(array) {
checkArray(array);
for (let i = array.length - 1; i > 0; i--) {
// 从 0 到 `length - 1` 遍历
for (let j = 0; j < i; j++) {
if (array[j] > array[j + 1]) swap(array, j, j + 1)
}
}
return array;
}
快排的原理如下。随机选取一个数组中的值作为基准值,从左至右取值与基准值对比大小。比基准值小的放数组左边,大的放右边,对比完成后将基准值和第一个比基准值大的值交换位置。然后将数组以基准值的位置分为两部分,继续递归以上操作。
function sort(array) {
if (!checkArray(array)) return
quickSort(array, 0, array.length - 1);
return array;
}
function quickSort(array, left, right) {
if (left < right) {
swap(array, , right)
// 随机取值,然后和末尾交换,这样做比固定取一个位置的复杂度略低
let indexs = part(array, parseInt(Math.random() * (right - left + 1)) + left, right);
quickSort(array, left, indexs[0]);
quickSort(array, indexs[1] + 1, right);
}
}
function part(array, left, right) {
let less = left - 1;
let more = right;
while (left < more) {
if (array[left] < array[right]) {
// 当前值比基准值小,`less` 和 `left` 都加一
++less;
++left;
} else if (array[left] > array[right]) {
// 当前值比基准值大,将当前值和右边的值交换
// 并且不改变 `left`,因为当前换过来的值还没有判断过大小
swap(array, --more, left);
} else {
// 和基准值相同,只移动下标
left++;
}
}
// 将基准值和比基准值大的第一个值交换位置
// 这样数组就变成 `[比基准值小, 基准值, 比基准值大]`
swap(array, right, more);
return [less, more];
}
function quickSort (arr) {
if (arr.length <= 1) { return arr; }
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++){
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat([pivot], quickSort(right));
};
const arr = [85, 24, 63, 45, 17, 31, 96, 50]
console.log(quickSort(arr)) // [17, 24, 31, 45, 50, 63, 85, 96]