前端核心手写面试题(看你的马步扎实不扎实)

防抖

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    防抖: <input id="input" type="text">
</body>
<script>
    // 监听拿到input输入的值
    input.addEventListener('input', function (e) {
        val(e.target.value)
    })
    // 防抖的核心代码
    function fn(time, fun) {
        let flag // 定义状态
        return function (value) {
            clearTimeout(flag)// 在执行之前 清除 定时器的 flag 不让他执行
            flag = setTimeout(() => {
                fun(value)
            }, time)
        }
    }
    let val = fn(1000, function (val) {
        console.log(val)
    })
</script>
</html>

节流

<body>
    <button id="button">手在快1秒执行一次</button>
</body>
<script>
    /*
        定时器版本的
          fns 回调函数
          time 间隔时间
        function throttle(fns, time) {
        let flag // 定义一个空状态
        return function () { // 内部函数访问外部函数形成闭包
            if (!flag) { // 状态为空执行
                flag = setTimeout(() => {
                    fns.apply(this, arguments) // 改变this指向 吧 event 事件对象传出去
                    flag = null
                }, time)
            }
        }
       }
    */

    function throttle(fun, time) {
        let flag = 0
        return function () {
            let now = +new Date().valueOf()
            // 当前的值 减去上一次的值 >= 传过来的事件 执行
            if (now - flag >= time) {
                fun.apply(this, arguments)
                flag = now
            }
        }
    }



    button.onclick = throttle((e) => {
        console.log(e)
    }, 1000)
</script>

深拷贝

   var arr = {
        a: 2,
        b: [33]
    }

  function cloneDeep(arr = {}) {
        // 终止递归 判断如果传进来的数据不是 object 或者 传进来的是一个 null 直接返回
        if (!arr || typeof arr != 'object' || arr == null) return arr
        // 声明一个对象
        let result
        // 用 instanceof 判断原型链上是否有该类型的原型 是 Array => [] ! Arrays =>{}
        arr instanceof Array ? result = [] : result = {}
        // forin 循环对象的key值
        for (const key in arr) {
            //  对象 key 赋值 result
            result[key] = cloneDeep(arr[key])
        }
        return result
   }


    let arr2 = cloneDeep(arr)
    // let arr2 = [...arr]
    arr2.b[0] = 5
    console.log(arr);//  {a: 2, b: Array(1)}   Array  a: 2  b: [33]
    console.log(arr2);//  {a: 2, b: Array(1)}  Array  a: 2  b: [5]
    // 修改新对象不影响原来的对象   

类型判断

    /*
      实现一个类型判断
        []     返回 array
        {}     返回object
        /^$/   RegExp
        1      number
   */


    //   第一种方案
    function type_of(obj) {
        let res = Object.prototype.toString.call(obj).split(' ')[1] // 输出 RegExp]
        /*
        res.substring(0, res.length - 1) 从第 0 项开始截取 截取到它的长度 -1 
        也就是最后一项的前一项 把最后的一项  ] 给干掉
        */
        return res.substring(0, res.length - 1).toLowerCase()
    }


    // 第二种方案
    function type_of(obj) {
        // .slice(8, -1) 从 第 8项开始截取 -1 不要最后一项
        return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
    }

数字千位分割

  const format = (n) => {
        let num = n.toString() // 拿到传进来的 number 数字 进行 toString
        let len = num.length // 在拿到字符串的长度
        // 当传进来的结果小于 3 也就是 千位还把结果返回出去 小于3 不足以分割
        if (len < 3) {
            return num
        } else {
            let render = len % 3 //传入 number 的长度 是否能被 3 整除
            console.log(render)
            /*
            match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。
            该方法类似 indexOf() 和 lastIndexOf(),但是它返回指定的值,而不是字符串的位置。
            var str = '123123000'
            str.match(/\w{3}/g).join(',') // 123,123,000
            */
            if (render > 0) { // 说明不是3的整数倍
                return num.slice(0, render) + ',' + num.slice(render, len).match(/\d{3}/g).join(',')
            } else {
                return num.slice(0, len).match(/\d{3}/g).join(',')
            }
        }
    }

    let str = format(298000)
    console.log(str)

生成随机数

MDN随机数 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/random

    // 生成随机数
    function random(min, max) {
        //  Math.floor() 向下进行取证
        return Math.floor(Math.random() * (max - min + 1) + min)
    }
    console.log(random(3, 5))

轮询方法的封装

