八股文,前端手写题

458 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

01.实现一个new

实现步骤:

  1. 创建一个新的对象
  2. 让这个对象的隐式原型 (__proto__)指向显示原型(prototype)
  3. 改变构造函数的this指向,让构造函数的this指向新创建的对象
  4. 执行构造函数
  5. 如果构造函数中没有返回对象,则返回上面创建出来的对象
function MyNew(fn, ...args) {
    // 创建一个新对象
    let obj = {}
    // 让这个对象的隐式原型 (__proto__)指向显示原型(prototype)
    obj.__proto__ = fn.prototype
    // 改变构造函数的this指向,让构造函数的this指向新创建的对象,
    // 执行构造函数
    fn.apply(obj, args)
    // 如果构造函数中没有返回对象,则返回上面创建出来的对象
    return obj
}

function Person(name, age) {
    this.name = name
    this.age = age
}

let p = new Person('小明', 10)

let p1 = MyNew(Person, '二明', 20)

console.log(p)
console.log(p1)

02.Js实现继承

function Person(name, age) {
    this.name = name
    this.age = age
}

Person.prototype.eat = function () {
    console.log('我会吃饭');
}

function Student(name, age, className) {
    Person.call(this, name, age)
    this.className = className
}

// 继承父类的共有属性
// 子类的 显示原型指向父类的 显示原型
Student.prototype = Object.create(Person.prototype)
// 并且原型的构造函数指向自己
Student.prototype.constructor = Student

let s = new Student('小明', 10, '六年级')

console.log(s.name)
console.log(s.age)
console.log(s.className)
s.eat()

image.png

03.实现instanceof

instanceof 的作用,就是判断一个对象是否属于某个

接上面的继承而来的移到题目

console.log(s instanceof Person)
console.log(s instanceof Student)

实现:

function MyInstanceof(left, right) {
    // 就是让左边的对象实例去找 右边的构造函数的原型

    let l = left.__proto__;
    let r = right.prototype

    // 如果从对象的原型向上找不为 null ,就一直向上找
    while (l) {
        if (l === r) return true
        l = l.__proto__
    }

    return false
}

console.log(MyInstanceof(s, Student))
console.log(MyInstanceof(s, Person))

04.实现call

call的作用

  1. 指定函数内部的this
  2. 让函数执行
Function.prototype.MyCall = function (context) {
    // 参数检测
    context = context ? Object(context) : window
    // 拿到要执行的函数 ,其实就是原来的函数  fn.MyCall() 
    // 谁调用的MyCall谁就是 this
    // 给 第一个参数添加一个属性,等于正在调用的函数, 
    // 收集参数
    let args = []
    for (let i = 1; i < arguments.length; i++) {
        args.push(arguments[i])
    }
    context.fn = this
    let ret = context.fn(...args)
    delete context.fn
    return ret
}


let obj = {
    name: '小明'
}

function fn(a, b) {
    console.log(this)
    return a + b
}
console.log(fn.MyCall(obj, 1, 2))

05.实现apply

作用:

  1. 也是改变this指向,只不过传递的参数是一个数组

实现:

Function.prototype.MyApply = function (context, args) {
    // 参数检测
    context = context ? Object(context) : window
    // 拿到要执行的函数 ,其实就是原来的函数  fn.MyApply() 
    // 谁调用的谁就是 this
    // 给 第一个参数添加一个属性,等于正在调用的函数
    context.fn = this
    if (!args) {
        return context.fn()
    }
    let ret = context.fn(...args)
    delete context.fn
    return ret
}


let obj = {
    name: '小明'
}

function fn(a, b) {
    console.log(this)
    return a + b
}
console.log(fn.MyApply(obj, [1, 2]))

06.实现bind

功能:

  1. 改变函数的this指向
  2. 返回出一个新的函数

代码实现:

Function.prototype.MyBind = function (context) {
    context = context ? Object(context) : window
    // 取外层函数的参数
    let outArgs = Array.prototype.slice.call(arguments, 1)
    let that = this  // 保存外部函数
    function res() {
        // 收集内层参数
        let innerArgs = Array.prototype.slice.call(arguments)
        // 执行原函数
        return that.apply(context, outArgs.concat(innerArgs))
    }
    return res
}

let obj = {
    name: '小明'
}

function fn(a, b) {
    console.log(this)
    return a + b
}

// 返回一个新的函数
let res = fn.MyBind(obj, 1, 2)

console.log(res())

07.实现map方法

功能:

  1. 遍历数组,接受一个函数。
Array.prototype.MyMap = function (fn) {
    // 保存结果
    let ret = []
    // this 就是当前调用的数组
    let arr = this
    // 遍历数组
    for (let i = 0; i < arr.length; i++) {
        // 拿到 fn的返回结果,放到最终的结果里面
        ret.push(fn(arr[i], i, arr))
    }
    return ret
}

