前端手写(正则、字符串、函数)

1 实现千位分隔符

// 保留三位小数
parseToMoney(1234.56); // return '1,234.56'
parseToMoney(123456789); // return '123,456,789'
parseToMoney(1087654.321); // return '1,087,654.321'
 function parseToMoney(num) {
            // toFixed(3) 函数将 num 保留三位小数并返回一个字符串
            num = parseFloat(num.toFixed(3));
            // 整个表达式的作用就是将整数的末尾到倒数第三位的每个三个数字字符的连续出现(即千位)前插入逗号
            let [integer, decimal] = String.prototype.split.call(num, '.');
            integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,');
            return integer + '.' + (decimal ? decimal : '');
        }

2 判断是否是电话号码

function isPhone(tel) {
            var regx = /^1[34578]\d{9}$/;
            return regx.test(tel);
        }

3 验证是否是邮箱

 function isEmail(email) {
            var regx = /^([a-zA-Z0-9_\-])+@([a-zA-Z0-9_\-])+(\.[a-zA-Z0-9_\-])+$/;
            return regx.test(email);
        }

4 验证是否是身份证

  function isCardNo(number) {
            var regx = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
            return regx.test(number);
        }

5 用正则写一个根据name获取cookie中的值的方法

function getCookie(name) {
            var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]*)'));
            if (match) return decodeURIComponent(match[2])
        }

函数柯里化相关

1 实现一个JS函数柯里化

预先处理的思想,利用闭包的机制

柯里化的定义:接收一部分参数,返回一个函数接收剩余参数,接收足够参数后,执行原函数

