前端面试中常见的原生JS手写实现函数

前端面试中常见的原生JS手写实现函数

    • 前言
    • 实现
      • call函数
      • apply函数
      • bind函数
      • new
      • instanceof函数
      • 继承实现
      • 深拷贝
      • 深度比较
      • 防抖函数
      • 节流函数
      • Promise.all()
      • Promise.race()
      • 函数柯里化
      • 用setTimeout实现setInterval
      • 原生JS实现Ajax
      • 上拉加载更多
      • 发布订阅模式
      • 数组转换为二叉树
      • sleep函数
      • map函数
      • reduce函数
      • filter函数
      • forEach函数
      • find函数
      • some函数
      • every函数
      • includes函数
      • join函数
      • 用reduce实现map
      • concat函数
      • 快速排序
      • flat函数
      • splice函数
      • isArray函数
      • slice函数
      • 将虚拟DOM转换为真实DOM
      • 字符串解析

前言

这里我总结前端面试中常见的原生JS手写实现函数,对付面试足够了,但是可能还会有露,这就得根据你自己具体的面试来总结了。其中如果有误,欢迎指正,我在这里先感谢各位!!!

实现

call函数

Function.prototype.mycall = function () {
    const args = [...arguments]
    const contex = args.shift() //作用对象
    let fn = Symbol()
    contex[fn] = this //为作用对象添加给定的函数
    let result = contex[fn](...args) //执行作用对象上的函数
    delete contex[fn]
    return result
}

apply函数

Function.prototype.myapply = function () {
    const args = [...arguments]
    const contex = args.shift()
    let fn = Symbol()
    contex[fn] = this
    let result = contex[fn](...args[0])   //不同之处
    delete contex[fn]
    return result
}

bind函数

Function.prototype.mybind = function () {
    const args = [...arguments]
    const thisArg = args.shift()
    const self = this 
    return function () {
        return self.apply(thisArg, args)
    }
}

new

function myNew() {
    const args = [...arguments]
    const father = args.shift()
    let obj = new Object({}) //创建一个新的空对象
    obj.__proto__ = father.prototype //解决原型
    let result = father.call(obj, ...args) //构造函数于新对象环境中执行
    // let result = father.apply(obj, [...args])
    return result ? result : obj
}

instanceof函数

function instance_of(L, R) {
    // 1.如果是基本数据类型,除了null
    const baseType = ['string', 'number', 'boolean', 'undefined', 'symblo', 'BigInt']
    if (baseType.includes(typeof (L))) return false
    // 2.如果时引用类型
    let LP = L.__proto__
    let RP = R.prototype
    while (true) {
        if (LP == null) { //找到最后一个了,__proto__为null(Object.prptptype.__prpto__ = null)
            return false
        }
        if (LP === RP) {
            return true
        }
        LP = LP.__proto__ //该层未找到,继续往上找
    }
}

继承实现

/原型链继承
function Sup (name) {
    this.name = name
}
Sup.prototype.sayName = function () {
    console.log(this.name)
}
function sub(name, age) {
    this.name = name
    this.age = age
}
sub.prototype = new Sup()//要点:继承原型链
sub.prototype.satAge = function () {
    console.log(this.age)
}

//sub.prototype = new sup()
sub.prototype.sayAge = function () {
    console.log(this.age)
}
//盗用构造函数继承或经典继承或对象伪装
function Sup (name) {
    this.name = name
}
Sup.prototype.sayName = function () {
    console.log(this.name)
}
function Sub (name, age) {
    Sup.call(this, name)//要点:在子类中执行父类的构造函数并继承其构造函数中的方法,而且向父类传递参数name
    this.age = age
}

//组合继承或伪经典继承
function SuperType(name) {
    this.name = name
}
SuperType.prototype.sayName = function () {
    console.log(this.name)
}

function SubType(name, age) {
    SuperType.call(this, name)   //要点1
    // SuperType.apply(this, name)   //报错,apply第二个参数是Array的实例,或者arguments对象。
    this.age = age
}

