js代码块

309 阅读3分钟

节流

高频事件触发,无论多频繁触发在n秒内只会执行一次,所以节流会稀释函数的执行频率。
scroll加载更多可以用到节流

<input type="text" id="search" value="12">
   function jieliu(fn, duration, isFirst) {
        let timer;
        return function (...args) {
            if(isFirst){
              isFirst = false;
              fn.apply(this, args);
              return;
            }
            if(!timer){
              timer = setTimeout(()=>{
                fn.apply(this, args);
                timer = null;
              }, duration)
            }
        }
    }

    document.getElementById('search').addEventListener("input", jieliu((e)=>{
      console.log(e.target.value);
    }, 1000, true))

防抖

你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行,真是任性呐! 常见的例子:有一个搜索输入框,为了提升用户体验,希望在用户输入后可以立即展现搜索结果,而不是每次输入完后还要点击搜索按钮

    <input type="text" id="search">
    
    function fangdou(fn, duration) {
        let timer;
        return function (...args) {
            if(timer){
              clearTimeout(timer);
            }
            timer = setTimeout(()=>{
              fn.apply(this, args);
            }, duration)
        }
    }
    
    document.getElementById('search').addEventListener('input', fangdou((e)=>{
      console.log(e.target.value)
    }, 1000));

call()手写

Function.prototype.myCall = function(thisArg, ...args) {
    const fn = Symbol('fn')        // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
    thisArg = thisArg || window    // 若没有传入this, 默认绑定window对象
    thisArg[fn] = this              // this指向调用call的对象,即我们要改变this指向的函数
    const result = thisArg[fn](...args)  // 执行当前函数
    delete thisArg[fn]              // 删除我们声明的fn属性
    return result                  // 返回函数执行结果
}

//测试
foo.myCall(obj)     // 输出'写代码像'

apply手写

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。
语法:func.apply(thisArg, [argsArray])

apply()call()类似,区别在于call()接收参数列表,而apply()接收一个参数数组,所以我们在call()的实现上简单改一下入参形式即可

Function.prototype.myApply = function(thisArg, args) {
    const fn = Symbol('fn')        // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
    thisArg = thisArg || window    // 若没有传入this, 默认绑定window对象
    thisArg[fn] = this              // this指向调用call的对象,即我们要改变this指向的函数
    const result = thisArg[fn](...args)  // 执行当前函数(此处说明一下:虽然apply()接收的是一个数组,但在调用原函数时,依然要展开参数数组。可以对照原生apply(),原函数接收到展开的参数数组)
    delete thisArg[fn]              // 删除我们声明的fn属性
    return result                  // 返回函数执行结果
}

//测试
foo.myApply(obj, [])     // 输出'写代码像'

bind手写

Function.prototype.bind=function(obj,arg){
  var arg=Array.prototype.slice.call(arguments,1);
  var context=this;
  return function(newArg){
    arg=arg.concat(Array.prototype.slice.call(newArg));
    return context.apply(obj,arg);
  }
}

promise手写实现,面试够用版:

function myPromise(constructor){
    let self=this;
    self.status="pending" //定义状态改变前的初始状态
    self.value=undefined;//定义状态为resolved的时候的状态
    self.reason=undefined;//定义状态为rejected的时候的状态
    function resolve(value){
        //两个==="pending",保证了状态的改变是不可逆的
       if(self.status==="pending"){
          self.value=value;
          self.status="resolved";
       }
    }
    function reject(reason){
        //两个==="pending",保证了状态的改变是不可逆的
       if(self.status==="pending"){
          self.reason=reason;
          self.status="rejected";
       }
    }
    //捕获构造异常
    try{
       constructor(resolve,reject);
    }catch(e){
       reject(e);
    }
}
// 定义链式调用的then方法
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:       
   }
}

简单深拷贝

//简单版深拷贝,只能拷贝基本原始类型和普通对象与数组,无法拷贝循环引用
function simpleDeepClone(a) {
  const b=Array.isArray(a) ? [] : {}
  for (const [key, value] of Object.entries(a)) {
    const type = typeof value
    if (type !== 'object' || value === null) {      
      b[key] = value    
    }else {
      b[key] = simpleDeepClone(value)
    }
  }
  return b
}

扁平化

function flatArr(arr){  
    return arr.reduce((cal, cur) => Array.isArray(cur) ? 
        cal.concat(flatArr(cur)) : cal.concat(cur) 
} 

圣杯布局

圣杯布局和双飞翼布局是前端工程师需要日常掌握的重要布局方式。两者的功能相同,都是为了实现一个两侧宽度固定,中间宽度自适应的三栏布局。

<style>
body{
    min-width: 550px;
}
#container{
    padding-left: 200px;
    padding-right: 150px;
}
#container .column{
    float: left;
}
#center{
    width: 100%;
}
#left{
    width: 200px;
    margin-left: -100%;
    position: relative;
    right: 200px;
}
#right{
    width: 150px;
    margin-right: -150px;
}
</style>
<div id="container">
    <div id="center" class="column">center</div>
    <div id="left" class="column">left</div>
    <div id="right" class="column">right</div>
</div>

Layout.gif