博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JS 常用函数垫片
阅读量:6276 次
发布时间:2019-06-22

本文共 5681 字,大约阅读时间需要 18 分钟。

常用的一些函数垫片,意在加深js基础概念,同时对js也是个很好的总结。以下案例为个人实践,考虑主流程完整,但有些边界问题未考虑,不推荐在工程项目中使用。正式项目推荐使用。

call/apply

问题

var foo = { value: 1 }function bar() { console.log(this.value) }bar.call(foo) // 期待打印:1bar.apply(foo) // 期待打印:1复制代码

思路

call/apply立即执行函数,同时函数中的this改为指向context。类似等价于以下

var foo = {    value: 1,    fn: function bar() { console.log(this.value) }}复制代码
Function.prototype.call = function(context, ...args) {    context = context || window    context.fn = this // 这里的this代表函数    context.fn(...args) // 给context添加属性fn,所以执行fn方法时,里面的this代表context    delete context.fn}Function.prototype.apply = function(context, ...args) {    context = context || window    context.fn = this    context.fn(args) // apply传递数组    delete context.fn}复制代码

bind

问题

var foo = { value: 1 }function bar() { console.log(this.value) }let barBind = bar.bind(foo)barBind() // 期待打印:1复制代码

思路

通过apply改变this,并且返回一个函数

Function.prototype.bind = function (context, ...args) {    var fn = this    return function() {        return fn.apply(context, args)    }}复制代码

curry

问题

let addFun = function(a, b, c) { return a + b + c }let curryFun = curry(addFun)curryFun(1)(2)(3) === 6 // true复制代码

思路

递归,当执行的参数个数等于原本函数的个数,执行函数

var curry = function(fn) {    var limit = fn.length // fn函数参数个数    return function judgeCurry (...args) {        if (args.length >= limit) {            return fn.apply(null, args)        } else {            return function(...args2) {                return judgeCurry.apply(null, args.concat(args2))            }        }    }}// or es6var curry = function(fn, ...args) {    if (args.length >= fn.length) {        return fn(...args)    }    return function (...args2) {        return curry(fn, ...args, ...args2)    }}复制代码

pipe/compose

pipe

  • pipe(fn1,fn2,fn3,fn4)(args)等价于fn4(fn3(fn2(fn1(args)))
  • 第一个函数的结果,作为第二个函数的参数,以此类推...

compose

  • compose(fn1,fn2,fn3,fn4)(args)等价于fn1(fn2(fn3(fn4(args)))
  • 与pipe相反,先计算倒数第一个结果,作为倒数第二的参数,以此类推...
let loopItem = (prevFn, nextFn) => (...args) => prevFn(nextFn(...args))const compose = (...fns) => fns.reduce(loopItem);const pipe = (...fns) => fns.reduceRight(loopItem)const example = pipe(    (x, y) => x * y,    x => x + 1);console.log(example(3, 4)) // 13复制代码

flatten

深度为1的展平

// before:[1, 2, [3, 4, [5, 6]]]// after flat: [1, 2, 3, 4, [5, 6]]// 思路:使用reduce或mapfunction flatSingle(arr) {    return arr.reduce((pre, val) => pre.concat(val), [])}// orlet flatSingle = arr => [].concat(...arr)复制代码

深度无限的展平

// before: [1,2,3,[1,2,3,4, [2,3,4]]]// after flatDeep: [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]// 思路:深度优先递归,使用reduce连接起来// 深度优先算法 - 递归function flatDeep(arr) {    return arr.reduce((pre, val) => pre.concat(Array.isArray(val) ? flatDeep(val) : val), [])}// 深度优先算法 - 堆栈function flatDeep(arr) {    const stack = [...arr]    const res = []    while (stack.length) {        const val = stack.pop() // 从尾部开始        Array.isArray(val) ? stack.push(val) : res.push(val)    }    return res.reverse()}// 取巧,利用Array.toString()function flatDeep(arr) {    return arr.toString().split(',')}复制代码

指定深度的展平

深度的含义是指每一项展平的次数

// before: [1,2,3,[1, [2]], [1, [2, [3]]]]// after: [ 1, 2, 3, 1, 2, 1, 2, [ 3 ] ]function flatDeep(arr, depth = 1) {    if (depth === 1) return arr.reduce((pre, val) => pre.concat(val), [])    return arr.reduce((pre, val) => pre.concat(Array.isArray(val) ? flatDeep(val, depth - 1) : val), [])}复制代码

去重

数组去除重复

// before: [2, 1, 3, 2]// after: [2, 1, 3]function removeRepeat(arr) {    return arr.filter((item, index) => arr.indexOf(item) === index)}// or es6let removeRepeat = arr =>  Array.from(new Set(arr))let removeRepeat = arr =>  [...new Set(arr)]复制代码

浅拷贝/深拷贝

// 浅拷贝function clone(source) {    var target = {}    for (var i in source) {        source.hasOwnProperty(i) && target[i] = source[i]    }    return target}// or es6const clone = source => Object.assign({}, source)const clone = source => { ...source }复制代码
// 深拷贝// 思路:递归赋值const deepClone = source => {    if (!source || typeof source !== 'object') {        throw new Error('error arguments', 'shallowClone')    }    // 区分array和object对象    let target = source instanceof Array ? [] : {}    for (let key in source) {        if (source.hasOwnProperty(key)) {            target[key] = typeof source[key] === 'object' ? deepClone(source[key]) : source[key]        }    }    return target}// or 取巧方法// 注意这种取巧方法是有限制的// 1. 只能解析Number、String、Array等能够被json表示的数据结构// 2. 不能处理循环引用const deepClone = source => JSON.parse(JSON.stringify(source))复制代码

防抖/节流

  • 防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。适合多次事件一次响应。
  • 节流:规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。适合大量事件按时间做平均分配触发。
// 防抖案例:窗口resize停止才执行最终的函数function debounce(fn, wait, ...args) {    var that = this    fn.tId && clearTimeout(fn.tId)    fn.tId = setTimeout(function() {        fn.apply(that, args)    }, wait)}function handle(e) {    console.log('resize end event')    console.log(e) // Event{}}// 缺点:handle不能写成匿名函数,因为把tId存储在handle函数对象上。所以间接导致传递参数e较为复杂window.onresize = function(e) { debounce(handle, 1000, e) }// 改进版// 思路: 用闭包把tId存储起来function debounce(fn, wait) {    var tId    return function() {        var that = this        var args = arguments        tId && clearTimeout(tId)        tId = setTimeout(function() {            fn.apply(that, args)        }, wait)    }}function handle(e) {    console.log('resize end event')    console.log(e) // Event{}}window.onresize = debounce(handle, 1000)复制代码
// 节流案例: 不停scroll时,滚动条每隔100ms固定频率执行函数function throttle(fn, wait) {    var cur = new Date()    fn.last = fn.last || 0    if (cur - fn.last > wait) {        fn.call()        fn.last = cur    }}function handle() {    console.log('scroll event')}window.onscroll = function() { throttle(handle, 100) }复制代码

参考文章

  • MDN

转载于:https://juejin.im/post/5cab46aa6fb9a06895488ac0

你可能感兴趣的文章
180918-JDK之Deflater压缩与Inflater解压
查看>>
redis系列:通过队列案例学习list命令
查看>>
npm发布包的那些事
查看>>
遮罩层 弹框 页面滚动
查看>>
机票分享第一篇 机票由何而来
查看>>
【spring 注解】第1篇:Java基础注解学习
查看>>
Linux命令之awk
查看>>
使用 Flask-Docs 自动生成 Api 文档
查看>>
Angular系列学习三:父子组件之间的交互(常见的组件通讯场景)
查看>>
垃圾回收算法|GC标记-清除算法
查看>>
移除注释的完善思路:真的可以用正则实现?
查看>>
我所了解的CSS包含块
查看>>
vue.js多页面开发 webpack.config.js 配置方式
查看>>
C语言中 变量的生命周期、作用域、类型转换
查看>>
android高仿抖音、点餐界面、天气项目、自定义view指示、爬取美女图片等源码...
查看>>
js代码常见技巧总结
查看>>
初识css层叠上下文
查看>>
再看正则表达式
查看>>
JavaScript异步精讲,让你更加明白Js的执行流程!
查看>>
mongoose 的那些基础操作
查看>>