前端笔试干货

258 阅读2分钟

一. 实现一个九九乘法表

实现: 用两层 for 循环就可实现

for(let i=1; i<10; i++) {
    for(let j=1; j<i + 1; j++) {
        document.write(`${j} * ${i} = ${i*j}&nbsp;&nbsp;&nbsp;&nbsp;`)
    }
    document.write(`</br>`)
}

结果

image.png

二. 找出1000以内的水仙花数

水仙花数: 水仙花数是指一个 n 位数 ( n≥3 ), 如果一个 n 位正整数等于其各位数字的 n 次方之和, 例如1^3 + 5^3 + 3^3 = 153

实现: 使用 % 取余, 使用 parseInt() 取整, 就可分别获取个、十、百位的数字, 然后做判断即可.

for(let i=100; i<1000; i++) {
    let ones = parseInt(i%10)
    let tens = parseInt((i/10)%10)
    let hundreds = parseInt((i/100)%10)
    if(ones*ones*ones + tens*tens*tens + hundreds*hundreds*hundreds == i) {
        console.log(`水仙花数: ${i}`)
    }
}

结果

image.png

三. 找出一串字符串/数组出现最多的字符

实现: 首先判断是字符串还是数组, 字符串就转成数组, 然后将数据以对象的key为数组的值, 对象的值为出现的次数的方式存储, 然后循环这个对象就可得到结果

function findCharacter(str) {
    if(!str.length) return
    // 判断是不是数组, 不是的话将字符串转成数组
    let arr = Array.isArray(str) ? str : str.split('')

    // 分别定义空对象, 最多出现值和最大次数
    let data = {}
    let maxNumber, maxTimes = 0

    // 将数据和数据出现次数存放在对象中
    arr.forEach(el => {
        data[el] ? data[el]++ : data[el] = 1
    })

    // 最多出现值和出现最大次数
    for(let num in data) {
        if(data[num] > maxTimes) {
            maxTimes = data[num]
            maxNumber = num
        }
    }
    return `重复次数最多的值是: ${maxNumber}, 一共出现了: ${maxTimes}次`
}

四. 模拟实现call,apply

  • 模拟实现call 实现: 注意两点, 一是call改变了this的指向, 指向了data, 二是执行了test函数. 最后注意this可以为null.
Function.prototype.simulateCall = function(context, ...args) {
    // 当context为null时
    let ctx = context || window
    ctx.fn = this

    const result = ctx.fn(...args)
    delete context
    return result
}

// 测试
var name = '全局磊'

let data = {
    name: '局部磊'
};
function test(age,job) {
    let name = this.name
    return { name, age, job }
}

test.simulateCall(data, '18', '前端小白') // {name: "局部磊", age: "18", job: "前端小白"}
test.simulateCall(null, '18', '前端小白') // {name: "全局磊", age: "18", job: "前端小白"}

test.call(data, '18', '前端小白') // {name: "局部磊", age: "18", job: "前端小白"}
test.call(null, '18', '前端小白') // {name: "全局磊", age: "18", job: "前端小白"}
  • 模拟实现apply 实现: 原理和call一样, 传值不同而已
Function.prototype.simulateApply = function (context, ...args){
    // 当context为null时
    let ctx = context || window
    ctx.fn = this

    const result = ctx.fn(...args)
    delete context
    return result
}

五. 模拟实现bind

实现: 模拟实现bind和模拟实现call和apply是不一样的, 因为bind改变this指向之后, 需要再执行一下函数.

Function.prototype.simulateBind = function(context, ...args) {
    let self = this;
    let args = Array.prototype.slice.call(arguments, 1);

    let blankFn = function () {};

    let boundFn = function () {
        let bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof blankFn ? this : context, args.concat(bindArgs));
    }

    blankFn.prototype = this.prototype;
    boundFn.prototype = new blankFn();
    return boundFn;
}

六. 手写防抖函数

实现: 防抖的原理是当持续触发事件时,一定时间内没有再触发事件,事件处理函数才会执行一次,在设定时间内,有一次触发事件,就重新计算时间.

function debounce(fn, time){
    var timeout = null
    return function () {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
            fn()
        }, time)
    }  
  }

七. 手写节流函数

实现: 节流的原理是当持续触发事件时,保证在一定时间内只调用一次事件处理函数,相当于定时器.

function throttle(fn, time){
    var execute = true 
    return function () {
        if(!execute) return
        execute = false
        setTimeout(() => {
            fn()
            execute = true 
        }, time)
    }
}

优秀文献参考