最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • javascript精进之路手写系列第二弹实际应用篇(18个)(附详解)

    正文概述 掘金(前端_小牛_到犀牛)   2021-07-19   303

    1. 实现日期格式化函数

    输入:

    dateFormat(new Date('2020-12-01'), 'yyyy/MM/dd') // 2020/12/01
    dateFormat(new Date('2020-04-01'), 'yyyy/MM/dd') // 2020/04/01
    dateFormat(new Date('2020-04-01'), 'yyyy年MM月dd日') // 2020年04月01日
    
    const dateFormat = (dateInput, format)=>{
        var day = dateInput.getDate() 
        var month = dateInput.getMonth() + 1  
        var year = dateInput.getFullYear()   
        format = format.replace(/yyyy/, year)
        format = format.replace(/MM/,month)
        format = format.replace(/dd/,day)
        return format
    }
    

    2. 交换a,b的值,不能用临时变量

    巧妙的利用两个数的和、差:

    a = a + b
    b = a - b
    a = a - b
    //[a,b]=[b,a];
    

    3. 实现数组的乱序输出

    主要的实现思路就是:

    • 取出数组的第一个元素,随机产生一个索引值,将该第一个元素和这个索引对应的元素进行交换。
    • 第二次取出数据数组第二个元素,随机产生一个除了索引为1的之外的索引值,并将第二个元素与该索引值对应的元素进行交换
    • 按照上面的规律执行,直到遍历完成
    var arr = [1,2,3,4,5,6,7,8,9,10];
    for (var i = 0; i < arr.length; i++) {
      const randomIndex = Math.round(Math.random() * (arr.length - 1 - i)) + i;
      [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
    }
    console.log(arr)
    

    还有一方法就是倒序遍历:

    var arr = [1,2,3,4,5,6,7,8,9,10];
    let length = arr.length,
        randomIndex,
        temp;
      while (length) {
        randomIndex = Math.floor(Math.random() * length--);
        temp = arr[length];
        arr[length] = arr[randomIndex];
        arr[randomIndex] = temp;
      }
    console.log(arr)
    

    4. 实现数组元素求和

    • arr=[1,2,3,4,5,6,7,8,9,10],求和
    let arr=[1,2,3,4,5,6,7,8,9,10]
    let sum = arr.reduce( (total,i) => total += i,0);
    console.log(sum);
    
    • arr=[1,2,3,[[4,5],6],7,8,9],求和
    var = arr=[1,2,3,[[4,5],6],7,8,9]
    let arr= arr.toString().split(',').reduce( (total,i) => total += Number(i),0);
    console.log(arr);
    

    递归实现:

    let arr = [1, 2, 3, 4, 5, 6] 
    
    function add(arr) {
        if (arr.length == 1) return arr[0] 
        return arr[0] + add(arr.slice(1)) 
    }
    console.log(add(arr)) // 21
    

    5. 实现数组的扁平化

    (1)递归实现

    普通的递归思路很容易理解,就是通过循环递归的方式,一项一项地去遍历,如果每一项还是一个数组,那么就继续往下遍历,利用递归程序的方法,来实现数组的每一项的连接:

    let arr = [1, [2, [3, 4, 5]]];
    function flatten(arr) {
      let result = [];
    
      for(let i = 0; i < arr.length; i++) {
        if(Array.isArray(arr[i])) {
          result = result.concat(flatten(arr[i]));
        } else {
          result.push(arr[i]);
        }
      }
      return result;
    }
    flatten(arr);  //  [1, 2, 3, 4,5]
    

    (2)reduce 函数迭代

    从上面普通的递归函数中可以看出,其实就是对数组的每一项进行处理,那么其实也可以用reduce 来实现数组的拼接,从而简化第一种方法的代码,改造后的代码如下所示:

    let arr = [1, [2, [3, 4]]];
    function flatten(arr) {
        return arr.reduce(function(prev, next){
            return prev.concat(Array.isArray(next) ? flatten(next) : next)
        }, [])
    }
    console.log(flatten(arr));//  [1, 2, 3, 4,5]
    

    (3)扩展运算符实现

    这个方法的实现,采用了扩展运算符和 some 的方法,两者共同使用,达到数组扁平化的目的:

    let arr = [1, [2, [3, 4]]];
    function flatten(arr) {
        while (arr.some(item => Array.isArray(item))) {
            arr = [].concat(...arr);
        }
        return arr;
    }
    console.log(flatten(arr)); //  [1, 2, 3, 4,5]
    

    (4)split 和 toString

    可以通过 split 和 toString 两个方法来共同实现数组扁平化,由于数组会默认带一个 toString 的方法,所以可以把数组直接转换成逗号分隔的字符串,然后再用 split 方法把字符串重新转换为数组,如下面的代码所示:

    let arr = [1, [2, [3, 4]]];
    function flatten(arr) {
        return arr.toString().split(',');
    }
    console.log(flatten(arr)); //  [1, 2, 3, 4,5]
    

    通过这两个方法可以将多维数组直接转换成逗号连接的字符串,然后再重新分隔成数组。

    **(5)**ES6 中的 flat

    我们还可以直接调用 ES6 中的 flat 方法来实现数组扁平化。flat 方法的语法:arr.flat([depth])

    其中 depth 是 flat 的参数,depth 是可以传递数组的展开深度(默认不填、数值是 1),即展开一层数组。如果层数不确定,参数可以传进 Infinity,代表不论多少层都要展开:

    let arr = [1, [2, [3, 4]]];
    function flatten(arr) {
      return arr.flat(Infinity);
    }
    console.log(flatten(arr)); //  [1, 2, 3, 4,5]
    

    可以看出,一个嵌套了两层的数组,通过将 flat 方法的参数设置为 Infinity,达到了我们预期的效果。其实同样也可以设置成 2,也能实现这样的效果。在编程过程中,如果数组的嵌套层数不确定,最好直接使用 Infinity,可以达到扁平化。

    (6)正则和 JSON 方法

    在第4种方法中已经使用 toString 方法,其中仍然采用了将 JSON.stringify 的方法先转换为字符串,然后通过正则表达式过滤掉字符串中的数组的方括号,最后再利用 JSON.parse 把它转换成数组:

    let arr = [1, [2, [3, [4, 5]]], 6];
    function flatten(arr) {
      let str = JSON.stringify(arr);
      str = str.replace(/([|])/g, '');
      str = '[' + str + ']';
      return JSON.parse(str); 
    }
    console.log(flatten(arr)); //  [1, 2, 3, 4,5]
    

    6. 实现数组去重

    给定某无序数组,要求去除数组中的重复数字并且返回新的无重复数组。

    ES6方法(使用数据结构集合):

    const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
    
    Array.from(new Set(array)); // [1, 2, 3, 5, 9, 8]
    

    ES5方法:使用map存储不重复的数字

    const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
    
    uniqueArray(array); // [1, 2, 3, 5, 9, 8]
    
    function uniqueArray(array) {
      let map = {};
      let res = [];
      for(var i = 0; i < array.length; i++) {
        if(!map.hasOwnProperty([array[i]])) {
          map[array[i]] = 1;
          res.push(array[i]);
        }
      }
      return res;
    }
    

    7. 实现数组的flat方法

    function _flat(arr, depth) {
      if(!Array.isArray(arr) || depth <= 0) {
        return arr;
      }
      return arr.reduce((prev, cur) => {
        if (Array.isArray(cur)) {
          return prev.concat(_flat(cur, depth - 1))
        } else {
          return prev.concat(cur);
        }
      }, []);
    }
    

    8. 实现数组的push方法

    let arr = [];
    Array.prototype.push = function() {
        for( let i = 0 ; i < arguments.length ; i++){
            this[this.length] = arguments[i] ;
        }
        return this.length;
    }
    

    9. 实现数组的filter方法

    Array.prototype._filter = function(fn) {
        if (typeof fn !== "function") {
            throw Error('参数必须是一个函数');
        }
        const res = [];
        for (let i = 0, len = this.length; i < len; i++) {
            fn(this[i]) && res.push(this[i]);
        }
        return res;
    }
    

    10. 实现数组的map方法

    Array.prototype._map = function(fn) {
       if (typeof fn !== "function") {
            throw Error('参数必须是一个函数');
        }
        const res = [];
        for (let i = 0, len = this.length; i < len; i++) {
            res.push(fn(this[i]));
        }
        return res;
    }
    

    11. 实现字符串的repeat方法

    输入字符串s,以及其重复的次数,输出重复的结果,例如输入abc,2,输出abcabc。

    function repeat(s, n) {
        return (new Array(n + 1)).join(s);
    }
    

    递归:

    function repeat(s, n) {
        return (n > 0) ? s.concat(repeat(s, --n)) : "";
    }
    

    12. 实现字符串翻转

    在字符串的原型链上添加一个方法,实现字符串翻转:

    String.prototype._reverse = function(a){
        return a.split("").reverse().join("");
    }
    var obj = new String();
    var res = obj._reverse ('hello');
    console.log(res);    // olleh
    

    需要注意的是,必须通过实例化对象之后再去调用定义的方法,不然找不到该方法。

    13. 将数字每千分位用逗号隔开

    数字有小数版本:

    let format = n => {
        let num = n.toString() // 转成字符串
        let decimals = ''
            // 判断是否有小数
        num.indexOf('.') > -1 ? decimals = num.split('.')[1] : decimals
        let len = num.length
        if (len <= 3) {
            return num
        } else {
            let temp = ''
            let remainder = len % 3
            decimals ? temp = '.' + decimals : temp
            if (remainder > 0) { // 不是3的整数倍
                return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') + temp
            } else { // 是3的整数倍
                return num.slice(0, len).match(/\d{3}/g).join(',') + temp 
            }
        }
    }
    format(12323.33)  // '12,323.33'
    

    数字无小数版本:

    let format = n => {
        let num = n.toString() 
        let len = num.length
        if (len <= 3) {
            return num
        } else {
            let remainder = len % 3
            if (remainder > 0) { // 不是3的整数倍
                return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') 
            } else { // 是3的整数倍
                return num.slice(0, len).match(/\d{3}/g).join(',') 
            }
        }
    }
    format(1232323)  // '1,232,323'
    

    14. 实现非负大整数相加

    JavaScript对数值有范围的限制,限制如下:

    Number.MAX_VALUE // 1.7976931348623157e+308
    Number.MAX_SAFE_INTEGER // 9007199254740991
    Number.MIN_VALUE // 5e-324
    Number.MIN_SAFE_INTEGER // -9007199254740991
    

    如果想要对一个超大的整数(> Number.MAX_SAFE_INTEGER)进行加法运算,但是又想输出一般形式,那么使用 + 是无法达到的,一旦数字超过 Number.MAX_SAFE_INTEGER 数字会被立即转换为科学计数法,并且数字精度相比以前将会有误差。

    实现一个算法进行大数的相加:

    function sumBigNumber(a, b) {
      let res = '';
      let temp = 0;
      
      a = a.split('');
      b = b.split('');
      
      while (a.length || b.length || temp) {
        temp += ~~a.pop() + ~~b.pop();
        res = (temp % 10) + res;
        temp  = temp > 9
      }
      return res.replace(/^0+/, '');
    }
    

    其主要的思路如下:

    • 首先用字符串的方式来保存大数,这样数字在数学表示上就不会发生变化
    • 初始化res,temp来保存中间的计算结果,并将两个字符串转化为数组,以便进行每一位的加法运算
    • 将两个数组的对应的位进行相加,两个数相加的结果可能大于10,所以可能要仅为,对10进行取余操作,将结果保存在当前位
    • 判断当前位是否大于9,也就是是否会进位,若是则将temp赋值为true,因为在加法运算中,true会自动隐式转化为1,以便于下一次相加
    • 重复上述操作,直至计算结束

    13. 实现 add(1)(2)(3)

    函数柯里化概念: 柯里化(Currying)是把接受多个参数的函数转变为接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。

    1)粗暴版

    function add (a) {
    return function (b) {
        return function (c) {
          return a + b + c;
        }
    }
    }
    console.log(add(1)(2)(3)); // 6
    

    2)柯里化解决方案

    • 参数长度固定
    var add = function (m) {
      var temp = function (n) {
        return add(m + n);
      }
      temp.toString = function () {
        return m;
      }
      return temp;
    };
    console.log(add(3)(4)(5)); // 12
    console.log(add(3)(6)(9)(25)); // 43
    

    对于add(3)(4)(5),其执行过程如下:

    1. 先执行add(3),此时m=3,并且返回temp函数;
    2. 执行temp(4),这个函数内执行add(m+n),n是此次传进来的数值4,m值还是上一步中的3,所以add(m+n)=add(3+4)=add(7),此时m=7,并且返回temp函数
    3. 执行temp(5),这个函数内执行add(m+n),n是此次传进来的数值5,m值还是上一步中的7,所以add(m+n)=add(7+5)=add(12),此时m=12,并且返回temp函数
    4. 由于后面没有传入参数,等于返回的temp函数不被执行而是打印,了解JS的朋友都知道对象的toString是修改对象转换字符串的方法,因此代码中temp函数的toString函数return m值,而m值是最后一步执行函数时的值m=12,所以返回值是12。
    • 参数长度不固定
    function add (...args) {
        //求和
        return args.reduce((a, b) => a + b)
    }
    function currying (fn) {
        let args = []
        return function temp (...newArgs) {
            if (newArgs.length) {
                args = [
                    ...args,
                    ...newArgs
                ]
                return temp
            } else {
                let val = fn.apply(this, args)
                args = [] //保证再次调用时清空
                return val
            }
        }
    }
    let addCurry = currying(add)
    console.log(addCurry(1)(2)(3)(4, 5)())  //15
    console.log(addCurry(1)(2)(3, 4, 5)())  //15
    console.log(addCurry(1)(2, 3, 4, 5)())  //15
    

    14. 实现类数组转化为数组

    类数组转换为数组的方法有这样几种:

    • 通过 call 调用数组的 slice 方法来实现转换
    Array.prototype.slice.call(arrayLike);
    
    • 通过 call 调用数组的 splice 方法来实现转换
    Array.prototype.splice.call(arrayLike, 0);
    
    • 通过 apply 调用数组的 concat 方法来实现转换
    Array.prototype.concat.apply([], arrayLike);
    
    • 通过 Array.from 方法来实现转换
    Array.from(arrayLike);
    

    15. 使用 reduce 求和

    arr = [1,2,3,4,5,6,7,8,9,10],求和

    let arr = [1,2,3,4,5,6,7,8,9,10]
    arr.reduce((prev, cur) => { return prev + cur }, 0)
    

    arr = [1,2,3,[[4,5],6],7,8,9],求和

    let arr = [1,2,3,4,5,6,7,8,9,10]
    arr.flat(Infinity).reduce((prev, cur) => { return prev + cur }, 0)
    

    arr = [{a:1, b:3}, {a:2, b:3, c:4}, {a:3}],求和

    let arr = [{a:9, b:3, c:4}, {a:1, b:3}, {a:3}] 
    
    arr.reduce((prev, cur) => {
        return prev + cur["a"];
    }, 0)
    

    16. 将js对象转化为树形结构

    // 转换前:
    source = [{
                id: 1,
                pid: 0,
                name: 'body'
              }, {
                id: 2,
                pid: 1,
                name: 'title'
              }, {
                id: 3,
                pid: 2,
                name: 'div'
              }]
    // 转换为: 
    tree = [{
              id: 1,
              pid: 0,
              name: 'body',
              children: [{
                id: 2,
                pid: 1,
                name: 'title',
                children: [{
                  id: 3,
                  pid: 1,
                  name: 'div'
                }]
              }
            }]
    

    代码实现:

    function jsonToTree(data) {
      // 初始化结果数组,并判断输入数据的格式
      let result = []
      if(!Array.isArray(data)) {
        return result
      }
      // 使用map,将当前对象的id与当前对象对应存储起来
      let map = {};
      data.forEach(item => {
        map[item.id] = item;
      });
      // 
      data.forEach(item => {
        let parent = map[item.pid];
        if(parent) {
          (parent.children || (parent.children = [])).push(item);
        } else {
          result.push(item);
        }
      });
      return result;
    }
    

    17. 使用ES5和ES6求函数参数的和

    ES5:

    function sum() {
        let sum = 0
        Array.prototype.forEach.call(arguments, function(item) {
            sum += item * 1
        })
        return sum
    }
    

    ES6:

    function sum(...nums) {
        let sum = 0
        nums.forEach(function(item) {
            sum += item * 1
        })
        return sum
    }
    

    18. 解析 URL Params 为对象

    let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
    parseParam(url)
    /* 结果
    { user: 'anonymous',
      id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型
      city: '北京', // 中文需解码
      enabled: true, // 未指定值得 key 约定为 true
    }
    */
    
    function parseParam(url) {
      const paramsStr = /.+?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
      const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
      let paramsObj = {};
      // 将 params 存到对象中
      paramsArr.forEach(param => {
        if (/=/.test(param)) { // 处理有 value 的参数
          let [key, val] = param.split('='); // 分割 key 和 value
          val = decodeURIComponent(val); // 解码
          val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字
          if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
            paramsObj[key] = [].concat(paramsObj[key], val);
          } else { // 如果对象没有这个 key,创建 key 并设置值
            paramsObj[key] = val;
          }
        } else { // 处理没有 value 的参数
          paramsObj[param] = true;
        }
      })
      return paramsObj;
    }
    

    结语

    • 点赞关注不迷路
    • 干完这些题;欢迎关注公众号前端要努力,一起努力学习!

    下载网 » javascript精进之路手写系列第二弹实际应用篇(18个)(附详解)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元