let arr = [1, 2, 3]

console.log(arr.MyMap((item) => item * 2))

08.实现find函数

Array.prototype.MyFind = function (fn) {
    let arr = this
    for (let i = 0; i < arr.length; i++) {
        if (fn(arr[i], i, arr)) {
            return arr[i]
        }
    }
}


// 找到符合条件的第一个元素
console.log([1, 2, 3].MyFind(item => item > 1))

09.实现filter函数

// 返回一个新数组
// 找到符合条件的元素放到新数组里面
Array.prototype.MyFilter = function (fn) {
    let ret = []
    let arr = this
    for (let i = 0; i < arr.length; i++) {
        if (fn(arr[i], i, arr)) {
            ret.push(arr[i])
        }
    }
    return ret
}


console.log([1, 2, 3, 4].MyFilter(item => item > 1));

10.实现some函数

// 如果有一个符合条件,就返回 true 
// 否则返回 false
Array.prototype.MySome = function (fn) {
    let arr = this
    for (let i = 0; i < arr.length; i++) {
        if (fn(arr[i], i, arr)) {
            return true
        }
    }
    return false
}


console.log([1, 2, 3].MySome(item => item > 100));

11.实现every方法

// 所有条件都满足,才返回 true
Array.prototype.MyEvery = function (fn) {
    let arr = this
    for (let i = 0; i < arr.length; i++) {
        if (!fn(arr[i], i, arr)) {
            return false
        }
    }
    return true
}


console.log([1, 2, 3, 4].MyEvery(item => item > 10));

12.reduce方法

Array.prototype.MyReduce = function (fn, initialValue) {
    // 拿到数组
    let arr = this
    // 先检测有没有传初始值
    let val = initialValue ? initialValue : this[0]

    // 计算 循环的初始位置
    let index = initialValue ? 0 : 1

    for (let i = index; i < arr.length; i++) {
        // 将 fn 函数计算的结果重新赋值给 val, 
        // 当做下一次调用 fn 函数的第一个参数
        val = fn(val, arr[i], index, arr)
    }
    return val
}

console.log([1, 2, 3, 4, 5].MyReduce((acc, curr) => {
    return acc += curr
}, 5))

13.实现findIndex方法

找到满足条件的元素的 索引

Array.prototype.MyFindIndex = function (fn) {
    let arr = this
    for (let i = 0; i < arr.length; i++) {
        if (fn(arr[i], i, arr)) {
            return i
        }
    }
    return -1
}

console.log([1, 2, 3].MyFindIndex(item => item === 2))

14.实现concat方法

Array.prototype.MyConcat = function (...args) {
    console.log(args)
    // 返回一个新的数组
    let ret = []
    // 取出原数组
    let arr = this

    if (!args) return [...arr]

    // 遍历 args 
    for (let i = 0; i < args.length; i++) {
        if (Array.isArray(args[i])) {
            arr.push(...args[i])
        } else {
            arr.push(arr[i])
        }
    }
    ret = [...arr]
    // 返回一个新数组
    return ret
}

let x = [1, 2, 3].MyConcat([4], [5])

console.log(x)

15.实现防抖函数

  1. 每次执行fn函数的时候把上一次的定时器清空

image.png

let input = document.querySelector('input')


// 调用形式,返回的是一个函数
function MyDebounce(fn, delay) {
    let timer = null
    let _debounce = function () {
        // 在每次调用 debounce 函数之前,先清空上一次的定时器
        clearTimeout(timer)
        timer = setTimeout(() => {
            fn()
            timer = null
        }, delay);
    }
    return _debounce
}


input.oninput = MyDebounce(function () {
    console.log('hihiihi');
}, 1000)

16.实现节流函数

  1. 无论函数调用的频率有多快,但是它真实执行的频率是一定的

节流函数原理:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效

例:(连续不断动都需要调用时用,设一时间间隔),像dom的拖拽,如果用消抖的话,就会出现卡顿的感觉,因为只在停止的时候执行了一次,这个时候就应该用节流,在一定时间内多次执行,会流畅很多

function MyThrottle(fn, wait) {
    let lastTime = 0
    let _throttle = function () {
        // 当前时间
        let now = new Date().getTime()
        // 如果当前时间 - 上一次执行时间的差值 大于设置的等待时间就执行函数
        if (now - lastTime > wait) {
            lastTime = now
            fn()
        }
    }
    return _throttle
}

let input = document.querySelector('input')
input.oninput = MyThrottle(function () {
    console.log('hiihi')
}, 1000)