js中sort函数的底层实现机制?
js中sort内置多种排序算法,是根据要排序数的乱序程度来决定使用哪一种排序方法。V8 引擎 sort 函数只给出了两种排序 InsertionSort 和 QuickSort,长度小于20的使用InsertionSort(插入排序),大于20的数组则使用 QuickSort(快速排序)
JS排序
冒泡排序
1、比较相邻的两个元素,如果前一个比后一个大,则交换位置。
2、比较完第一轮的时候,最后一个元素是最大的元素。
3、这时候最后一个元素是最大的,所以最后一个元素就不需要参与比较大小。
function bSort(arr) {
var len = arr.length;
for (var i = 0; i < len-1; i++) {
for (var j = 0; j < len - 1 - i; j++) {
// 相邻元素两两对比,元素交换,大的元素交换到后面
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
return arr;
}
快速排序
1. 在数组中,找一个基准点P,然后splice出去;
2. 将数组中小于改基准点的数据放在一个数组,大于基准点的数据放在一个数组;
3. 对左边的数组进行递归操作,对右边的数组递归操作,最后concat起来,并返回
function quickSort(arr){
if(arr.length<=1){ //如果数组中只有一位数,返回数组
return arr;
}
var mNumIndex = Math.floor(arr.length/2); //取基准值的下标
var mNum = arr.splice(mNumIndex,1)[0]; //取基准值
var left = []; //左边数组
var right = []; //右边数组
for(var i=0;i<arr.length;i++){
if(arr[i]<mNum){ //如果数组小于基准值,放在左边数组
left.push(arr[i]);
}else{ ///否则
right.push(arr[i]);
}
}
return quickSort(left).concat([mNum],quickSort(right));
//返回左边数组+基准值+右边数组
}
插入排序
function insertSort(arr) {
let length = arr.length;
for(let i = 1; i < length; i++) {
let temp = arr[i];
for(let j = i; j > 0; j--) {
if(arr[j] >= arr[j-1]) {
break; // 当前考察的数大于前一个数,证明有序,退出循环
}
arr[j] = arr[j-1]; // 将前一个数复制到后一个数上
}
arr[j] = temp; // 找到考察的数应处于的位置
}
return arr;
}
选择排序(实现思路跟冒泡排序差不多, 可以说是冒泡排序的衍生版本
第一次从待排序的[数据元素]中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法
function selectionSort(arr) {
var len = arr.length
var minIndex, temp
for (i = 0; i < len - 1; i++) {
minIndex = i
for (j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j
}
}
temp = arr[i]
arr[i] = arr[minIndex]
arr[minIndex] = temp
}
return arr
}
数组去重
ES6: Array.from(new Set(arr))
function unique(arr) {
var newArr = []
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i])
}
}
return newArr
}
function unique(arr) {
var result = [];
var obj = {};
for(var i =0; i<arr.length; i++){
if(!obj[arr[i].key]){
result.push(arr[i]);
obj[arr[i].key] = true;
}
}
return result
}
数组平铺
let ary = [1, [2, [3, [4, 5]]], 6];// -> [1, 2, 3, 4, 5, 6]
let str = JSON.stringify(ary);
一、递归调用
function flat(arr){
var newArr = [];
for(var i= 0; i<arr.length;i++){
if(Array.isArray(arr[i])){
newArr = newArr.concat(flat(arr[i]))
} else {
newArr.push(arr[i]);
}
}
return newArr;
}
function flatten(arr) {
return arr.reduce(function(prev, next){
return prev.concat(Array.isArray(next) flatten(next) : next)
}, [])
}
二、replace + split
ary = str.replace(/(\[|\])/g, '').split(',')
三、 toString
function flatten(arr) {
return arr.toString().split(',').map(function(item){
return +item
})
}
四、调用ES6中的flat方法
ary = arr.flat(Infinity);
实现bind 或者 call
Function.prototype.bind= function(obj){
var _self = this, args = arguments;
return function() {
_self.apply(obj, Array.prototype.slice.call(args, 1));
}
}
Function.prototype.myCall = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
const args = [...arguments].slice(1)
const result = context.fn(...args)
delete context.fn
return result
}
js 实现once 方法
function runOnce(fn, context) { //控制让函数只触发一次
return function () {
try {
fn.apply(context || this, arguments);
}
catch (e) {
console.error(e);//一般可以注释掉这行
}
finally {
fn = null;
}
}
}
var obj = {name: "狗子", age: 24};
var canOnlyFireOnce = runOnce(function () {
console.log("你好" + this.name);
}, obj);
canOnlyFireOnce(); //你好天涯孤雁
canOnlyFireOnce(); // nothing
如何统计网页里出现多少种html标签
var doms = document.getElementsByTagName('*')
var obj = {}
var ret = []
for (let i = 0; i < doms.length; i++) {
var name = doms[i].nodeName
if(!obj[name]){
ret.push(name)
obj[name] = true
}
}
console.log(ret.length)
let names = [...document.getElementsByTagName('*')].map(v=>v.nodeName)
console.log(new Set(names).size)
获取dom下的所有节点
function getDom(dom) {
var list = []
var domChildren = dom.childNodes
for (var i = 0; i < domChildren.length; i++) {
// nodeType === 1 是元素节点,2是属性节点。
if (domChildren[i].nodeType === 1) {
list.push(domChildren[i])
var retArr = getDom(domChildren[i])
list = list.concat(retArr)
}
}
return list
}
getDom(document.querySelector('body'))
Promise相关
实现一个函数delay(alert,3,4000)(“hello”),每间隔4秒打印一次“hello”,打印三次。
function delay(fn,nums,times){
return async function(content){
for(var i = 0; i<nums; i ++){
await new Promise(resolve =>{
setTimeout(()=>{
fn.call(this, content);
resolve(true);
}, times)
})
}
}
}
delay(console.log, 3, 4000)('hello');
手动实现promise.all的方法
function PromiseAll(arr) {
return new Promise(function(resolve,reject){
//判断参数类型
if(!Array.isArray(arr)){
return reject(new TypeError('arguments muse be an array'))
}
var count = 0,
length = arr.length,
newArr = new Array(length);
for(let i = 0; i < length; i++){
Promise.resolve(arr[i]).then((value)=>{
count++;
newArr[i] = value;
if(count == length){
return resolve(newArr)
}
}).catch((error)=>{
return reject(error)
})
}
})
}
let p1 = Promise.reject(1),
p2 = Promise.resolve(2),
p3 = Promise.resolve(3);
PromiseAll([p1, p2, p3]).then((res) =>{
console.log(res, 'res')
}, (err) =>{
console.log(err, 'err')
})
promise 超时
function promiseTimeout(promise, delay){
let timeout = new Promise(function (reslove, reject){
setTimeout(function (){
reject('超时啦~')
}, delay)
})
return Promise.race([timeout, promise])
}
Person("Li");
// 输出: Hi! This is Li!
Person("Dan")
.sleep(10)
.eat("dinner");
// 输出:
// Hi! This is Dan!
// 等待 10 秒..
// Wake up after 10
// Eat dinner~
Person("Jerry")
.eat("dinner")
.eat("supper");
// 输出:
// Hi This is Jerry!
// Eat dinner~
// Eat supper~
Person("Smith")
.sleepFirst(5)
.eat("supper");
// 输出:
// 等待 5 秒
// Wake up after 5
// Hi This is Smith!
// Eat supper
callback 到 promise 通用 promisify 方法
function promisify (originFn) {
return function(...args) {
return new Promise( (resolve, reject) => {
let cb = (err, data) => err ? reject(err) : resolve(data);
originFn.call(this, ...args, cb)
} )
}
}
let readFilePromisify = promisify(fs.readFile);
readFilePromisify(fileName, 'utf8')
.then( (data) => {
console.log(data);
}).catch( (err) => {
console.error(err);
});
JS sleep 队列
class Zeus{
constructor(name){
this.name = name
this.queue = [start];
function start(resolve) {
console.log(`Hi! This is ${name}`);
resolve();
}
setTimeout(() => {
let start = Promise.resolve();
for (let i = 0; i < this.queue.length; i++) {
start = start.then(() => new Promise(this.queue[i]));
}
})
}
eat(food) {
function eatFood(resolve) {
console.log(`Eat ${food}~`);
resolve();
}
this.queue.push(eatFood);
return this;
}
sleepFn(time){
return function fn(resolve){
console.log(`等待${time}秒...`);
setTimeout(()=>{
console.log(`Wake up after ${time}`)
return resolve();
}, time * 1000);
}
}
sleep(time) {
this.queue.push(this.sleepFn(time));
return this;
}
sleepFirst(time) {
this.queue.splice(0, 0, this.sleepFn(time));
return this;
};
}
function Person(){
return new Zeus(...arguments);
}
无重复字符的最长子串
function lengthOfLongestSubstring(str) {
//遇到一个重复元素时,结束
let max = 0;
let countArr = [];
let arr = str.split('');
for (let i = 0; i < arr.length; i++) {
countArr.push(arr[i]);
//没结尾
if (arr[i+1] != undefined) {
//不包含下一个元素
if (countArr.indexOf(arr[i+1]) < 0) {
if (max < countArr.length) {
max = countArr.length;
}
}
//包含下一个元素
else {
countArr.splice(0, countArr.indexOf(arr[i+1])+1);
//console.log(countArr);
}
}
//结束
else {
max++;
}
}
return max;
};
const lengthOfLongestSubstring = (s) => {
if (!s) return 0;
let map = new Map(),
l = 0,
r = 0,
len = s.length,
size = 0;
while (r < len) {
if (map.has(s[r])) {
l = Math.max(l, map.get(s[r]) + 1);
}
map.set(s[r], r);
r++;
size = Math.max(size, r - l);
}
return size
};
斐波那契数列
function Fibonacci (n) {
if ( n <= 1 ) {return 1};
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
function Fibonacci3(n){
if (n===1 || n===2) {
return 1;
}
let ac1 = 1, ac2 = 1;
for (let i = 2; i < n; i++){
[ac1, ac2] = [ac2, ac1 + ac2];
}
return ac2;
}
js 爬楼梯 动态规划
var climbStairs1 = function(n) {
if(n<=2){
return n;
}
return climbStairs1(n-1) + climbStairs1(n-2);
};
console.log(climbStairs1(3));
动态规划: var climbStairs3 = function(n){
let result = new Array(n+1);
result[1] = 1; //到第一阶有1种
result[2] = 2; //到第二阶有2种
for(let i = 3; i<n+1; i++){
result[i] = result[i-1] + result[i-2];
}
return result[n];
}
console.log(climbStairs3(8));
js 对两个有序数组进行合并排序
function mergeArray(arr1,arr2){
return arr1.concat(arr2).sort(function(a,b){return a-b })
}
function sort1(a,b){
var i=0,j=0,k=0;
var result=[]
while(i<a.length&&j<b.length){
if(a[i]<b[j]){
result[k++]=a[i++];
}
else{
result[k++]=b[j++];
}
}
while(i<a.length){
result[k++]=a[i++];
}
while(j<b.length){
result[k++]=b[j++]
}
return result
};
JS实现找出字符串中出现最多的字符和次数
function getMost(str){
var obj = {};
for(var i =0; i< str.length; i ++){
var char = str.charAt(i);
if(obj[char]){
obj[char] ++;
}else {
obj[char] = 1;
}
}
var max = 0;
var maxChar = null;
for(var p in obj) {
if (max <obj[p]){
max = obj[p];
maxChar = p;
}
}
return `出现最多的字符:${maxChar},共出现${max}`
}
reduce实现map
Array.prototype.map = function(func,thisArg){
return this.reduce((accumulator,currentValue,currentIndex,array) => {
accumulator.push(func.bind(thisArg)(currentValue,currentIndex,array))
return accumulator
},[])
}
REM 布局
(function(){
var dpr = window.devicePixelRatio || 1;
var html = document.querySelector('html');
changeRem();
window.addEventListener('resize', changeRem);
function changeRem() {
html.style.fontSize =document.documentElement.clientWidth / 750 + 'px'
}
})()
排列组合数组
function recombination(arr) {
results = []
result = []
function doExchange(arr, index) {
for (var i = 0; i < arr[index].length; i++) {
result[index] = arr[index][i]
if (index != arr.length - 1) {
doExchange(arr, index + 1)
} else {
results.push(result.join(','))
}
}
}
doExchange(arr, 0)
return results
}
链表
实现单向链表
function LinkedList() {
// 封装一个Node类, 用于保存每个节点信息
function Node(element) {
this.element = element
this.next = null
}
// 链表中的属性
this.length = 0
this.head = null
// 链表尾部追加元素方法
LinkedList.prototype.append = function (element) {
// 1.根据新元素创建节点
var newNode = new Node(element)
// 2.判断原来链表是否为空
if (this.head === null) { // 链表尾空
this.head = newNode
} else { // 链表不为空
// 2.1.定义变量, 保存当前找到的节点
var current = this.head
while (current.next) {
current = current.next
}
// 2.2.找到最后一项, 将其next赋值为node
current.next = newNode
}
// 3.链表长度增加1
this.length++
}
}
var list = new LinkedList() list.append(1) list.append(2) list.append(3) list.append(4) console.log(list)
单向链表反转
function myReverse (linkedList) {
// 1 拿到传参链表的head
var head = linkedList.head
// 2 边界判断 如果头结点是空 或者只有一个结点 那还反转个啥
if(head === null || head.next === null) return linkedList
// 3 用三个指针
var current = head
var pre = null
var next = null
while(current != null) {
next = current.next
current.next = pre
pre = current
current = next
}
linkedList.head = pre
}
洗牌算法,随机排序
function randomsort2 (arr) {
const newarr = []
while (arr.length) {
const ran = parseInt(Math.random() * arr.length)
newarr.push(arr[ran])
arr.splice(ran, 1)
}
return newarr
}
function randomsort3 (arr) {
return arr.sort(() => Math.random() - 0.5)
}
正则方面
trim
// s: 空格 g:全局 m:多行
String.prototype.trim=function(){ // 去所有空格
return x.replace(/^\\s+|\\s+$/gm,'');
}
String.prototype.ltrim=function(){ // 去左空格
return this.replace(/(^\\s\*)/g,"");
}
String.prototype.rtrim=function(){ // 去右空格
return this.replace(/(\\s\*$)/g,"");
}
解决地址栏参数
function GetQueryString(name){
var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if(r!=null)return unescape(r[2]); return null;
}
正则 截取从开始到结束
str ='health/32_2.3.6 NetType/'
str.match('health/[0-9]*_(.*?)NetType')
var pattern = new RegExp(/^a.*?b$/);
str = '123a666b'
str.match('^a.*?b$')
js 实现对象pick
var obj = { a: { b: { c: 1 } } };
function find(obj, keys){
var Nobj = obj
try{
return eval(`Nobj.${keys}`)
}catch(err){
return err
}
}
find(obj, 'a.b.c')
手动实现一个es6模板字符串
let str2 = '${obj.name}的年龄是${obj.age}';
function replacefunc(desc) {
return desc.replace(/\$\{([^}]+)\}/g, function (match, key) {
console.log(match); // ${obj.name} || ${obj.age}
console.log(key); // obj.name || obj.age
console.log(eval(key)); // lily || 90
return eval(key);
})
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。