SubType.prototype = new SupType()//要点2
SubType.prototype.sayAge = function () {
    console.log(this.age)
}
var instance = new SubType("Alo", 18)

//原型式继承
function object (o) {
    function F() {}
    f.prototype = o
    return new F()
}

//寄生式继承
function createAnother (obj) {
    let clone = object(o)
    clone.sayHi = function () {
        console.log('hi')
    }
    return clone
}

//寄生式组合继承
function createother (Sub, Sup) {
    let prototype = object(Sub, Sup)
    prototype.construct = sub
    sub.prototype = prototype
}

function SuperType(name) {
    this.name = name
}
SuperType.prototype.sayName = function () {
    console.log(this.name)
}

function SubType(name, age) {
    SuperType.call(this, name)   //要点1
    // SuperType.apply(this, name)   //报错,apply第二个参数是Array的实例,或者arguments对象。
    this.age = age
}

createother(SubType, SupType)//要点2,这里于组合继承不一样
SubType.prototype.sayAge = function () {
    console.log(this.age)
}

深拷贝

function deepClone (obj) {
    let objclone = Array.isArray(obj) ? [] : {}//最后复制的对象或数组
    if (obj && typeof obj === 'object') {
        for (let key of obj) {//遍历obj
            if (obj.hasOwnProperty(key)) {//确保是对象自身的属性而非原型链继承的
                if (obj[key] && typeof obj[key] === "object") {
                    objClone[key] = deepClone(obj[key]);
                } else {
                    objClone[key] = obj[key];
                }
            }
        }
    } else if (obj instanceof Array) {
        return obj.map(item => deepClone(item))
    } else (obj instanceof Function) {
    return obj
    return objclone
}

//另一种实现方法
function deepClone(obj) {
    let cloneObj = JSON.stringify(obj)
    cloneObj = JSON.parse(cloneObj)
    return cloneObj
}

深度比较

function isObject(obj) {
        return typeof obj === 'object' && obj != null
    }

    function deepCompare(obj1, obj2) {
        // 1.有一个或两个都不是对象
        if (!isObject(obj1) || !isObject(obj2)) {
            return obj1 === obj2
        }
        // 2.同一个对象
        if (obj1 === obj2) {
            return true
        }
        // 3.不是同一个对象
        const obj1Keys = Object.keys(obj1)
        const obj2Keys = Object.keys(obj2)
        // 3.1键数量不一样
        if (obj1Keys.length != obj2Keys.length) {
            return false
        }
        // 3.2键数量一样
        for (let key in obj1) {
            const res = deepCompare(obj1[key], obj2[key])
            if (!res) {
                return false
            }
        }
        return true
    }

防抖函数

function debounce(fn, delay = 500) {
    let timeout = null
    return function () {
        let contex = this
        if (timeout) {
            clearInterval(timeout)
        }
        timeout = setTimeout(function () {
            fn.apply(contex)
        }, delay)
    }
}

节流函数

两种方式

function throttle(fn, wait = 500) {//定时器实现
    let timer = null
    return function () {
        const context = this
        const arg = arguments
        if (!timer) {     //即timer不为null的时候一直没操作
            timer = setTimeout(() => {
                timer = null
                fn.apply(context, arg)
            }, wait)
        }
    }
}

function throttle1(fn, wait = 500) {//时间戳实现
    let previous = 0
    return function () {
        const contex = this
        const arg = arguments
        let now = Date.now()
        if (now - previous > wait) {
            fn.apply(contex, arg)
            previous = now
        }
    }
}

Promise.all()

Promise.all = function (promises) {
        const values = new Array(promises.length) //保存成功的结果
        let resolvedCount = 0; // 保存成功promise的数量
        return new Promise((resolve, reject) => {
            // 遍历promises获取每个promise的结果
            promises.forEach((p, index) => {
                // p.then(value => {   //需要防止p不是promise
                Promise.resolve(p).then(value => {
                    resolvedCount++
                    values[index] = value
                    // 如果全部成功了,返回一个promise
                    if (resolvedCount === promises.length) {
                        resolve(values) //只发生一次
                    }
                }, reason => {
                    reject(reason) //只发生一次
                })
            })
        })
    }

Promise.race()

Promise.race = function (promises) {
        return new Promise((resolve, reject) => {
            // 遍历promise获取每个promise的结果
            // 遍历promises获取每个promise的结果
            promises.forEach((p, index) => {
                // p.then(value => {   //需要防止p不是promise
                Promise.resolve(p).then(value => {
                    resolve(value) //只发生一次,谁先成功就返回该成功值
                }, reason => {
                    reject(reason) //只发生一次 谁先失败就返回失败值
                })
            })
        })
    }

函数柯里化

function add() {
    let res = [...arguments].reduce((a, b) => a + b)
    let temp = function () {
        if (arguments.length) {
            res += [...arguments].reduce((a, b) => a + b) // 累加
            return temp
        } else {
            return res
        }
    }
    temp.toString = function () { // 重写toSting() 方法
        return res;
    }
    return temp
}

用setTimeout实现setInterval

var num = 0
var max = 10
function incrementNumber() {
    num++
    // 如果执行次数未达到max设定的值,则设置另一次超时调用
    if (num < max) {
        console.log(num)
        setTimeout(incrementNumber, 1000)
    } else {
        alert('done')
    }
}
setTimeout(incrementNumber, 1000)

原生JS实现Ajax

var Ajax = {
    get: function(url, fn) {
        //XMLHttpRequest对象用于在后台与服务器交换数据
        var xhr = new XMLHttpRequest()
        xhr.open('GET', url, true)
        xhr.onreadystatechange = function() {
            //readyState == 4说明请求已完成
            if (xhr.readyState == 4 && xhr.status == 200 || xhr.status == 304) {
                //从服务器获得数据
                fn.call(this, xhr.resposeText)
            }
        }
        xhr.send()
    },
    //data应为'a=a1&b=b1'这种字符串格式,在jq里,如果data为对象会自动将对象转换为这种字符串
    post: function(url, data, fn) {
        var xhr = new XMLHttpRequest()
        xhr.open("POST", url, true)
        //添加http头,发送信息至服务器时内容编码类型
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4 && xhr.status == 200 || xhr.status == 304) {
                //从服务器获得数据
                fn.call(this, xhr.resposeText)
            }
        }
        xhr.send(data)
    }
}