const pollFetch = async (callback,pollingInterval,delayTimeStatus = false,stop = false) => {
    if(stop) return
    const timeoutPromise = (delayTime) => {
        return new Promise(resolve => {
            window.timeId = setTimeout(resolve, delayTime);
        })
    }
    while (true) {
        const pollingTime = 1 * pollingInterval;
        const delayTime = pollingTime + (1000 * parseInt(Math.random() * 40, 10));     // 轮询时间间隔
        try {
            await callback();
            await timeoutPromise(delayTimeStatus ? delayTime : pollingTime);
        } catch (e) {
            await timeoutPromise(delayTimeStatus ? delayTime : pollingTime);
        }
    }
}
/*
 pollFetch(()=> {},1000,true)
 @ callback          回调              必穿     任务队列 
 @ pollingInterval   1000 == 一秒      必穿     轮询时间打印服务器的
 @ delayTimeStatus   不布尔            非必穿   延迟的状态
 @ stop              布尔              菲必传   停止定时器 
*/
pollFetch(() => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
            console.log(4243242342)
        }, 50);
    })
},200,false,false)

手写call方法

    // 给function的原型上面添加一个 _call 方法
    Function.prototype._call = function (context) {
        //  判断调用者是否是一个函数  this 就是调用者
        if (typeof this != 'function') {
            return
        }
        // 如果有 context 传参就是传参者 没有就是window
        that = context || window
        // 保存当前调用的函数
        that.fn = this   // 吧 this == fns   存到that.fn来
        // 截取传过来的参数
        /*
          arguments
                 a: 1
                 fn: ƒ fns()
        */
        // 通过 slice 来截取传过来的参数
        const local = [...arguments].slice(1)
        // 传入参数调用函数
        let result = that.fn(...local)
        // 删出 fn 属性
        delete that.fn
        return result
    }

    let obj = { a: 1 }
    function fns(a, b) {
        console.log(a, b);
        console.log(this)
    }
    fns._call(obj, 23, 555)

手写apply

    Function.prototype.myApply = function (context) {
        if (typeof this !== 'function') {
            // 判断当前调用者是否为函数
            return
        }
        // 保存传入的this指向
        that = context || window
        // 保存当前调用的函数
        that.fn = this
        let result
        // 这里开始判断传入的参数是否存在,此时参数是一个数组形式[thisArg,[传参]]
        // 那么如果arguments[1]即传参存在的时候,就是需要传参调用保存的函数
        // 如果不存在就直接调用函数
        if (arguments[1]) {
            result = that.fn(...arguments[1])
        } else {
            result = that.fn()
        }
        return result
    }

    let obj = {
        a: 1
    }

    function fn(...val) {
        console.log(this)
        console.log(...val)
    }
    fn.apply(obj, [1, 4, 5, 6, 7, 9])

手写 bind

    Function.prototype._bind = function (context) {
        // 当调用用了_bind 函数  this 指向调用 _bind 的那个函数
        // this 不是 function 不让使用 抛出类型错误
        if (typeof this != 'function') {
            throw new TypeError('Error')
        }
        // 获取参数 除了传过来的第一项我全都要
        const args = [...arguments].slice(1)
        // 吧 function 进行一个缓存
        fn = this

        // 返回一个函数 因为 bind 不是立即调用的
        return function Fn() {
            // 判断  this 原型链上是否又该类型的原型 Fn
            return fn.apply(this instanceof Fn ? new fn(...arguments) : context, args.concat(...arguments))
        }

    }

    let obj = {
        a: 1
    }

    function fn(...val) {
        console.log(this)               // obj
        console.log(...val)   // 231, 31242344, 432
    }
    fn._bind(obj, 231, 31242344, 432)()

数组去重

        /*
          数组的去重 
        */
        let ylbsz = [1, 35, 6, 78, 66, 6, 35]
        function _set(arr) {
            // 放一个新数组
            let newArr = []
            for (let i = 0; i < arr.length; i++) {
                if (newArr.indexOf(arr[i]) == -1) {
                    newArr.push(arr[i])
                }
            }
            return newArr
        }
        console.log(_set(ylbsz))
        console.log([...new Set([11, 11, 222, 222])])

        // 字符串去重
        let str = '123321你好你好'
        console.log([...new Set(str.split(''))].join(''))

