大厂面试必要亲自手撸的代码

342 阅读13分钟

排序

快速排序

function qSort(arr){
        if (arr.length == 0) {
            return [];
        }
        var left = [];
        var right = [];
        var pivot = arr[0];
        for (var i = 1; i < arr.length; i++) {
            if (arr[i] < pivot) {
                left.push(arr[i]);
                } else {
                right.push(arr[i]);
                }
        }
        return qSort(left).concat(pivot, qSort(right));
    }
    // var a = [];
    // for (var i = 0; i < 10; ++i) {
    //     a[i] = Math.floor((Math.random()*100)+1); 
    // }
    // console.log(a);
    // console.log();
    // console.log(qSort(a));

    var a = [1,3,2];
    var b = [4,5,6]
    var c = 7;
    console.log([...a,c,...b]);  // [1, 3, 2, 7, 4, 5, 6]

插入排序

/*
     最佳情况:T(n) = O(n2)
     最差情况:T(n) = O(n2)
     平均情况:T(n) = O(n2)
     算法原理:
外循环将数组元素挨个移动,而内循环则对外循环中选中的元素及它后面的那个元素进行比较。
如果外循环中选中的元素比内循环中选中的元素小,那么数 组元素会向右移动,为内循环中的这个元素腾出位置,就像之前介绍的姓氏卡片一样。
    
    选择排序和插入排序要比冒泡 排序快,插入排序是这三种算法中最快的。
*/

function insertSort(array) {
    var i, j, temp, n = array.length;
    for (i = 1; i < n; i++) {
        j = i - 1; temp = array[i];
        while (j < i && array[j] > temp) {
            array[j + 1] = array[j];
            j--;
        }
        array[j + 1] = temp;

    }
    return array;
}
var arrayA = [1, 3, 2, 6, 4, 5];
console.log(insertSort(arrayA));

function insertSort(arr) {
    var temp;  //temp变量用于临时存储待插入元素
    for (var i = 1; i < arr.length; i++) {
        temp = arr[i];
        //从前往后查找插入位置
        for (var j = i; j > 0 && arr[j - 1] > temp; j--) {
            arr[j] = arr[j - 1]; //将大于temp的arr[j]元素后移
        }
        arr[j] = temp;//我觉得是因为j--了一次,所以是arr[j]=temp,而不是arr[j-1]=temp
    }
}
//测试
var testArr = [35, 22, 1, 56, 88, 25];
insertSort(testArr);
console.log(testArr);

/**
 * 数据结构书中
 * 
 *  入排序每次排一个数组项,以此方式构建最后的排序数组。假定第一项已经排序了,接着, 它和第二项进行比较,第二项是应该待在原位还是插到第一项之前呢?这样,头两项就已正确排序,接着和第三项比较(它是该插入到第一、第二还是第三的位置呢?),以此类推。
 * 
 *  例,算法的第一行用来 明代码中使用的变量(行{1})。接着, 代数组来给第i项找到 正确的  (行{2})。注意,算法是从第二个  (索引1)而不是0  开始的(我们 为第 一项已排序了)。然后,用i的值来初始化一个 助变量(行{3})并将其值 存储于一 时变量 中(行{4}),便于之后将其 入到正确的  上。下一步是要找到正确的  来 入项目。  要变量j比0大(因为数组的第一个索引是0——没有 值的索引)并且数组中前面的值比 比较 的值大(行{5}),我们就把这个值 到当前  上(行{6})并减小j。最终,该项目能 入到 正确的  上。
 */

// this.insertionSort = function () {
//     var length = array.length,
//         j, temp;
//     for (var i = 1; i < length; i++) {

//         j = i;
//         temp = array[i];
//         while (j > 0 && array[j - 1] > temp) {
//             array[j] = array[j - 1];
//             j--;
//         }
//         array[j] = temp;
//     }
// }

冒泡排序


/*
	依次比较相邻的两个值,如果后面的比前面的小,则将小的元素排到前面。依照这个规则进行多次并且递减的迭代,直到顺序正确。

	平均时间复杂度O(n*n)
	最好情况O(n)
	最差情况O(n*n)
	空间复杂度O(1)
	稳定性:稳定
*/
/**
 * javascript数据结构中的算法
 * 
 */

// var swap = function(index1, index2){
//         var aux = array[index1];
//         array[index1] = array[index2];
//         array[index2] = aux;
// };

