JavaScript的骚操作:防抖、节流、柯里化等

1、对象混合

在项目中经常会遇到将后端传回来的多个对象,合并成一个对象的问题,我们通常会用到Object.assign()函数,那我们如何仿写一个这个函数呢?

/**
 * 对象混合,将第二个参数开始的对象,混合到第一个对象中
*/
function minxin(obj){
    //当只有一个对象的时候返回
   if(arguments.length <=1) return obj
    //获取参数对象列表,这里我们假设已经按照要求传过来的是对错
   var args = Array.from(arguments)
   //循环这个列表。
   for(var i=1;i<args.length;i++){
       for(var prop in args[i]){
           //将列表中的每个属性,复制到obj中,也就是我们的第一个参数
           obj[prop] = args[i][prop]
       }
   }
   return obj
}

2、深/浅克隆一个对象

我们经常会有深克隆一个对象的需求,除了loadsh中的克隆方法,你自己会写一个深克隆么?下面呢,是我封装的可以深浅克隆对象的方法

/**
 * 克隆一个对象
 * @param {boolean} deep 是否深度克隆
 */
function clone(obj, deep) {
    if (Array.isArray(obj)) {//如果是数组
        if (deep) { //深克隆
            var newArr = []
            //此时要考虑数组中可能含有对象数据项,所以循环遍历每一个数据项对其克隆
            for (var i = 0; i < obj.length; i++) {
                return clone(obj[i], true)
            }
            return newArr
        } else {//浅克隆,直接返回一个新的数组即可
            return obj.slice()
        }
    } else if (typeof obj === "object") {//是一个对象
        var newObj = {}
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) { //不克隆原型链上的值
                if (deep) {//深克隆也要考虑对象中的属性还是一个对象
                    newObj[key] = clone(obj[key], true)
                } else {
                    newObj[key] = obj[key]
                }
            }
        }
        return newObj
    } else { //一个普通值
        return obj
    }
}

3、函数防抖

函数防抖也是我们在一个网站中经常会用的到的方法。那么什么是函数防抖呢?其实很简单,就是我们在一段时间内频繁触发同一个事件,可能会十分的影响性能,所以,我们想要在一段时间内只触发一次。这就要用到防抖和节流。举个例子,防抖是频繁触发后,1s内没有再触发时才调用事件处理程序。

/**
 * 函数防抖
 */
function debounce(callback, time) {
    var timer;
    //使用闭包,可以避免污染全局变量
    return function () {
        clearTimeout(timer) //清除计时器
        //重新开始计时
        var args = arguments
        timer = setTimeout(() => {
            callback.apply(null, args)
        }, time);
    }
}
//测试案例
var handle = debounce(function (a, b) {
    console.log(a, b)
}, 1000)
window.onresize = function () {
    handle(1, 2)
}

4、函数节流

那么和防抖相对应的就是节流了,节流就是在一段时间内只触发一次!

/**
 * 函数节流
 */
function throttle(callback, time, immediately) {
    if (immediately) {//是否立即执行一次
        var t;
        return function () {
            //之前没有计时 或 距离上次执行的时间已超过规定的值
            if (!t || Date.now() - t >= time) {
                callback.apply(null, arguments)
                //得到的当前时间戳
                t = Date.now()
            }
        }
    } else {//如果不立即执行一次,那么这个时间让他延迟多长时间后再执行
        var timer;
        return function () {
            if (timer) return
            var args = arguments
            timer = setTimeout(() => {
                callback.apply(null, args)
                timer = null
            }, time);
        }
    }
}

5、柯里化

科里化是把一个多参函数,变为单参函数的一个方法,可以说是在JavaScript中最骚的操作之一了,毕竟在函数式编程中,单参函数的调用要比多参函数调用方便的多。

/**
 * 柯里化函数
 * 在函数式编程中,柯里化最重要的作用是把多参函数变为单参函数
 */
function curry(func) {
    //获取参数,把传过来的参数固定
    var args = Array.from(arguments).slice(1)
    var that = this
    return function () {
        var curArgs = Array.from(arguments)
        var totalArgs = args.concat(curArgs)
        if (totalArgs.length >= func.length) {
            return func.apply(null, totalArgs)
        } else {
            totalArgs.unshift(func)
            return curry.apply(that, totalArgs)
        }
    }
}

function f(x, y, z) {
    return (x + y) * z;
}
var g = curry(f, 1)
var x = g(2)
console.log(x(3))

柯里化demo

<style>
.container {
    border: 2px solid;
    padding: 30px;
}
</style>
<div class="container">

</div>