上拉加载更多

window.onscroll = function() {
    //文档内容实际高度(包括超出视窗的溢出部分)
    var scrollHeight = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight)
    //滚动条的滚动距离
    var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
    //窗口可视范围高度
    var clientHeight = window.innerHeight || Math.min(document.documentElement.clientHeight, document.body.clientHeight)
    if(clientHeight + scrollTop >= scrollHeight){
        console.log("===加载更多内容……===");
    }
}

发布订阅模式

//发布订阅中心,on-订阅,off取消,emit发布,内部需要一个单独事件中心caches进行存储
    interface CacheProps {//单独的事件中心
        [key: string]: Array <((data ? : unknow) => void)>
    }
    class Observe {
        private caches: CacheProps = {}//事件中心
        on (eventName: string, fn: (data ? : unknow) => void) {// eventName事件名-独一无二, fn订阅后执行的自定义行为
            this.caches[eventName] = this.caches[eventName] || []
            this.caches[eventName].push(fn)
        }
        emit (eventName: string, data ? : unknow) {// 发布 => 将订阅的事件进行统一执行
            if (this.caches[eventName]) {
                this.caches[eventName].forEach((fn: (data ? : unknow) => void) => fn(data))
            }
        }
        off (eventName: string, fn: (data ? : unknow) => void) {
            if (this.caches[eventName]) {
                const newCaches = fn ? this.caches[eventName].filter(e => e !== fn) : []
                this.caches[eventName] = newCaches
            }
        }
    }