// var bubbleSort = function () {
//     var length = array.length;           
//     for (var i = 0; i < length; i++) {        
//         for (var j = 0; j < length - 1; j++) { 
//             if (array[j] > array[j + 1]) {  
//                 swap(j, j + 1);            
//             }
//         }
//     }
// }


// function bubbleSort(arr) {
//         var len = arr.length;
//         var temp;

//         for ( var i = len; i > 1; --i) {
//            for ( var j = 0; j < i ; ++j ) {
//               if (arr[j] > arr[j + 1]) {
//                  swap(arr, j, j + 1);
//               }
//           }
//        }
// }
// function swap(arr, index1, index2) {
//         var temp = arr[index1];
//         arr[index1] = arr[index2];
//         arr[index2] = temp;
// }

/**
 * 改进后的冒泡排序:如果从内循环减去外循环中已跑过的 数,就可以避免内循环中所有不必要的比较
 */
// 控制内外循环,内循环arr.length-1-i,把剩下的跟那个元素比较
// 从小到大输出

function bubbleSort(arr) {
    for (var i = 0; i < arr.length - 1; i++) {
        for (var j = 0; j < arr.length - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                var temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    return arr;
}

var arr = [8, 94, 15, 88, 55, 76, 21, 39];
bubbleSort(arr);
console.log(arr);

选择排序

/*
     最佳情况:T(n) = O(n2)
     最差情况:T(n) = O(n2)
     平均情况:T(n) = O(n2)
     算法原理:
     - 选择排序从数组的开头开始,将第一个元素和其他元 素进行比较。
     - 检查完所有元素后,最小的元素会被放到数组的第一个位置,然后算法会从 第二个位置继续。
     - 这个过程一直进行,当进行到数组的倒数第二个位置时,所有的数据便 完成了排序。


*/


function selectSort(arr) {
	var len = arr.length;
	var index, temp;
	for (var i = 0; i < len - 1; i++) {   //外循环从数组的第一个元素移动到倒数第二个元素
		index = i;   //初始化最小值索引
		for (var j = i + 1; j < len; j++) {  //内循环从第 二个数组元素移动到最后一个元素
			if (arr[j] < arr[index]) {//寻找最小的数    将第一个元素和其他元素比较
				index = j;//保存最小数的索引 ,始终保持最小值的索引位置变化(也就是后面比它小的值的位置)
			}
		}
		temp = arr[i];   //arr[i]为被比较的值,在后面所有中找出比它小的值进行不断交换
		arr[i] = arr[index];
		arr[index] = temp;    //将得到的索引值(内部循环索引值,此j值(index值)不会跟i值相等)的值和被比较的值进行交换
	} //后面多次循环的逻辑一样,即初始化最小值索引
	return arr;
}

var arr = [8, 94, 15, 88, 55, 76, 21, 39];
console.log(selectSort(arr));

/**
 * 数据结构书中
 * 选择排序同样也是一个复杂度为O(n2)的算法。和冒泡排序一样,它包含有嵌套的两个循环, 这导致了二次方的复杂度。然而,接下来要学的插入排序比选择排序性能要好。
 */
// this.selectionSort = function () {
// 	var length = array.length,
// 		indexMin;
// 	for (var i = 0; i < length - 1; i++) {
// 		indexMin = i;
// 		for (var j = i; j < length; j++) {
// 		}
// 	}
// 	if (array[indexMin] > array[j]) {
// 		indexMin = j;
// 		if (i !== indexMin) {
// 			swap(i, indexMin);
// 		}
// 	}
// };

希尔排序

/*
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;
随着增量逐渐减少,每组包含的关键词越来越多,
当增量减至1时,整个文件恰被分成一组,算法便终止。

*/
function shellSort(arr) {
	let l = arr.length;
    let gap = l >> 1;
    
    while(gap>0) {
        for(let i = gap;i<l;i++) {
            let tem = arr[i];
            let j = i - gap; 
            for(;j>=0&&tem<arr[j];j-=gap){
                arr[j+gap] = arr[j];
            }
            arr[j+gap] = tem;
        }
        gap = gap >> 1;
    }
    
    return arr;
}

JS函数柯里化

把接收多个参数的函数变成接收一个参数的函数,并且返回接收余下参数的函数且返回结果的新函数技术

特点:提前返回、参数复用、延迟执行

function curry(fn, arg){
    let length = fn.length;
    let args = arg || [];
    return function (){
        let newArgs = args.concat(Array.prototype.slice.call(arguments));
        if (newArgs.length < length) {
            return curry.call(this,fn,newArgs);
        }else{
            return fn.apply(this,newArgs);
        }

    }
    
}
function multiFn(a, b, c) {
    return a * b * c;
}

var multi = curry(multiFn);

multi(2)(3)(4);
multi(2,3,4);
multi(2)(3,4);
multi(2,3)(4);

<!--这个更好理解-->
function currying(fn){
    var allArgs = [];

    return function next(){
        var args = [].slice.call(arguments);

        if(args.length > 0){
          	
            allArgs = allArgs.concat(args);
            return next;
        }else{
          console.log(allArgs)
            return fn.apply(null, allArgs);
        }
      	
    } 
}
var add = currying(function(){
    var sum = 0;
    for(var i = 0; i < arguments.length; i++){
        sum += arguments[i];
    }
    return sum;
});

console.log(add(3)(4)())   3+4=7


const curry = (fn, arr = []) => (...args) => (
  arg => arg.length === fn.length
    ? fn(...arg)
    : curry(fn, arg)
)([...arr, ...args])

let curryTest=curry((a,b,c,d)=>a+b+c+d)
curryTest(1,2,3)(4) //返回10
curryTest(1,2)(4)(3) //返回10
curryTest(1,2)(3,4) //返回10



const curry = (fn, arr = []) => (...args) => {return (
  arg =>{return arg.length === fn.length
    ? fn(...arg)
    : curry(fn, arg)}
)([...arr, ...args])}

compose函数,函数式编程

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

function compose(...funcs){
  if (funcs.length === 0 ) arg => arg
  if (funcs.length === 1 ) funcs[0]
  return funcs.reduce((a,b) => (...args) => a(b(...args)))
}

const add = num => num + 10
const multiple = num =>num * 3 
let result = compose(multiple,add)
result(5)


function compose(...funcs){
console.log(funcs)
  if(funcs.length===0) arg => 1
  if(funcs.length===1) { conssole.log(funcs[0]()) return funcs[0]}
  funcs.reduce((a,b)=>(...args)=>a(b(args)))


  function compose() {
    var fns = [].slice.call(arguments)
    return function (initialArg) {
        var res = initialArg
        for (var i = fns.length - 1; i > -1; i--) {
            res = fns[i](res)
        }
        return res
    }
}

const add = num => num + 10
const multiple = num =>num * 3 
let result = compose(multiple,add)
result(5)


function compose() {
    var fns = [].slice.call(arguments)
    return function (initialArg) {
        var res = initialArg
        for (var i = fns.length - 1; i > -1; i--) {
            res = fns[i](res)
        }
        return res
    }
}

function pipe() {
    var fns = [].slice.call(arguments)
    return function (initialAgr) {
        var res = initialAgr
        for (var i = 0; i < fns.length; i++) {
            res = fns[i](res)
        }
        return res
    }
}

var greet = function (name) { return 'hi:' + name }
var exclaim = function (statement) { return statement.toUpperCase() + '!' }
var transform = function (str) { return str.replace(/[dD]/, 'DDDDD') }
var welcome1 = compose(greet, exclaim, transform)
var welcome2 = pipe(greet, exclaim, transform)
console.log(welcome1('dot'))//hi:DDDDDOT!
console.log(welcome2('dolb'))//HI:DDDDDOLB!

compose内的函数执行顺序为从右向左,即最右边的函数(最后一个参数)最先执行,执行完的结果作为参数传递给前一个函数(包裹它的函数),一直到整个函数执行完毕,return一个函数,所以compose内部实现的原理类似多米诺骨牌,层层递进的。

pipe函数与compose函数十分相近,也是一个函数执行完毕后将结果作为参数传递给另一个函数,但它们的区别仅在于pipe函数的接收的函数参数,是从左向右执行的,即第一个参数(函数)执行完毕,将结果吐出来作为参数传递给第二个函数,也就是pipe的第二个参数,直到pipe所有参数作为函数都执行完毕,return出一个函数,才算执行完成。

compose和pipe的优点在于,哪怕再要增加或者删除一个参数(执行函数),只需增加或删除相应的参数和定义的函数即可,维护和扩展都十分方便。


promise

function myPromise(construct){
    let self = this;
    self.status = 'pending';
    self.resolveVal = undefinded;
    self.rejectVal = undefinded;
    function resoved(resolveVal){
        if(self.status === 'pending'){
             self.resolveVal = resolveVal;
             this.status = 'resolved'
        }
        
    }
    function rejected(rejectVal){
        if(self.status === 'pending'){
             self.rejectVal = rejectVal;
             this.status = 'rejected'
        }
    }
    //捕获构造异常
    try{
       constructor(resolve,reject);  //这句绝对不能少
    }catch(e){
       reject(e);
    }
}

myPromise.prototype.then=function(onFullfilled,onRejected){
   let self=this;
   switch(self.status){
      case "resolved":
        onFullfilled(self.value);
        break;
      case "rejected":
        onRejected(self.reason);
        break;
      default:       
   }
}

var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})

instanceOf

instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。

  1. instanceof的普通的用法,obj instanceof Object 检测Object.prototype是否存在于参数obj的原型链上。

  2. 继承中判断实例是否属于它的父类

  3. 复杂用法

function instanceof(left,right){
    let proto = left.__proto__;
    let prototype = right.prototype;
    while(true){
        if(proto === null) return false
        if(proto === prototype) return true
        proto = proto.__proto__;
    }
    
}

let result = [
            {id:-1,name:'中国银行'},
            {id:-3,name:'北京银行'},
            {id:2,name:'河北银行'},
            {id:10,name:'保定银行'},
            {id:7,name:'涞水银行'}
          ]
          function sortId(a,b){  
            return a.id-b.id  
          }  由小到大
          function sortId(a,b){  
            return b.id-a.id  
          }  由大到小
          result.sort(sortId);
          
          console.log(Math.max.apply(Math,diffVal));  //diffVal是数组

深拷贝

不仅把第一级克隆一份给新的数组/对象,如果原始数组中存在多级,那么是把每一级都克隆一份赋值给新数组/对象的每一个级别。

方法一:JSON.parse和JSON.stringify

let arr2 = JSON.parse(JSON.stringify(arr1))

  • 原理:先转化为字符串,再把字符串转换为新的对象,这样浏览器会重新开辟内存来存储信息。

  • 应用场景:对 数字/字符串/布尔/null/普通对象/数组对象 等没有影响,可以使用

缺点:JSON.stringify(arr1):并不是对所有的值都能有效处理

【1】正则会变成空对象
【2】JSON.stringify(函数、undefined、Symbol) 变成undefined
【3】JSON.parse(undefined)报错
【4】日期格式数据变为字符串后,基于parse 回不到对象格式了

JSON.stringify(new Number(4))
"4"
JSON.stringify(new RegExp(/[a]/))
"{}"
JSON.stringify(new Date())
""2020-07-16T02:51:37.020Z""
JSON.stringify(function a(){})
undefined
JSON.stringify({a:1})
"{"a":1}"
JSON.stringify([1,2,3])
"[1,2,3]"
JSON.stringify(undefined)
undefined
JSON.stringify(Symbol)
undefined
JSON.stringify(null)
"null"

JSON.parse(undefined)
//  Uncaught SyntaxError: Unexpected token u in JSON at position 0
    at JSON.parse (<anonymous>)
    at <anonymous>:1:6
JSON.parse(null) //null

var a=[1,[2,[44]],[]]

var obj = {
 userName1:1,
   userName2:{
  userAge1:1 
    },
   userAge13:{
    userAge1:{
     a: 3
  }
 }
}
都能深拷贝

方法二:自己封装

function deepClone(obj) {
    // 过滤一些特殊情况
    if(obj === null) return null;
    if(typeof obj !== "object") return obj;
    if(obj instanceof RegExp) { // 正则
         return new RegExp(obj);
    }
    if(obj instanceof Date) { // 日期
         return new Date(obj);
    }
    // let newObj = {}
    // let newObj = new Object()
    let newObj = new obj.constructor; // 不直接创建空对象的目的:克隆的结果和之前保持所属类  =》 即能克隆普通对象,又能克隆某个实例对象
    for(let key in obj) {
        if(obj.hasOwnProperty(key)) {
             newObj[key] = deepClone(obj[key]);
        }
    }
    // let newObj = obj.constructor === Array ? [] : {};
    //for(let key in obj) {
    //    newObj[key] = Object.prototype.toString.apply(obj[key])  === '[object Object]' ? deepCopy(obj[key]) : //obj[key];
    //}
    return newObj;
}

bind

blog.csdn.net/q3254421/ar…

输入:接受一个或者多个参数,第一个是要绑定的上下文,额外参数当作绑定函数的前置参数。 输出:返回原函数的拷贝,即返回一个函数,这个函数呢具备原函数的功能

改变this指向,并且返回一个函数,需要手动调用 func.apply(this,arg)

Function.prototype.myBind = function(obj){
    let _self = this //this表示的就是被调用的函数
    if(typeof this!=='function'){
        return
    }
    let arg1 = [].slice.call(argument,1);//第二个参数截取,获取被调用函数的参数
    let fBound = function(){
        let arg2 = [].slice.call(argument);//因为bind返回一个函数,所以后面可能还会带有参数
         // 如果当前函数的this指向的是构造函数中的this 则判定为new 操作,也就是this是实例对象
        let realObj = this instansof _self ? this : obj  //绑定的调用函数也能被new操作法创建对象,此时this会被忽略
        return _self.apply(realObj,arg1.concact(arg2))
    }
    // 定义一空函数,作为中间桥梁
    let emptyFun = function(){} //创建空函数作为中间人,承接原函数的原型丢给绑定函数
    //维护原型关系
    if(this.prototype){
        emptyFun.prototype = this.prototype; //this是原函数,也就是foo
    }
    fBound.prototype = new emptyFun() //fBound继承emptyFun的原型链
    return fBound
}


function foo(name) {
    this.name = name;
}
var obj = {};  //空对象,指向window
var bar = foo.myBind(obj);
bar('Jack');  //Jack2对应arg2 , 指向window或者undefinded  相当于执行window.foo('Jack'),也就是this.name 为Jack
console.log(obj.name);  // Jack    obj拥有了name属性

var alice = new bar('Alice');
console.log(obj.name);  // Jack     //realObj是obj(上下文)
console.log(alice.name);    // Alice    //realObj是实例对象
bar的this指向 :

1 变量 bar 是绑定之后的函数,也就是 fBound , _self 是 原函数 foo 的引用
2 如果直接 bar(‘jack’) 它指向windowundefined3new bar(‘alice’) (相当于 new foo(‘alice’) )过程中, fBound 的this指向了new表达式返回的对象alice
4 如果是 new 调用绑定函数,此时绑定函数中的this是由new调用绑定函数返回的实例对象,这个对象的构造函数是 fnBound 
5 当我们忽略掉原型连接那行代码时,其原型对象并不等于原函数 self 的原型,所以 this instanceof self ? this : oThis 得到的值还是指定的传入的对象,而不是 new 返回的对象

new

使用new创建的实例:

  • 能访问到构造函数里的属性
  • 能访问原型中的属性
function create(){
    // 1. 获取构造函数,并且删除 arguments 中的第一项
    let ConF = [].shift.call(arguments);
    // 2. 创建一个空的对象并链接到构造函数的原型,使它能访问原型中的属性
    let obj = Object.create(ConF.prototype);
    // 3. 使用apply改变构造函数中this的指向实现继承,使obj能访问到构造函数中的属性
    let ref = ConF.apply(obj,arguments);
    // 4. 优先返回构造函数返回的对象
    return ref instanceof Object ? ref : obj
}

当面试的时候可以说,对象可能是实例对象,可能是上下文,可能是某一个对象

call

Function.prototype.myCall = function(ctx){
    context = (context !== null && context !== undefined) ? Object(context) : window;
    var fn = Symbol();
    context[fn] = this;
    
    let args = [...arguments].slice(1);
    let result = context[fn](...args);
    
    delete context[fn];
    return result;
}

apply

Function.prototype.apply3 = function(context, arr) {
    // 1. 若是传入的context是null或者undefined时指向window;
  // 2. 若是传入的是原始数据类型, 原生的call会调用 Object() 转换
  context = context ? Object(context) : window;
  <!--创建一个独一无二的fn函数的命名-->
  let fn = Symbol();
  <!--这里的this就是指调用apply的那个函数-->
  context[fn] = this;

  let result = arr ? context[fn](...arr) : context[fn]();
  
  <!--给context额外附件了一个属性fn, 所以用完之后需要删除-->
  delete context[fn];
  
  <!--函数fn可能会有返回值, 需要将其返回-->
  return result;
};

jsonp

zhangguixu.github.io/2016/12/02/…

原理:

利用script的src没有跨域的限制,通过指向一个需要访问的url地址,由服务端返回一个预先定义好的js函数的调用,服务端把要返回的数据带到函数参数里面,需要前后端一起配合完成。

实现方式:

  • 服务器返回直接调用函数callback(res)
  • 创建script标签,url上面带有请求的callbak名
  • 前端写出需要调用的函数

缺点:

  • 只能get请求
  • 容易发生XSS漏洞

优点:

  • 兼容性好,好多旧浏览器版本都支持
node实现服务器代码
let http = require('http');
let urlLib = require('url');
let port = 8080;
let data = {'data':'world'};
http.createServe(function(req,res){
    var  params = urlLib.parse(req.url,true); //获取请求的url
    if(params.query.callback){
        var str = params.query.callback + '(' + JSON.stringify(data) + ')';
        res.end(str);
    }else{
        res.end();
    }
}).listen(port,function(){
    console.log('jsonp server is start');
});


前端代码
function jsonpFun(res){
    alert('hello ' + res.data);
}

function jsonp(res){
    let script = document.createElement('script');
    let url = res.url + '?callback=' + res.callback;
    let sript.url = url
    document.getElementsByTag('head')[0].appendChild(script)
}
jsonp({
    url:  "http://localhost:8080/a.com,
    callback: jsonpFun
})

cors

let express = require('express');
let app = express();
app.use(function(req,res,next){
    res.header('Access-Controll-Allow-Origin','')
    res.header('Access-Controll-Allow-Headers','Content-type')
    res.header('Access-Controll-Allow-Method','PUT,POST,GET,OPTION,DELETE')
})

去重

function reducer(a){
  let hash = {};
  let b = a.reduce(function(arr, item){
    //item是对象或者数组
    hash[item] ? '' : hash[item]=true && arr.push(item)
	return arr
  },[])
  console.log( b) //[Object { a: 1 }, 2, Array [1, 2]]
}

reducer([{a:1},{a:1},2,2,[1,2],[1,2]])


防抖和节流

节流 比如公交车站等车,每经过30分钟就会发车,不管后面还有没有人都会发车。这就是节流的过程。

防抖 以最后一个乘客为准,再等30分钟,如果30分钟内都没有人上车,就会发车。如果在期间有人上车,那么重新等30分钟。这就是防抖的过程。

应用场景

在进行窗口的resize、scroll,输入框内容校验等操作时。

函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。如下图,持续触发scroll事件时,并不执行handle函数,当1000毫秒内没有触发scroll事件时,才会延时触发scroll事件。

函数节流(throttle):当持续触发事件时,保证一定时间段内只调用一次事件处理函数。节流通俗解释就比如我们水龙头放水,阀门一打开,水哗哗的往下流,秉着勤俭节约的优良传统美德,我们要把水龙头关小点,最好是如我们心意按照一定规律在某个时间间隔内一滴一滴的往下滴。如下图,持续触发scroll事件时,并不立即执行handle函数,每隔1000毫秒才会执行一次handle函数。

函数节流主要有两种实现方法:时间戳和定时器。

mp.weixin.qq.com/s/Vkshf-nED…

防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零 clearTimeout

防抖场景:

1.登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
2.调整浏览器窗口大小时,resize次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
3.文本编辑器实时保存,当无任何更改操作一秒后进行保存

节流:控制流量,单位时间内事件只能触发一次,如果服务器端的限流即 Rate Limit。代码实现重在开锁关锁 timer=timeout; timer=null

节流场景: 1.scroll 事件,每隔一秒计算一次位置信息等
2.浏览器播放事件,每个一秒计算一次进度信息等
3.input 框实时搜索并发送请求展示下拉列表,没隔一秒发送一次请求 (也可做防抖)

function debounce(fn,delay){
    let timer = null;
    return function(){
        let self = this;
        let args = argument;
        clearTimeout(timer);
        timer = setTimeout({
            fn.apply(self,args)
        },delay)
    }
}
定时器

 var throttle = function(func, delay) {
            var timer = null;
            return function() {
                var context = this;
                var args = arguments;
                if (!timer) {
                    timer = setTimeout(function() {
                        func.apply(context, args);
                        timer = null;
                    }, delay);
                }
            }
        }
        function handle() {
            console.log(Math.random());
        }
        window.addEventListener('scroll', throttle(handle, 1000));
时间戳
  var throttle = function(func, delay) {
            var prev = Date.now();
            return function() {
                var context = this;
                var args = arguments;
                var now = Date.now();
                if (now - prev >= delay) {
                    func.apply(context, args);
                    prev = Date.now();
                }
            }
        }
        function handle() {
            console.log(Math.random());
        }
        window.addEventListener('scroll', throttle(handle, 1000));

深度优先和广度优先

深度优先

递归
function deepFirstTraverse(node,nodeList){
    if(node){
      nodeList.push(node)  
      let children = nodeList.children;
      for(let i=0,i<children.length;i++){
        deepFirstTraverse(children[i],nodeList)  
      }
    }
    return nodeList
}

非递归

function deepFirstSearch(node) {
    var nodes = [];
    if (node != null) {
        var stack = [];
        stack.push(node);
        while (stack.length != 0) {
        var item = stack.pop();
        nodes.push(item);
        var children = item.children;
        for (var i = children.length - 1; i >= 0; i--)
            stack.push(children[i]);
        }
    }
    return nodes;
}

广度优先

function breadthFirstTraverse(node){
    var nodes = [];  
    if (node != null) {  
        var queue = [];  
        queue.unshift(node);  
        while (queue.length != 0) {  
            var item = queue.shift();  
            nodes.push(item);  
            var children = item.children;  
            for (var i = 0; i < children.length; i++)  
                queue.push(children[i]);  
        }  
    }  
    return nodes;  
}

递归版本
function breadthFirstTraverse(node) {
    var nodes = [];
    var i = 0;
    if (!(node == null)) {
        nodes.push(node);
        breadthFirstSearch(node.nextElementSibling);
        node = nodes[i++];
        breadthFirstSearch(node.firstElementChild);
    }
    return nodes;
}

随机获取颜色

通过hsb生成随机颜色
var getRandomColor = function(){
    return "hsb(" + Math.random()  + ", 1, 1)";
 }


function fn1(){
    return '#' + Math.floor( Math.random() * 0xffffff ).toString(16);
}


function fn2(){
    return '#' + ( Math.random() * 0xffffff<<0 ).toString(16);
}


function fn3(){
    var r = Math.floor( Math.random() * 256 );
    var g = Math.floor( Math.random() * 256 );    
    var b = Math.floor( Math.random() * 256 );
    return "rgb("+r+','+g+','+b+")";
}


function fn4(){
    var color = "#";
    for( var i = 0; i < 6; i++ ){
        color += ( Math.random()*16 | 0 ).toString(16);
    }
    return color;
}


function fn5(){
    var colorValue = "0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f";
    var colorArray = colorValue.split(",");
    var color = "#";
    for( var i = 0; i < 6; i++ ){
        color += colorArray[ Math.floor( Math.random() * 16 ) ];
    }
    return color;
}


组中相同元素组合成一个新的数组问题

``js` var arr=['1','1','1','2',,'3','4','5','5','6','6','7', '8','9','9','10']; var newArr1 = [], tempArr = []; for(var i=0,j=arr.length;i<j;i++){ if(arr[i] == arr[i+1]){ tempArr.push(arr[i]); } else { tempArr.push(arr[i]); newArr1.push(tempArr.slice(0)); tempArr.length = 0; } } console.log(newArr1);

var _arr = [1,3,2,4,3,2,1,3,4,2];

var _res = []; //
_arr.sort();
for (var i = 0; i < _arr.length;) {
var count = 0;
for (var j = i; j < _arr.length; j++) {
if (_arr[i] == _arr[j]) {
count++;
}
}
_res.push([_arr[i], count]);
i += count;
}
console.log(_res); //[ [1, 2], [2, 3], [3, 3], Array [4, 2]] //_res 二维数维中保存了 值和值的重复数
var _newArr = [];
for (var i = 0; i < _res.length; i++) {
// console.log(_res[i][0] + "重复次数:" + _res[i][1]);
_newArr.push(_res[i][0] + 'x' + _res[i][1]);
}
console.log(_newArr);


https://juejin.cn/post/6844903809206976520
https://juejin.cn/post/6844903810113126413
https://juejin.cn/post/6844904096382582797#heading-95