数组排序(冒泡)

    function sort(arr) {
        // 外层循环控制的是比较的轮数,你要循环几轮
        //  arr.length (5 ) - 1  = 4
        for (let i = 0; i < arr.length - 1; i++) {
            // 内层循环控制的每一轮交换的次数
            // 第一轮要比较4次   下标为 0     arr.length - 1(4) - i(0)  =  4
            // 第二轮要比较3次   下标为 1     arr.length - 1(4) - i(1)  =  3
            // 第三轮要比较2次   下标为 2     arr.length - 1(4) - i(2)  =  2
            // 第四轮要比较1次   下标为 3     arr.length - 1(4) - i(3)  =  1

            // 内层循环控制的每一轮交换的次数
            for (let j = 0; j < arr.length - 1 - i; j++) {
                // arr[j] 第一项  arr[j + 1] 第二项
                // arr[j] 第二项  arr[j + 1] 第三项
                // ....
                if (arr[j] > arr[j + 1]) {
                    //  大于后一项 做一个交换变量 es6 结构赋值 的变量交换
                    [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
                }
            }
        }
        return arr
    }
    console.log(sort([200, 100, 3, 9, 4]));

数组的排序(快排)

    let arr = [1, 4, 6, 7, 7, 9]

    // 1. 取出我们数组的中间项 用splice 拿到中间项 
    // 2. 在声明两个空数组,一个空数组用来放置 中间一项 大于 循环的每一项的话 就把他放到左边的数组里,下
    // 余的话就放到右边的数组
    // 3.接下来就是循环便利了 
    // 最后再次利用函数自己调用自己一直执行 递归
    // 4. 如果传进来的数组 小于等于 1 的话就终止递归循环

    // 做一个快排
    function fn(arr) {
        // 如果传进来的数组 小于等于 1 的话就终止递归循环
        if (arr.length <= 1) return arr

        // 总长度除以2的话就可以拿到中间一项的下标
        let n = Math.floor(arr.length / 2)
        // splice 返回被删除的元素 下标为 0 就是只拿第一项 因为它只返回一个数据 就是被删除的一项
        let cen = arr.splice(n, 1)[0]

        // 2. 在声明两个空数组,一个空数组用来放置 中间一项 大于 循环的每一项的话 就把他放到左边的数组里,下
        // 余的话就放到右边的数组

        let leftArr = []
        let rightArr = []
        // 接下来就是循环便利了 判断
        for (let j = 0; j < arr.length; j++) {
            let item = arr[j]
            //  > 如果是大于就是 从大到小   如果是小于  < 从小到大
            cen < item ? rightArr.push(item) : leftArr.push(item)
        }

        return fn(leftArr).concat(cen, fn(rightArr))
    }
    console.log(fn(arr))

数组排序(插入排序)

  function insert(arr) {
        // 1、准备一个数组这个数组就是新抓的牌,开始先抓一张牌进来
        let handle = []
        handle.push(arr[0])
        // 2、第二次开始按顺序抓牌一直把牌抓光了
        for (let i = 1; i < arr.length; i++) {
            // a 是新抓的牌
            let a = arr[i]
            // 和手里的牌进行比较(从后向前比)
            //  handle.length - 1 因为下标是从 0 开始数的
            for (let j = handle.length - 1; j >= 0; j--) {
                // 每一次要比较手里的牌
                let b = handle[j]
                // 如果当前新牌a比b 大了,可就要放到 b 的后面
                // 如果 a > b 就是表示从小到大
                // a < b 送大到小
                if (a > b) {
                    // 如果想要放到 j 的后面就要加上一个 1,
                    /**
                     * 比如说我要插队一般要插就是插到 a 的前面
                     * 但是我想要插到 a 的后面怎么办
                     * 那这个时候就要插到 a 下一个人的人前面就可以了
                     * 
                    */
                    handle.splice(j + 1, 0, a)
                    break
                }
                // 还有一种情况就是如果你比较到第一项的还比较吗,不比较了
                // 我们吧新牌放到最前面即可
                if (j == 0) {
                    handle.unshift(a)
                }
            }
        }
        return handle
    }

    console.log(insert([2, 8, 5, 92, 52, 4]))

数组的最大值最小值

    const array = [5, 4, 7, 8, 9, 2];
    let num = array.reduce((a, b) => a > b ? a : b);
    console.log(num) // 9
    let nums = array.reduce((a, b) => a < b ? a : b);
    console.log(nums) // 2

对象去重

    /*@  数据处理对象去重*/
    let arr = [
        {
            a: 1,
            b: 2,
            id: 6666
        },
        {
            a: 1,
            b: 2,
            id: 6666
        },
        {
            a: 6,
            b: 8,
            id: 77777
        },
        {
            a: 8,
            b: 2,
            id: 88888
        },
    ]
    /*
      @ 输出 : 
          0: { a: 1, b: 2, id: 6666 }
          1: { a: 6, b: 8, id: 77777 }
          2: { a: 8, b: 2, id: 88888 }
    */
    
 function ssff(arr4, id) {
        var obj = {};
        function deWeightFour() {
            arr4 = arr4.reduce(function (a, b) {
                obj[b.id] ? '' : obj[b.id] = true && a.push(b);
                return a;
            }, [])
            return arr4;
        }
        var newArr4 = deWeightFour();
        return newArr4
 }
 console.log(ssff(arr, 'id'))

手写 indexOf

    ~ function () {
        // 在函数前加上波浪号,其作用是把函数声明转换为表达式,
        // 就可以直接将下面的代码放入某个函数里运行。
        // 不用indexOf 和 includes

        function myIndexOf(a) {
            // 1、 这个也可以正则实现 下面代码
            // let reg = new RegExp(a)
            // res = reg.exec(this)
            // return res === nu ll ? -1 : res.index
            // 这个也可以正则实现

            let lena = a.length
            y = this.length
            flag = -1
            if (lena > y) return -1
            // 如果输入的字符串大于要检测的字符串直接 -1
            for (var i = 0; i <= y - lena; i++) {
                if (this.substr(i, lena) === a) {
                    // substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符。
                    flag = i
                    break
                }
            }
            return flag
        }
        String.prototype.myIndexOf = myIndexOf
    }()
    let demo = 'dwanlghMappaw'
    let str = 'h'
    console.log(demo.myIndexOf(str));

手写 instanceof

    // instanceof
    // 1、只要当前类出现在实例的原型上,结果都为 true 
    // 2、由于我们可以肆意的修改原型的指向,所以检测出来的结果是不准确的
    // 3、不能检测基本数据类型


    var arr = []
    console.log(arr);
    function instance_of(example, classFunc) {    // 实例.__proto__ === 类.prototype   => instanceof
        let classFuncPrototype = classFunc.prototype // 这个就代表类的原型
        proto = Object.getPrototypeOf(example) // example.__proto__    实例的.__proto__
        while (true) {  // 我也不知道你要找多少次  while循环
            if (proto === null) { // 找到最顶层为null就说明没找到 返回 false
                return false
            }
            if (proto === classFuncPrototype) {
                return true
            }
            proto = Object.getPrototypeOf(proto)
        }
    }
    console.log(instance_of(arr, Array));
    console.log(instance_of(arr, Object));
    console.log(instance_of(arr, RegExp));

怎么判断两个对象是否相等?

/*
① 首先比较两个对象的长度,如果长度不相等使flag为false,为不相等;
② 如果长度相等那就遍历对象1(对象2也可以),利用hasOwnProperty() 方法查看对象1中是否包含对象2中的属性或者方法,如果不包含则使flag为false,为不想等。
hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。                        
③ 接下来判断两对象的内存地址是否相同,不同则为true
*/
	function compreObj(obj1, obj2) {
		var flag = true;
		function compre(obj1, obj2) {
			if (Object.keys(obj1).length != Object.keys(obj2).length) {
				flag = false;
			} else {
				for (let x in obj1) {
					if (obj2.hasOwnProperty(x)) {
						if (obj1[x] !== obj2[x]) {
							compre(obj1[x], obj2[x]);
						}
					} else {
						flag = false;
					}
				}
			}
			if (flag === false) {
				return false;
			} else {
				return true;
			}
		}
		return compre(obj1, obj2)
	}
	console.log(compreObj(对象1, 对象2));

如何快速让字符串变成已千为精度的数字

var str = "10000000000";

/*
第一种:把数字转换成字符串后,打散为数组,再从末尾开始,逐个把数组中的元素插入到新数组(result)的开头。 每插入一个元素,counter就计一次数(加1),
当counter为3的倍数时,就插入一个逗号,但是要注意开头(i为0时)不需要逗号。最后通过调用新数组的join方法得出结果。
*/


String.prototype.toThousands = function(){
	var num = this;
	var result = [ ], counter = 0;
	num = (num || 0).toString().split('');
	for (var i = num.length - 1; i >= 0; i--) {
		counter++;
		result.unshift(num[i]);
		if (!(counter % 3) && i != 0) { result.unshift(','); }
	}
	return result.join('');
}
console.log(str.toThousands());


/*
第二种:通过正则表达式循环匹配末尾的三个数字,每匹配一次,就把逗号和匹配到的内容插入到结果字符串的开头,  
然后把匹配目标(num)赋值为还没匹配的内(RegExp.leftContext)。如果数字的位数是3的倍数时,最后一次匹配到的内容肯定是三个数字,
但是最前面的三个数字前不需要加逗号;如果数字的位数不是3的倍数,那num变量最后肯定会剩下1到2个数字,循环过后,要把剩余的数字插入到结果字符串的开头。
*/

function toThousands(num) {
	var num = (num || 0).toString(), re = /\d{3}$/, result = '';
	while ( re.test(num) ) {
		result = RegExp.lastMatch + result;
		if (num !== RegExp.lastMatch) {
			result = ',' + result;
			num = RegExp.leftContext;
		} else {
			num = '';
			break;
		}
	}
	if (num) { result = num + result; }
	return result;
}
console.log(toThousands(str));  

第三种:第二种的改良版

function toThousands(num) {
	var num = (num || 0).toString(), result = '';
	while (num.length > 3) {
		result = ',' + num.slice(-3) + result;
		num = num.slice(0, num.length - 3);
	}
	if (num) { result = num + result; }
	return result;
}
console.log(toThousands(str));  

第四种:懒人版

function toThousands(num) {
	return (num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
}
console.log(toThousands(str));


如何判断JS对象中是否存在循环引用

我个人的理解是,如果一个对象的值等于父级(祖父级,曾祖父级…),则说明是循环引用了

定义一个空数组吗,且对于目标对象进行递归,每次都判断递归项是否为对象,是的话就放到数组里,而且每次判断属性值是否存在,在的话说明环引用了

function cycle(obj, parent) {
    //表示调用的父级数组
    var parentArr = parent || [obj];
    for (var i in obj) {
        if (typeof obj[i] === "object") {
            //判断是否有循环引用
            parentArr.forEach((pObj) => {
                if (pObj === obj[i]) {
                    obj[i] = "[cycle]"
                }
            });
            cycle(obj[i], [...parentArr, obj[i]])
        }
    }
    return obj;
}


/**@2方法*/

function hasLoop(obj){
	// 判断对象内部是否有和源相同的属性
	function findLoop(target, src){
		// 源数组,并将自身传入
		const source = src.slice().concat([target])
		for(const key in target){
			// 如果是对象才需要判断
			if(typeof target[key] === 'object'){
				// 如果在源数组中找到 || 递归查找内部属性找到相同
				if(source.indexOf(target[key]) > -1 || findLoop(target[key], source)){
					return true
				}
			}
		}
		return false
	}
	// 如果传入值是对象,则执行判断,否则返回false
	return typeof obj === 'object' ? findLoop(obj, []) : false
}

简易版Promise.all

    function PromiseAll(promiseArray) {    //返回一个Promise对象
        return new Promise((resolve, reject) => {
            if (!Array.isArray(promiseArray)) {                        //传入的参数是否为数组
                return reject(new Error('传入的参数不是数组!'))
            }
            const res = []
            let counter = 0                         //设置一个计数器
            for (let i = 0; i < promiseArray.length; i++) {
                Promise.resolve(promiseArray[i]).then(value => {
                    counter++                  //使用计数器返回 必须使用counter
                    res[i] = value
                    if (counter === promiseArray.length) {
                        resolve(res)
                    }
                }).catch(e => reject(e))
            }
        })
    }

    const s1 = new Promise((res, rej) => {
        setTimeout(() => {
            res('p1')
        }, 1000)
    })
    const s2 = new Promise((res, rej) => {
        setTimeout(() => {
            res('p2')
        }, 2000)
    })
    const s3 = new Promise((res, rej) => {
        setTimeout(() => {
            res('p3')
        }, 3000)
    })
    const test = PromiseAll([s1,s2, s3])
        .then(res => console.log(res))
        .catch(e => console.log(e))

    console.log(test);

一维数组转换 tree 结构

    let arr = [
        { id: 1, name: '部门1', pid: 0 },
        { id: 2, name: '部门2', pid: 1 },
        { id: 3, name: '部门3', pid: 1 },
        { id: 4, name: '部门4', pid: 3 },
        { id: 5, name: '部门5', pid: 4 },
    ]

    // // 上面的数据转换为 下面的 tree 数据
    // [
    //     {
    //         "id": 1,
    //         "name": "部门1",
    //         "pid": 0,
    //         "children": [
    //             {
    //                 "id": 2,
    //                 "name": "部门2",
    //                 "pid": 1,
    //                 "children": []
    //             },
    //             {
    //                 "id": 3,
    //                 "name": "部门3",
    //                 "pid": 1,
    //                 "children": [
    //                     {
    //                         id: 4,
    //                         name: '部门4',
    //                         pid: 3,
    //                         "children": [
    //                             {
    //                                 id: 5,
    //                                 name: '部门5',
    //                                 pid: 4,
    //                                 "children": []
    //                             },
    //                         ]
    //                     },
    //                 ]
    //             }
    //         ]
    //     }
    // ]



    function tree(items) {
        // 1、声明一个数组和一个对象 用来存储数据
        let arr = []
        let obj = {}
        // 2、for of  便利我么传进来的一个数据,给当前的数据添加children 属性为 array 把他放到我们的obj对象里面 
        for (let item of items) {
            obj[item.id] = { ...item, children: [] }
        }
        // 3、for of  再次便利然后逻辑处理
        for (let item of items) {
            // 4、把数据里面的id 取出来赋值 方便下一步的操作
            let id = item.id
            let pid = item.pid
            // 5、根据 id  将 obj 里面的每一项数据取出来
            let treeitem = obj[id]
            // 6、如果是第一项的话 吧treeitem 放到 arr 数组当中
            if (pid === 0) {
                // 把数据放到 arr 数组里面
                arr.push(treeitem)
            } else {
                // 如果没有 pid 找不到 就开一个 obj { }
                if (!obj[pid]) {
                    obj = {
                        children: []
                    }
                }

                // 否则给它的 obj 根基 pid(自己定义的下标) 进行查找 它里面的children属性 然后push
                obj[pid].children.push(treeitem)
            }
        }
        // 返回处理好的数据
        return arr
    }
    console.log(tree(arr))

proxy 实现数据绑定

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <li id="li"></li>
    <input id="search" type="text">
</body>
<script>
    let obj = {}
    obj = new Proxy(obj, {
        get(target, prop) {
            return target[prop]
            console.log(target,prop)
        },
        set(target, prop, value) {
            console.log(target, prop, value)
            target[prop] = value
            observer()
        }
    })
    function observer() {
        li.innerHTML = obj.name
        search.value = obj.name
    }
    setTimeout(() => {
        obj.name = '孙志豪'
    }, 1000)
    search.oninput = function () {
        obj.name = this.value
    }

</script>

</html>

思考题 LazyMan

    // LazyMan('Hank');
    // 输出:
    // Hi! This is Hank!


    // LazyMan('Hank').sleep(3).eat('dinner')
    // 输出:
    // Hi! This is Hank!
    // //等待3秒..
    // Wake up after 3
    // Eat dinner~

    // LazyMan('Hank').eat('dinner').eat('supper')
    // 输出:
    // Hi This is Hank!
    // Eat dinner~
    // Eat supper~

    // LazyMan('Hank').sleepFirst(2).eat('dinner').sleep(3).eat('supper')
    // 输出:
    // 等待2秒..
    // Wake up after 2
    // Hi This is Hank!
    // Eat dinner~
    // 等待3秒..
    // Wake up after 3
    // Eat supper~


    // function LazyMan(arr) {
    //     return 'Hi! This is' + arr
    // }

    // console.log(LazyMan('Hank'))


    function LazyMan(params) {
        // 声明一个数组
        let arr = []
        // 这是第一个的输出结果
        const fn = () => { console.log(`Hi This is ${params}!`), next() }
        // 声明一个方法用来控制是否执行下一项
        const next = () => {
            // 返回删除后的第一个数组 
            const fn = arr.shift()
            // 如果第一项在就去执行第一个
            fn && fn()
        }
        // 吧第一个输出放到数组中
        arr.push(fn)
        // 上来执行一次,是最快执行但不是立马执行
        setTimeout(() => { next() })
        // 声明一个对象给 抛出去
        let api = {
            sleepFirst: (content) => {
                //因为这结果是是第一个执行 所以用 unshift
                arr.unshift(() => {
                    setTimeout(() => { console.log('Wake up after' + content); next() }, content * 1000)
                })
                return api
            },
            eat: (content) => {
                // 这个是后面的执行的
                arr.push(() => {
                    console.log(`Eat ${content}~`)
                    next()
                })
                return api
            },
            sleep: (content) => {
                // 延迟并最后执行
                arr.push(() => {
                    setTimeout(() => { console.log('Wake up after' + content); next() }, content * 1000)
                })
                return api
            },
        }
        return api
    }

    LazyMan('Hank').sleepFirst(2).eat('dinner').sleep(3).eat('supper')

思考题 1 某公司1 - 12 月的营业额

    /***
     * @ sun  完成一下需求某公司1 - 12 月的营业额 存放在一个对象里面如一下:
     *  let obj = {
           1: 888,
           2: 542,
           5: 637
        }
       请把数据处理成为 : 
        [888, 542, null, null, 637, null, null, null, null, null, null, null]
     * 
     * */

第一种方案 
    let arr = new Array(12).fill(null).map((item, index) => {
        return obj[index + 1] || null
    })
    console.log(arr)

第二种方案
Array.from(arr) 把对象变为一个数组  前提就是必须要有 length 的属性  arr.length = 13
    obj.length = 13
    let result = Array.from(obj).slice(1).map(item => {
        return typeof item == 'undefined' ? null : item
    })
    console.log(result)


第三种方案
创建一个12项为 null 的数组
    let arr = new Array(12).fill(null)
    //  Object.keys(obj) 获取 obj 的key值
    Object.keys(obj).forEach(item => {
        // 这个 -1 就把下标减去一个
        arr[item - 1] = obj[item]
    })
 console.log(arr)

思考题 2 实现 5.add(3).minus(2), 输出结果为 6

    /**
     *  @ 思考题
     * 实现  5.add(3).minus(2),  输出结果为 6
     *  add调用 加法  minus调用 减法
     */

    ~function () {
        // 每一个方法执行完都会返回一个 number 这类的实例, 这样就可以继续调用number类原型中的方法(链式写法)
        function check(n) {
            n = Number(n)
            return isNaN(n) ? 0 : n
        }
        function add(n) {
            console.log(this)
            // this 就是谁调用我this 就是谁
            n = check(n)
            return this + n
        }

        function minus(n) {
            n = check(n)
            return this - n
        }
        // Number.prototype.add = add
        // Number.prototype.minus = minus

        // 方法多的情况下简写
        ['add', 'minus'].forEach(element => {
            // eval() 函数会将传入的字符串当做 JavaScript 代码进行执行。
            Number.prototype[element] = eval(element)
        });


    }()


    // 由于不能以数字开头 必须加上圆括号
    console.log((5).add(3).minus(2))

思考题3 大写转为小写小写转为大写

    /*
    *   @  这里有一个字符串需要将它吧大写转为小写小写转为大写
    *   let str = 'sUnZHIhaO'
    */

    // charCodeAt(N)  返回在指定的位置的字符的 Unicode 编码

    let str = 'sUnZHIhaO'

    function fn(val) {
        return val.replace(/[a-zA-Z]/g, config => {
            // config 每一次正则匹配的结果
            // charCodeAt a ==65 z ==90 判断是否大小写 config.charCodeAt() >= 65 && config.charCodeAt() <= 90
            // 验证字母大小写:把字母转换为大写,看原来的是否是大写,如果是就转换为小写,不是还是原来
            return config.toUpperCase() === config ? config.toLowerCase() : config.toUpperCase()
        })
        console.log(val)
    }
    console.log(fn('sUnZHIhaO'))

思考题4 找一个字符串中重复最多一项和重复一项的字符串的最多个数

    /*
     var str = 'abcabcddkkkk' 
     @ 查找一个字符串中重复最多一项和重复一项的字符串的最多个数
        输出 k4
    */

    var str = 'abcabcddkkkk'


    function func(str) {
        let obj = {} // 声明一个对象
        for (let i = 0; i < str.length; i++) {
            let item = str[i]
            //  obj[item]  =  如果中间有重复的 obj[item] 就让他+1 没有重复的话就 直接返回 1 
            obj[item] = obj[item] + 1 || 1
        }
        //  声明两个变量一个 max_num是重复最对的个数  重复max_key 以这个是key
        var max_key, max_num = 0
        //   for in 用来便利对象拿到的是key值
        for (const key in obj) {
            if (max_num < obj[key]) {
                //   拿到value 值赋值 max_num
                max_num = obj[key]
                // 拿到 key 赋值给 max_keys
                max_key = key
            }
        }
        return `${max_key}${max_num}`
    }
    console.log(func(str))

思考题5 单个大写字母排到最后

    /*
    输入:
      let arr = ['A1', 'B1', 'A2', 'B2']
      let str = ['A', 'B']
    输出:   ['A1', 'B1', 'A', 'A2', 'B2', 'B']
    */
    let arr = ['A1', 'A2', 'B1', 'B2']
    let str = ['A', 'B']
    // // 加上一个 Z 可以让当前数据排到最后
    // str = str.map(item => item + 'Z')
    // // localeCompare() 方法返回一个数字来指示一个参考字符串是否在排序顺序前面或之后或与给定字符串相同。
    // let obj = arr.concat(str).sort((a, b) => a.localeCompare(b)).map(item => item.replace('Z', ''))
    // console.log(obj)

    // 循环 str 拿到每一项
    let k = 0
    // 循环两遍
    for (let i = 0; i < str.length; i++) {
        let item1 = str[i]
        for (let j = 0; j < arr.length; j++) {
            let item2 = arr[j]
            if (item2.includes(item1)) {
                // 如果包含就记录一下当前的位置(后面还有包含的会重新记录的这个值)
                k = j
            }
        }
        // 把当前项插入arr到后面
        arr.splice(k + 1, 0, item1)
    }
    console.log(arr)

思考题6要求重新排序为数字在后字母在前

  /*
       @ '14575476a1b2c309lmcsa89908pppkdgvkkllhijkldddefgklmnopqrstuywxyz'
         输出 Abcdefghijklmnopqrstuvwxyz9876543210
    */

    let str = '14575476a1b2c309lmcsa89908pppkdgvkkllhijkldddefgklmnopqrstuywxyz'
    console.log([...new Set(str.replace(/[^a-zA-Z]/g, '').split('').sort().concat([...str.replace(/[^\d]/g, '')].sort().reverse()))].join('').replace(/( |^)[a-z]/g, (L) => L.toUpperCase()))
   

  /*
      @ 有一个字符串‘a1b2c309pppkkk’
        要求重新排序为数字在后字母在前
        且首字母大写,数字需要倒序排列,最后整体字符串去重。
    */

    let a = str.replace(/[^a-zA-Z]/g, '') // 拿到所有的字母
    let b = str.match(/[0-9]/g).reverse().join('') // 拿到所有的数字进行 反转
    console.log([...new Set(a + b)].join('').replace(str[0], str[0].toUpperCase()))

思考题 处理数组的属性名

    let arr = ['数据1', '数据2']
    let obj = [
        [213, 56465],
        [21342, 769],
    ]
/*
  处理为一下
      let ff = [
        { '数据1': 213, '数据2': 56465 },
        { '数据1': 21342, '数据2': 769 },
    ]
*/ 

    function toHash(array, data) {
        // 定义一个空数组 因为 期望数据最层是一个数组
        let reslute = []
        // 循环传进来的需要处理的数据
        data.forEach((item, index) => {
            // 每一次便利都要想这个对象里面添加数据
            let obj = {}
            // 循环data数据里面的每一项每一项是个数组
            item.forEach((val, i) => {
                // obj 对应的属性名对应 array 下标的每一项
                obj[array[i]] = val
            })
            // 根据 data 的下标为每一项添加内层循环处理好的对象
            reslute[index] = obj
        })
        return reslute
    }
    console.log(toHash(arr, obj))

思考题 保留两位小数

    function round(num, decimal) {
        if (isNaN(num)) {
            return 0
        }
        const p1 = Math.pow(10, decimal + 1)
        const p2 = Math.pow(10, decimal)
        return Math.round(num * p1 / 10) / p2
    }
    console.log(round(21.436,2)) // 21.44

思考题 计算总价格的封装 并且保留小数


     /**
     * 取数组的之和,或对象数组中指定属性的之和 并且保留 3 三位小数
     * @method sum
     * @param  {String} attr  对象数组的属性
     * @return {Integer} result 之和
     * @example
     *         let a=[1,2,3,4]
     *         DictArray.$(a).sum();//==>10.
     *         let a=[{age:1,name:"n1"},{age:3,name:"n2"}];
     *         DictArray.$(a).sum("age");//==>4.
     */
    function round(num, decimal) {
        if (isNaN(num)) {
            return 0
        }
        const p1 = Math.pow(10, decimal + 1)
        const p2 = Math.pow(10, decimal)
        return Math.round(num * p1 / 10) / p2
   }
   class DictArray {
        data;
        constructor(data) {
            this.data = data
        }
        sum(attr, accuracy = 2) {
            let res = this.data.filter(item => item != null)
            let ress = attr ? res.reduce((r, item) => r + Number(item[attr]), 0) : res.reduce((r, item) => r += Number(item), 0)
            return round(ress, accuracy)
        }
        static $(arr) {
            let res = new DictArray(arr)
            return res
        }
    }
    let a = [{ age: 1.4444, name: "n1" }, { age: 3.7765, name: "n2" }];
    console.log(DictArray.$(a).sum('age', 3))

写了好多后续更新…

你可能感兴趣的:(JavaScript,javascript,前端)