数组转换为二叉树

function arratTotree (arr) {
        let tem = {}//创建临时对象
        let tree = {}//转换后的树
        //先遍历数组,将数组中每一项推入tem中
        for (let i of arr) {
            tem[arr[i][id]] = arr[i]
        }
        //遍历tem,将当前字节点与父节点建立连接
        for (let i of tem) {
            if (Array.isArray(i)) {
                arr2Tree(i)
            } else if (tem[i][parentid] !== 'root') {//父节点不是根节点
                if (!tem[tem[i][parentid]].children) {//若父节点的孩子节点为空
                    tem[tem[i][parentid]].children = new Array()//直接为其创建一个孩子节点数组
                }
                tem[tem[i][parentid]].children.push(tem[i])//并且将当前节点推入父节点的孩子节点数组中
            } else {
                tree[tem[i][id]] = tem[i]
            }
        }
    }
    return tree

sleep函数

//方法1
    function sleep (time) {
        return new Promise(resolve => setTimeout(time))
    }
    const t1 = +new Date()
    sleep(300).then(() => {
        const t2 = +new Date()
        console.log(t2 - t1)
    })
    
    //方法2
    async function test() {
        for (var i = 0; i < 10; i++) {
            await sleep(1000);
    
            console.log(i);
        }
    }
    function sleep(delay) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                try {
                    resolve(1)
                } catch (e) {
                    reject(0)
                }
            }, delay);
        })
    }
    test()

map函数

Array.prototype.myMap = function (fn, context) {
    let arr = this
    let res = []
    for (let i = 0; i < arr.length; i ++) {
        res.push(fn.call(context, arr[i], i, arr))
    }
    return res
}

reduce函数

Array.prototype.myReduce = function (fn, base) {
    let initarr = this
    let arr = initarr.concat()
    if (base) arr.unshift(base)
    let index, newValue
    while (arr.length > 1) {
        index = initarr.length - arr.length + 1
        newValue = fn.call(null, arr[0], arr[1], index, initarr)
        arr.splice(0, 2, newValue)
    }
    return newValue
}

filter函数

Array.prototype.myfilter =  function (fn, context) {
    let arr = this
    let tem = []
    for (let i = 0; i < arr.length; i ++) {
        let res = fn.call(context, arr[i], i, arr)
        if (res) tem.push(arr[i])
    }
    return tem
}

forEach函数

Array.prototype.myforEach = function (fn, context) {
    let arr = this
    for (let i = 0; i < arr.length; i ++) {
        fn.call(context, arr[i], i, arr)
    }
}

find函数

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

some函数

Array.prototype.mysome = function (fn, context) {
    let arr = this
    for (let i = 0; i < arr.length; i ++) {
        let res = fn.call(context, arr[i], i, arr)
        if (res) return true
    }
    return false
}

every函数

Array.prototype.myevery = function (fn, context) {
    let arr = this
    for (let i = 0; i < arr.length; i ++) {
        let res = fn.call(context, arr[i], i, arr)
        if (!res) return false
    }
    return true
}

includes函数

Array.prototype.myincludes = function (val, index) {
    let arr = this
    let k = Math.max(index >= 0 ? index : arr.length - Math.abs(index), 0)
    for (; k < arr.length; k ++) {
        if (arr[k] === val) {
            return true
        }
    }
    return false
}

join函数

Array.prototype.myjoin = function (connector) {
    let arr = this
    let str = ''
    for (let i = 0; i < arr.length; i ++) {
        if (i == arr.length - 1) str += arr[i]
        else {
            str += arr[i] + connector.toString()
        }
    }
    return str
}

用reduce实现map