<script>
function createElement(container, name, props, styles, content) {
    var ele = document.createElement(name);
    container.appendChild(ele);

    for (var prop in props) {
        ele[prop] = props[prop];
    }

    for (var prop in styles) {
        ele.style[prop] = styles[prop];
    }
    if (content) {
        ele.innerHTML = content;
    }
}
var div = document.querySelector(".container");
var create = curry(createElement, div, "div", {}, {
    height: "100px",
    background: "#008c8c",
    margin: "30px",
    color:"#fff"
});

create("第1个内容");
create("第2个内容");
create("第3个内容");
create("第4个内容");
create("第5个内容");
create("第6个内容");
create("第7个内容");

</script>

6、单参函数管道

当我们遇到多个函数连续调用,切下一个函数的参数是上一个函数的返回结果,此时就可以用到单参函数管道,他可以简化我们调用的过程,使调用更为简单。

在这里插入图片描述

/**
 * 函数管道
 * 方法一
 */
function pipe() {
    //获取管道函数列表
    var args = Array.from(arguments);
    return function (val) {
        //巧妙使用reduce求和函数,将上一个函数的结算结果,当做下一个函数的参数
        return args.reduce(function (result, func) {
            return func(result);
        }, val);
    }
}
/**
 * 函数管道
 * 方法二
 */
function pipe() {
     //获取管道函数列表
    var args = Array.from(arguments);
    return function (val) {
        //通过一个循环遍历每一个函数,并将上一个函数的计算结果保存下来
        for (var i = 0; i < args.length; i++) {
             var func = args[i];
            //将上一个函数保留下来的结果,给下一个函数当作参数使用
             val = func(val);
         }
        return val;
    }
}   

7、仿写一个 apply 方法

我们都知道函数中的apply、call和bind是可以改变函数中的this指向,那么我们怎么自己来实现一个改变this指向的方法。

 Function.prototype.newApply = function(ctx,arr){
    //ctx:执行期上下文,即我们要绑定的this指向
    var ctx = ctx
    //在ctx中创建一个临时变量fn存放当前的this
    ctx.fn = this
    var result
    if(!arr){
        result = ctx.fn()
    }else{
        var args = []
        for(var i=0;i<arr.length;i++){
            args.push("arr["+i+"]")
        }
        result = eval('ctx.fn('+args.join(',') + ')')
    }
     //删除临时变量fn
    delete ctx.fn
    return result
}
//测试用例
let person = {
    name:"zhang"
}
function obj(name,age){
    console.log(this.name)
    return {
        name:name,
        age:age
    }
}
var newObj = obj.newApply(person,['zhangsan','30'])
console.log(newObj)

8、自己封装一个type方法

我们都知道typeof 可以得到值的类型,但是这个方法并不准确,那么我们自己实现一个type方法

function type(target){
    //定义一个输出字典
    var template = {
        "[object Array]" : "array",
        "[object Object]" : "object",
        "[object Number]" : "number - object",
        "[object String]" : "string - object",
        "[object Boolean]" : "boolean - object"
    }
    if(typeof(target) === null){ //解决null是object的问题
        return null;
    }
    if(typeof(target) == "object"){
       var str = Object.prototype.toString(target)
       return template[str];
    }else{
       return typeof(target);
    }
}

9、数组去重

数组去重的方法千千万,但是这种方法不知道你会不会呢?我们通过一个临时对象的方法来存储数组中的的每一个值

// 数组去重
Array.prototype.unique = function(){
    var temp = {},
        arr = [],
        len = this.length;
    for(var i=0;i<len;i++){  
        if(!temp[this[i]]){  // undefined 取反
            temp[this[i]] = "a";
            arr.push(this[i]);
        }
    }
    return arr;
}

10、圣杯模式(继承)

使用prototype实现继承你再不会,那你还怎么进大厂

/*
*  Target是继承者 Origin是被继承者
*/
function inherit(Target,Origin){
    //通过创建一个新的F构造函数作为中间层 
    // 防止继承者修改prototype时对被继承者产生影响
    function F(){}
    F.prototype = Origin.prototype;
    Target.prototype = new F();
    Target.prototype.constuctor = Target;
    Target.prototype.uber = Origin.protoype;
}
/*
* yahoo 中提供的圣杯模式
*/
var inherit = (function(){
    var F = function(){};  //中间层F 将构造函数F变为私有化变量 
    return function(Target,Origin){ //返回继承函数
        F.prototype = Origin.prototype;
        Target.prototype = new F();
        Target.prototype.constuctor = Target;
        Target.prototype.uber = Origin;
    };
}())

猜你喜欢

转载自blog.csdn.net/Newbie___/article/details/106787008
今日推荐