2

js中sort函数的底层实现机制?

js中sort内置多种排序算法,是根据要排序数的乱序程度来决定使用哪一种排序方法。V8 引擎 sort 函数只给出了两种排序 InsertionSort 和 QuickSort,长度小于20的使用InsertionSort(插入排序),大于20的数组则使用 QuickSort(快速排序)
image.png

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;
}

正则 截取从开始到结束

image.png

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);
  })
}

李帅醒
113 声望5 粉丝

内心还是残留一丝文艺情怀的小码农 新浪微博@李胖醒plus


« 上一篇
test
下一篇 »
js基础