Array.prototype.mapbyreduce = function (fn, context) {
    let arr = this
    return arr.reduce((pre, cur, index, arr) => {
        let res = fn.call(context, cur, index, arr)
        return [...pre, res]
    }, [])
}

concat函数

Array.prototype.myconcat = function () {
        let res = JSON.parse(JSON.stringify(this))
        for (let i = 0; i < arguments.length; i ++) {
            let arg = arguments[i]
            for (let j = 0; j < arg.length; j ++) {
                res.push(arg[j])
            }
        }
        return res
    }

快速排序

function quick (arr) {
        if (arr.length <= 1) return arr
        let pivotindex = Math.floor(arr.length / 2)
        let pivot = arr.splice(pivotindex, 1)
        let left = []
        let right = []
        for (let i = 0; i < arr.length; i ++) {
            if (arr[i] < pivot) {
                left.push(arr[i])
            } else {
                right.push(arr[i])
            }
        }
        return quick(left).concat(pivot, quick(right))
    }

flat函数

Array.prototype.myflat = function (count) {
        let arr = this
        if (count == 0) return arr
        return arr.reduce((res, value)=> {
            if (Array.isArray(value)) {
                res = res.concat(myflat(count - 1))
            } else {
                res = res.concat(value)
            }
            return res
        }, [])
    }

splice函数

Array.prototype.mysplice = function () {
        let arr = this
        let index = arguments[0]//第一个参数
        let num = arguments[1]//第二个参数
        let len = arguments.length
        let content  = []
        if (len == 2) {//仅有两个参数,则splice函数用于删除元素
            return arr.slice(0, index).concat(arr.slice(index + num, arr.length))
        } else if (len > 2) {
            for (let i = 2; i < len; i ++) {//将其余参数放入数组content中
                content.push(arguments[i])
            }
            if (num == 0) {//有2个以上的参数,且第二个参数为0时,则splice函数为插入元素
                return arr.slice(0, index).concat(content, arr.slice(index, arr.length))
            } else if (num > 0) {//有2个以上的参数,且第二个参数不为0时,则splice函数为替换元素
                return arr.slice(0, index).concat(content, arr.slice(index + num, arr.length))
            }
        } else {
            return arr
        }
    }

isArray函数

Array.prototype.isArray = function (obj) {
        return Object.prototype.toString.call(obj) === '[object Array]'
    }

slice函数

Array.prototype.myslice = function (start, end) {
        let arr = this
        start = start === undefined ? 0 : start
        end = end === undefined ? arr.kength : end
        let res = []
        for (let i = start; i < end; i ++) {
            res.push(arr[i])
        }
        return res
    }

将虚拟DOM转换为真实DOM

function render(vnode, container) {
        return container.appendChild(_render(vnode))
    }
    function _render (vnode) {
        if (typeof vnode === 'number') {
            vnode = String(vnode)
        }
        if (typeof vnode === 'string') {
            return document.createTextNode(vnode)
        }
        let dom = document.createElement(vnode.tag)
        if (vnode.attrs) {
            Object.keys(vnode.attrs).forEach(key => {
                let value = vnode.attrs[key]
                dom.setAttribute(key, value)
            })
        }
        vnode.children.forEach(child => _render(child))
        return dom
    }
    function reder(vnode, con) {
        return con.appendChild(_render(vnode))
    }

字符串解析

function parse (str, obj) {
        let res = ''
        let flag = false
        let start
        for (let i = 0; i < str.length; i ++) {
            if (str[i] === '{') {
                flag = true
                start = i + 1
                continue
            }
            if (!flag) {
                res += str[i]
            } else {
                if (str[i] === '}') {
                    flag = false
                    res += match(str.slice(start, i), obj)
                }
            }
        }
    }
    function match (str, obj) {
        let keys = obj.split(".").slice(1)
        let index
        let o = obj
        while (index < keys.length) {
            let key = keys[index]
            if (!o[key]) {
                return `{${str}}`
            } else {
                o = o[key]
            }
        }
        return o
    }

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