函数柯里化的主要作用和特点就是参数复用提前返回延迟执行

 function curry(fn, args) {
            var length = fn.length;
            var args = args || [];
            return function () {
                newArgs = args.concat(Array.prototype.slice.call(arguments));
                if (newArgs.length < length) {
                    return curry.call(this, fn, newArgs);
                } else {
                    return fn.apply(this, newArgs);
                }
            }
        }
        const curry = (fn, arr = []) => { // arr就是我们要收集每次调用时传入的参数
            let len = fn.length; // 函数的长度,就是参数的个数
            return function (...args) {
                let newArgs = [...arr, ...args] // 收集每次传入的参数
                // 如果传入的参数个数等于我们指定的函数参数个数,就执行指定的真正函数
                if (newArgs.length === len) {
                    return fn(...newArgs)
                } else {
                    // 递归收集参数
                    return curry(fn, 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)

ES6写法

const curry = (fn, arr = []) => (...args) => (
            arg => arg.length === fn.length ?
            fn(...arg) :
            curry(fn, arg)
        )([...arr, ...args])
// 柯里化简单应用
// 判断类型,参数多少个,就执行多少次收集
function isType(type, val) {
  return Object.prototype.toString.call(val) === `[object ${type}]`
}

let newType = curry(isType)

// 相当于把函数参数一个个传了,把第一次先缓存起来
let isString = newType('String')
let isNumber = newType('Number')

isString('hello world')
isNumber(999)

2 请实现一个 add 函数,满足以下功能

add(1); 			// 1
add(1)(2);  	// 3
add(1)(2)(3);// 6
add(1)(2, 3); // 6
add(1, 2)(3); // 6
add(1, 2, 3); // 6
 function add(...args) {
            // 在内部声明一个函数,利用闭包的特性保存并收集所有的参数值
            let fn = function (...newArgs) {
                return add.apply(null, args.concat(newArgs))
            }
            // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
            fn.toString = function () {
                return args.reduce((total, curr) => total + curr)
            }
            return fn
        }

        // 测试,调用toString方法触发求值

add(1).toString(); 			// 1
add(1)(2).toString();  	// 3
add(1)(2)(3).toString();// 6
add(1)(2, 3).toString(); // 6
add(1, 2)(3).toString(); // 6
add(1, 2, 3).toString(); // 6

 

 

3 实现 (5).add(3).minus(2) 功能

例: 5 + 3 - 2,结果为 6

   Number.prototype.add = function (n) {
            return this.valueOf() + n;
        };
        Number.prototype.minus = function (n) {
            return this.valueOf() - n;
        };

实现add(1)(2) =3

  // 题意的答案
        const add = (num1) => (num2) => num2 + num1;

        // 整了一个加强版 可以无限链式调用 add(1)(2)(3)(4)(5)....
        function add(x) {
            // 存储和
            let sum = x;

            // 函数调用会相加,然后每次都会返回这个函数本身
            let tmp = function (y) {
                sum = sum + y;
                return tmp;
            };

            // 对象的toString必须是一个方法 在方法中返回了这个和
            tmp.toString = () => sum
            return tmp;
        }

        alert(add(1)(2)(3)(4)(5))

32 字符串相关

1 查找字符串中出现最多的字符和个数

在此函数中,第一个参数 $0 表示完整的匹配子字符串,而第二个参数 $1 表示匹配中第一个捕获组的内容。

let str = "abcabcabcbbccccc";
        let num = 0;
        let char = '';
        // 使其按照一定的次序排列
        str = str.split('').sort().join('');
        // "aaabbbbbcccccccc"
        // 定义正则表达式
        let re = /(\w)\1+/g;
        str.replace(re, ($0, $1) => {
            if (num < $0.length) {
                num = $0.length;
                char = $1;
            }
        });
        console.log(`字符最多的是${char},出现了${num}次`);

2 字符串查找

请使用最基本的遍历来实现判断字符串 a 是否被包含在字符串 b 中,并返回第一次出现的位置(找不到返回 -1)。

a='34';b='1234567'; // 返回 2
a='35';b='1234567'; // 返回 -1
a='355';b='12354355'; // 返回 5
isContain(a,b);
function isContain(a, b) {
            for (let i in b) {
                if (a[0] === b[i]) {
                    let tmp = true;
                    for (let j in a) {
                        if (a[j] !== b[~~i + ~~j]) {
                            tmp = false;
                        }
                    }
                    if (tmp) {
                        return i;
                    }
                }
            }
            return -1;
        }

3 字符串最长的不重复子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。


示例 1:

输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

示例 4:

输入: s = ""
输出: 0
 const lengthOfLongestSubstring = function (s) {
            if (s.length === 0) {
                return 0;
            }

            let left = 0;
            let right = 1;
            let max = 0;
            while (right <= s.length) {
                let lr = s.slice(left, right);
                const index = lr.indexOf(s[right]);
                if (index > -1) {
                    left = index + left + 1;
                } else {
                    lr = s.slice(left, right + 1);
                    max = Math.max(max, lr.length);
                }
                right++;
            }
            return max;
        };

33 实现工具函数

1 对象扁平化

 function objectFlat(obj = {}) {
            const res = {}

            function flat(item, preKey = '') {
                Object.entries(item).forEach(([key, val]) => {
                    const newKey = preKey ? `${preKey}.${key}` : key
                    if (val && typeof val === 'object') {
                        flat(val, newKey)
                    } else {
                        res[newKey] = val
                    }
                })
            }
            flat(obj)
            return res
        }

        // 测试
        const source = {
            a: {
                b: {
                    c: 1,
                    d: 2
                },
                e: 3
            },
            f: {
                g: 2
            }
        }
        console.log(objectFlat(source));

2 实现一个管理本地缓存过期的函数

封装一个可以设置过期时间的localStorage存储函数

let storage = new Storage();
storage.setItem({
  name:"name",
  value:"ppp"
})
class Storage {
            constructor(name) {
                this.name = 'storage';
            }
            //设置缓存
            setItem(params) {
                let obj = {
                    name: '', // 存入数据  属性
                    value: '', // 属性值
                    expires: "", // 过期时间
                    startTime: new Date().getTime() //记录何时将值存入缓存,毫秒级
                }
                let options = {};
                //将obj和传进来的params合并
                Object.assign(options, obj, params);
                if (options.expires) {
                    //如果options.expires设置了的话
                    //以options.name为key,options为值放进去
                    localStorage.setItem(options.name, JSON.stringify(options));
                } else {
                    //如果options.expires没有设置,就判断一下value的类型
                    let type = Object.prototype.toString.call(options.value);
                    //如果value是对象或者数组对象的类型,就先用JSON.stringify转一下,再存进去
                    if (Object.prototype.toString.call(options.value) == '[object Object]') {
                        options.value = JSON.stringify(options.value);
                    }
                    if (Object.prototype.toString.call(options.value) == '[object Array]') {
                        options.value = JSON.stringify(options.value);
                    }
                    localStorage.setItem(options.name, options.value);
                }
            }
            //拿到缓存
            getItem(name) {
                let item = localStorage.getItem(name);
                //先将拿到的试着进行json转为对象的形式
                try {
                    item = JSON.parse(item);
                } catch (error) {
                    //如果不行就不是json的字符串,就直接返回
                    item = item;
                }
                //如果有startTime的值,说明设置了失效时间
                if (item.startTime) {
                    let date = new Date().getTime();
                    //何时将值取出减去刚存入的时间,与item.expires比较,如果大于就是过期了,如果小于或等于就还没过期
                    if (date - item.startTime > item.expires) {
                        //缓存过期,清除缓存,返回false
                        localStorage.removeItem(name);
                        return false;
                    } else {
                        //缓存未过期,返回值
                        return item.value;
                    }
                } else {
                    //如果没有设置失效时间,直接返回值
                    return item;
                }
            }
            //移出缓存
            removeItem(name) {
                localStorage.removeItem(name);
            }
            //移出全部缓存
            clear() {
                localStorage.clear();
            }
        }

设置5秒过期

let storage = new Storage();
storage.setItem({
  name:"name",
  value:"ppp",
  expires: 5000
})
// 过期后再取出来会变为 false
let value = storage.getItem('name');
console.log('我是value',value);

3 实现lodash的chunk方法--数组按指定长度拆分

/**
 * @param input
 * @param size
 * @returns {Array}
 */
_.chunk(['a', 'b', 'c', 'd'], 2)
// => [['a', 'b'], ['c', 'd']]

_.chunk(['a', 'b', 'c', 'd'], 3)
// => [['a', 'b', 'c'], ['d']]

_.chunk(['a', 'b', 'c', 'd'], 5)
// => [['a', 'b', 'c', 'd']]

_.chunk(['a', 'b', 'c', 'd'], 0)
// => []

 

 
function chunk(arr, length) {
  let newArr = [];
  for (let i = 0; i < arr.length; i += length) {
    newArr.push(arr.slice(i, i + length));
  }
  return newArr;
}

4 手写深度比较isEqual

思路:深度比较两个对象,就是要深度比较对象的每一个元素。=> 递归

  • 递归退出条件:
    • 被比较的是两个值类型变量,直接用“===”判断
    • 被比较的两个变量之一为null,直接判断另一个元素是否也为null
  • 提前结束递推:
    • 两个变量keys数量不同
    • 传入的两个参数是同一个变量
  • 递推工作:  - 深度比较每一个key
 function isEqual(obj1, obj2) {
            //其中一个为值类型或null
            if (!isObject(obj1) || !isObject(obj2)) {
                return obj1 === obj2;
            }

            //判断是否两个参数是同一个变量
            if (obj1 === obj2) {
                return true;
            }

            //判断keys数是否相等
            const obj1Keys = Object.keys(obj1);
            const obj2Keys = Object.keys(obj2);
            if (obj1Keys.length !== obj2Keys.length) {
                return false;
            }

            //深度比较每一个key
            for (let key in obj1) {
                if (!isEqual(obj1[key], obj2[key])) {
                    return false;
                }
            }

            return true;
        }

5 实现一个JSON.stringify

JSON.stringify(value[, replacer [, space]]):
  • Boolean | Number| String类型会自动转换成对应的原始值。
  • undefined、任意函数以及symbol,会被忽略(出现在非数组对象的属性值中时),或者被转换成 null(出现在数组中时)。
  • 不可枚举的属性会被忽略如果一个对象的属性值通过某种间接的方式指回该对象本身,即循环引用,属性也会被忽略
  • 如果一个对象的属性值通过某种间接的方式指回该对象本身,即循环引用,属性也会被忽略
 function jsonStringify(obj) {
            let type = typeof obj;
            if (type !== "object") {
                if (/string|undefined|function/.test(type)) {
                    obj = '"' + obj + '"';
                }
                return String(obj);
            } else {
                let json = []
                let arr = Array.isArray(obj)
                for (let k in obj) {
                    let v = obj[k];
                    let type = typeof v;
                    if (/string|undefined|function/.test(type)) {
                        v = '"' + v + '"';
                    } else if (type === "object") {
                        v = jsonStringify(v);
                    }
                    json.push((arr ? "" : '"' + k + '":') + String(v));
                }
                return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
            }
        }
        jsonStringify({
            x: 5
        }) // "{"x":5}"
        jsonStringify([1, "false", false]) // "[1,"false",false]"
        jsonStringify({
            b: undefined
        }) // "{"b":"undefined"}"

6 实现一个JSON.parse

用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。提供可选的reviver函数用以在返回之前对所得到的对象执行变换(操作)

function jsonParse(opt) {
    return eval('(' + opt + ')');
}
jsonParse(jsonStringify({x : 5}))
// Object { x: 5}
jsonParse(jsonStringify([1, "false", false]))
// [1, "false", falsr]
jsonParse(jsonStringify({b: undefined}))
// Object { b: "undefined"}

7 解析 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;
}

8 转化为驼峰命名

var s1 = "get-element-by-id"

// 转化为 getElementById

var f = function(s) {
    return s.replace(/-\w/g, function(x) {
        return x.slice(1).toUpperCase();
    })
}

9 实现一个函数判断数据类型

function getType(obj) {
   if (obj === null) return String(obj);
   return typeof obj === 'object' 
   ? Object.prototype.toString.call(obj).replace('[object ', '').replace(']', '').toLowerCase()
   : typeof obj;
}

// 调用
getType(null); // -> null
getType(undefined); // -> undefined
getType({}); // -> object
getType([]); // -> array
getType(123); // -> number
getType(true); // -> boolean
getType('123'); // -> string
getType(/123/); // -> regexp
getType(new Date()); // -> date

10 对象数组列表转成树形结构(处理菜单)

[
    {
        id: 1,
        text: '节点1',
        parentId: 0 //这里用0表示为顶级节点
    },
    {
        id: 2,
        text: '节点1_1',
        parentId: 1 //通过这个字段来确定子父级
    }
    ...
]

转成
[
    {
        id: 1,
        text: '节点1',
        parentId: 0,
        children: [
            {
                id:2,
                text: '节点1_1',
                parentId:1
            }
        ]
    }
]
function listToTree(data) {
  let temp = {};
  let treeData = [];
  for (let i = 0; i < data.length; i++) {
    temp[data[i].id] = data[i];
  }
  for (let i in temp) {
    if (+temp[i].parentId != 0) {
      if (!temp[temp[i].parentId].children) {
        temp[temp[i].parentId].children = [];
      }
      temp[temp[i].parentId].children.push(temp[i]);
    } else {
      treeData.push(temp[i]);
    }
  }
  return treeData;
}

 

 

11 树形结构转成列表(处理菜单)

[
    {
        id: 1,
        text: '节点1',
        parentId: 0,
        children: [
            {
                id:2,
                text: '节点1_1',
                parentId:1
            }
        ]
    }
]
转成
[
    {
        id: 1,
        text: '节点1',
        parentId: 0 //这里用0表示为顶级节点
    },
    {
        id: 2,
        text: '节点1_1',
        parentId: 1 //通过这个字段来确定子父级
    }
    ...
]
function treeToList(data) {
  let res = [];
  const dfs = (tree) => {
    tree.forEach((item) => {
      if (item.children) {
        dfs(item.children);
        delete item.children;
      }
      res.push(item);
    });
  };
  dfs(data);
  return res;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值