• JavaScript高频手写面试题


    1.手写instanceof

    2.实现数组的map方法

    3.使用数组内置的方法reduce方法实现map方法

    4.手写reduce方法

     5.数组扁平化

    6.深拷贝

    7.浅拷贝

    8.手动实现call,apply,bind

    9.手写new

    10.手写primose

    11.手写ajax

    12.手写防抖

    13.手写节流

    14.手写promise加载图片




    1.手写instanceof:

    instanceof在查找的过程中会遍历左边的原型链,直到找到右边变量的prototype,如果查找失败就返回false
                // instanceof的作用是判断当前目标对象是否是指定的类型
                // 解决思路是:instanceof在查找的过程中会遍历左边的原型链,直到找到右边变量的prototype,如果查找失败就返回false
                let myInstanceof = (target, origin) => {
                    while (target) {
                        if (target.__proto__ === origin.prototype) {
                            return true
                        }
                        target = target.__proto__
                    }
                    return false
                }
    
                let arr = [2, 3, 5]
                console.log(myInstanceof(arr, Array)) // true
                console.log(myInstanceof(arr, Object)) // true

    2.实现数组的map方法

            <script>
                // map() 方法返回一个新数组,新数组中的元素为原始数组中的每个元素调用函数处理后得到的值。
                Array.prototype.myMap = function (fn, context) {
                    let res = [] // 用来保存返回的新数组
                    context = context || []
                    let arr = this //由于方法是添加到数组原型上的,所以谁调用这个方法this就指向哪个数组
                    for (let i = 0; i < arr.length; i++) {
                        res.push(fn(arr[i]))
                    }
                    return res
                }
    
                let arr = [1, 2, 3]
                let res = arr.myMap((item) => item * 2)
                console.log(res)
            </script>

     3.使用数组内置的方法reduce方法实现map方法:

                Array.prototype.myMap = function (fn, thisValue) {
                    var res = []
                    thisValue = thisValue || []
                    //   pre:累加的值,cur:当前数组的值,index:当前的值的索引,arr:当前的数组
                    // this指的是当前调用这个方法是数组
                    this.reduce(function (pre, cur, index, arr) {
                        return res.push(fn.call(thisValue, cur, index, arr))
                    }, [])
                    return res
                }
                var arr = [2, 3, 1, 4]
                var res = arr.myMap(function (item, index, arr) {
                    return item + 1
                })
                console.log(res) // 3,4,2,5

     4.手写reduce方法:

        <body>
            <!-- 
                reduce方法的使用,
                参数1传递一个函数作为参数,函数中有四个参数:
                total:累加项的和
                currValue:当前循环的项
                currIndex:当前循环项的索引
                arr:当前的数组
    
                参数2是一个初始值:如果传递了值,它就会作为total的初始值,arr的第一项就会作为currvalue的值
                如果没有传递值:arr的第一项就是作为total的值,第二项就会作为currvalue的值
                
             -->
            <!-- <script>
                var arr = [1, 2, 3, 4]
                var i = 0
                var y = 0
                const res1 = arr.reduce(function (total, currValue, currIndex, arr) {
                    console.log(i)
                    i++
                    return total + currValue
                })
                const res2 = arr.reduce(function (total, currValue, currIndex, arr) {
                    console.log(y)
                    y++
                    return total + currValue
                }, 10)
                console.log(res1) // 10
                console.log(res2) // 20
            </script> -->
    
            <script>
                //实现reduce方法
                //如果有initValue,那么total的值就是initValue,currValue的值就是arr的第一项
                //如果没有initValue,那么total的值就是arr的第一项
                var arr = [1, 2, 3, 4]
                Array.prototype.myReduce = function (fn, initValue) {
                    var i = 0
                    // 1.先判断initValue的值是否存在
                    if (
                        Object.prototype.toString.call(initValue) === '[object Undefined]'
                    ) {
                        // 如果不存在,initvalue的值就是初始值
                        initValue = this[0]
                        i = 1
                        console.log('false')
                    }
    
                    for (i; i < this.length; i++) {
                        initValue = fn(initValue, this[i], i, this)
                    }
                    return initValue
                }
    
                const res = arr.myReduce(function (total, currValue, currIndex, arr) {
                    return total + currValue
                }, 10)
                console.log(res)
            </script>
        </body>

     5.数组扁平化

        <body>
            数组扁平化就是将多维数组转为一维数组
            <!-- <script>
                // 方法一:使用flat,这是es6新增方法方法,flat方法的参数是需要转换的维度
                var arr = [1, 3, 4, [3, [3, 5, [6, 1, 8]]]] //这是四维数组
                console.log(arr.flat())
                console.log(arr.flat(Infinity)) // 如果不知到数组的维度可以使用Infinity
            </script> -->
    
            <!-- 利用concat实现数组扁平化,不能和flat一样指定维度,concat可以链接两个数组或者多个数组-->
    
            <script>
                var arr = [1, 3, 4, [3, [3, 5, [6, 1, 8]]]] //这是四维数组
                function myFlat(arr) {
                    let res = []
                    for (let i = 0; i < arr.length; i++) {
                        if (Array.isArray(arr[i])) {
                            res = res.concat(myFlat(arr[i]))
                        } else {
                            res.push(arr[i])
                        }
                    }
    
                    return res
                }
                console.log(myFlat(arr))
            </script>
        </body>

     6.深拷贝:

    (1)使用递归实现深拷贝

            <script>
                //创建一个引用类型的数据
                var oldObj = {
                    name: 'zs',
                    happy: ['吃饭', '睡', '打豆豆'],
                    parent: {
                        father: 'li',
                        mather: 'wang'
                    }
                }
                //创建一个新对象
                var newObject = {}
    
                //创建一个函数用于实现深拷贝,主要使用了递归
                function cloneDeep(target, source) {
                    //先循环获取每一项
                    for (let i in source) {
                        let item = source[i]
                        //判断每一项中的数据是否是数组
                        if (item instanceof Array) {
                            target[i] = []
                            cloneDeep(target[i], item)
                        } else if (item instanceof Object) {
                            //如果当前项是对象,就返回
                            target[i] = {}
                            cloneDeep(target[i], item)
                        } else {
                            target[i] = item
                        }
                    }
                    return target
                }
                cloneDeep(newObject, oldObj)
    
                oldObj.name = '李四'
                console.log(oldObj)
                console.log(newObject)
            </script>

    (2)使用Object.create()方法实现深拷贝

            <script>
                //创建一个引用类型的数据
                // var oldObj = {
                //     name: "zs",
                //     happy: ['吃饭', '睡', '打豆豆'],
                //     parent: {
                //         father: 'li',
                //         mather: 'wang'
                //     }
                // };
    
                // //使用方法获取一个拷贝来的方法
                // var newObj = Object.create(oldObj);
                // console.log(newObj);
                // console.log(newObj.__proto__);
    
                //注意使用该方法实现的深拷贝它的属性是放在它的原型上的,这些属性不是newObj自己拥有的
    
                // object.create()方法的实现原理:利用了原型是继承
                function create2(o) {
                    function NewObj() {}
                    NewObj.prototype = o
                    return new NewObj()
                }
    
                //创建一个引用类型的数据
                var oldObj = {
                    name: 'zs',
                    happy: ['吃饭', '睡', '打豆豆'],
                    parent: {
                        father: 'li',
                        mather: 'wang'
                    }
                }
    
                //使用方法获取一个拷贝来的方法
                var newObj = create2(oldObj)
                console.log(newObj)
                console.log(newObj.__proto__)
            </script>

     (3)使用jquery的extend方法实现深拷贝

        <script src="./query.js"></script>
        <script>
            //创建一个引用类型的数据
            var oldObj = {
                name: "zs",
                happy: ['吃饭', '睡', '打豆豆'],
                parent: {
                    father: 'li',
                    mather: 'wang'
                }
            };
            var newObj = $.extend({}, oldObj);
            console.log(newObj);
    
            extend方法的使用方法
            // extend(obj1,obj2,obj3):她将obj2,obj3的值复制给obj1,如果有重复的属性就覆盖她
        </script>

    (4)使用JSON.parse和JSON.stringify方法实现深拷贝

        <script>
            //创建一个引用类型的数据
            var oldObj = {
                name: "zs",
                happy: ['吃饭', '睡', '打豆豆'],
                parent: {
                    father: 'li',
                    mather: 'wang'
                }
            };
    
            var newObj = JSON.parse(JSON.stringify(oldObj));
            newObj.name = "李四";
            console.log(oldObj);
            console.log(newObj)
        </script>

    7.浅拷贝

    (1)使用扩展运算符

        <!-- 什么是扩展运算符?
            扩展运算符就是三个点,扩展运算符的作用就是将数据或者类数组对象转为用逗号隔开的值
        -->
        <script>
            var arr = [1, [7, [9]], {
                a: "1"
            }, function() {}, null, undefined, NaN];
    
    
            var result = [...arr];
            console.log(result);
            //修改源对象中的某个值
            arr[2].a = "222";
            console.log(arr);
            console.log(result);
        </script>

    (2)使用assgin方法

        <script>
            var arr = [1, [7, [9]], {
                a: "1"
            }, function() {}, null, undefined, NaN];
    
    
            var result = Object.assign(arr);
            console.log(result);
            //修改源对象中的某个值
            arr[2].a = "222";
            console.log(arr);
            console.log(result);
        </script>

     8.手动实现call,apply,bind

            <script>
                // 手动实现apply(obj, [arg1, arg2, arg3])
                Function.prototype.myApply = function (context) {
                    let obj = context || window
                    obj.fn = this
                    const arg = arguments[1] || [] // 如果传递的有参数,那么第二个就是传毒的参数数组
                    let res = obj.fn(...arg)
                    delete obj.fn
                    return res
                }
                function f(a, b) {
                    console.log(a, b)
                    console.log(this.name)
                }
                let obj = {
                    name: '这是'
                }
                f.myApply(obj, [1, 2])
            </script>
    
            <script>
                // 手动实现call(obj,arg1,arg2)
                Function.prototype.myCall = function (context) {
                    let obj = context || window
                    obj.fn = this
                    // 获取参数,由于参数不确定传递过来的个数,就先去掉第一个参数,他就是要指向的this
                    let arr = [...arguments].slice(1)
                    res = obj.fn(...arr)
                    delete obj.fn
                    return res
                }
                function f(a, b) {
                    console.log(a, b)
                    console.log(this.name)
                }
                let obj = {
                    name: '栗色'
                }
                f.myCall(obj, 1, 2)
            </script>
    
            <script>
                // 手动实现bind(obj,[arg1,arg2])由于bind函数支持柯里化
                Function.prototype.myBind = function (context) {
                    let obj = context || window
                    let fn = this
                    // 获取第一次传递过来的参数
                    let arg = Array.prototype.slice.call(arguments, 1) // 由于第一位是obj,所以需要将裁剪
                    return function () {
                        let arr = Array.prototype.slice.call(arguments)
                        fn.apply(obj, arg.concat(arr))
                    }
                }
                let obj = {
                    name: '展示'
                }
                function fn(a, b) {
                    console.log(a, b)
                }
    
                let call = fn.bind(obj, [2, 3])
                call(20)
            </script>

     9.手写new

        <body>
            new的操作: (1)创建一个空对象,
            (2)将对象的__proto__指向构造函数的原型对象 (3)利用apply改变this的指向,
            (4)返回结果
            <script>
                function Person(name, age) {
                    this.age = age
                    this.name = name
                }
                Person.prototype.sayHi = function () {
                    console.log('hi,' + this.name)
                }
    
                var p1 = new Person('张三', 20)
                console.log(p1)
    
                // 手动实现new
                function myCreate() {
                    // 创建一个空对象
                    let obj = {}
                    // 获取传递过来的构造函数
                    let fn = [].shift.call(arguments) // shift方法将数组的第一项弹出来并且返回弹出的第一项,这里的[]填不填元素都没有关系
                    // 将空对象的__proto__指向构造函数的prototype(获取构造函数原型上的方法)
                    obj.__proto__ = fn.prototype
                    // 使用apply修改this的指向,将传递过来的参数赋值给obj(获取属性)
                    console.log(arguments)
                    let res = fn.apply(obj, arguments)
                    // 确保返回的是一个对象(以防传递进的fn不是构造函数)
                    return typeof res === 'object' ? res : obj
                }
    
                var p2 = myCreate(Person, '李四', 30)
                console.log(p2)
            </script>
        </body>
    View Code

    10.手写primose

        <body>
            <script>
                // promise 是一个容器,存放的是 未来的某个值
                // 语法上来说,就是一个对象
    
                // console.log(1);
                // const promise = new Promise((resolve, reject) => {
                //   console.log(2);
                // let num = Math.random();
                // if (num > 0.5) {
                //   resolve('成功');
                // } else {
                //   reject('失败');
                // }
                // });
                // promise.then(
                //   (res) => {
                //     console.log(4, res);
                //   },
                //   (err) => {
                //     console.log(5, err);
                //   }
                // );
                // console.log(3);
    
                // promise 有几个状态,是常量,先定义起来
                const PENDDING = 'pendding' // 等待状态
                const FULFILLED = 'fulfilled' // 执行状态
                const REJECTED = 'rejected' // 拒绝状态
    
                class MyPromise {
                    // cb为创建的promise的时候传递进来的函数
                    constructor(cb) {
                        // 一个promise要关注几个点
                        this.status = PENDDING // 当前的promise的状态
                        this.value = undefined // 成功以后的返回值
                        this.reason = undefined // 失败以后的返回值
    
                        // 使用箭头函数可以固定this的指向,不然在cb中调用resolve,this就找不到指向
                        const resolve = (val) => {
                            if (this.status === PENDDING) {
                                // 改变状态
                                this.status = FULFILLED
                                this.value = val
                            }
                        }
                        const reject = (err) => {
                            if (this.status === PENDDING) {
                                this.status = REJECTED
                                this.reason = err
                            }
                        }
    
                        try {
                            // 传递cb进来的时候,它是需要两个参数的,这两个参数分别是函数,所以在上面定义了两个函数
                            cb(resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    }
    
                    then(onFulfilled, onRejected) {
                        if (this.status === FULFILLED) {
                            onFulfilled(this.value)
                        }
    
                        if (this.status === REJECTED) {
                            onRejected(this.reason)
                        }
                    }
                }
    
                const p = new MyPromise((resolve, reject) => {
                    let num = Math.random()
                    if (num > 0.5) {
                        resolve('成功')
                    } else {
                        reject('失败')
                    }
                })
                p.then(
                    (res) => {
                        console.log('请求成功了:', res)
                    },
                    (err) => {
                        console.log('请求失败了:', err)
                    }
                )
            </script>
        </body>
    View Code

     11.手写ajax

        <body>
            ajax请求的五个步骤: (1)创建xmlHttpRequest对象
            (2)设置请求方法和请求的地址 (3)使用send发送情求 (4)监听状态变化
            (5)最后接受返回的数据
            <script>
                function myAjax() {
                    let xhr = new XMLHttpRequest()
                    xhr.open('get', 'url')
                    xhr.onreadystatechange = () => {
                        // xhr的readyState的状态有0-4:0(请求未就绪)1(已经建立服务器链接)2(请求发送)3(请求处理中) 4(已经接受了响应)
                        if (xhr.readyState === 4) {
                            if (xhr.status >= 200 && xhr.status < 300) {
                                let string = xhr.responseText
                                let object = JSON.parse(string)
                            }
                        }
                    }
    
                    // 发送请求
                    xhr.send()
                }
            </script>
    
            <!-- 使用promise实现ajax -->
            <script>
                function myAjax(url) {
                    const p = new Promise(function (resolve, reject) {
                        // 创建xhr对象
                        let xhr = new XMLHttpRequest()
                        // 创建链接
                        xhr.open('get', url)
                        // 监听状态改变
                        xhr.onreadystatechange = () => {
                            if (xhr.readyState === 4) {
                                if (xhr.status >= 200 && xhr.status < 300) {
                                    resolve(JSON.parse(xhr.responseText))
                                } else {
                                    reject('请求失败了')
                                }
                            }
                        }
                        // 发送请求
                        xhr.send()
                    })
    
                    return p
                }
            </script>
        </body>
    View Code

     12.手写防抖

        <body>
            <input id="input1" />
            <script>
                // 防抖就是在在触发了事件的一段时候后执行函数,如果在这段时间里又触发了,就重新计数
                // fn是指向的函数,而delay是执行的时间
                function debounce(fn, delay) {
                    if (typeof fn !== 'function') {
                        throw new TypeError('fn不是函数')
                    }
                    let timer
                    // 返回一个函数是因为它不是已返回就调用,而是当触发了时间的时候次啊会调用它
                    return function () {
                        var _this = this // 这里将this赋值给_this是因为定时器中的this是指向window的
                        var args = arguments // 这里是获取调用函数时候传递进来的参数
                        // 如果定时器存在,就先清除定时器
                        if (timer) {
                            clearTimeout(timer)
                        }
                        // 重置定时器
                        timer = setTimeout(function () {
                            fn.apply(_this, args)
                        }, delay)
                    }
                }
    
                var input1 = document.getElementById('input1')
                input1.addEventListener(
                    'keyup',
                    debounce(() => {
                        console.log(input1.value)
                    }, 600)
                )
            </script>
        </body>
    View Code

    13.手写节流

    <!--
     * @Description: 节流函数就是连续触发事件,只在n秒中执行一次,常用场景;如拖动dom,如果使用防抖的话据会出现卡顿的情况,因为它只在结束的 时候触发一次
     使用节流就流顺的多
     * @Version: 2.0
     * @Autor: Seven
     * @Date: 2021-02-16 12:25:47
     * @LastEditors: Gan
     * @LastEditTime: 2021-02-16 12:42:27
    -->
    <!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>手写节流函数</title>
        </head>
        <body>
            <script>
                function throttle(fn, delay) {
                    // 设置一个定时器
                    let timer
                    // 返回一个函数,当触发事件的时候,就回触发这个事件
                    return function () {
                        // 将调用该函数的元素给_this,因为定时器中的this是执行window的
                        var _this = this
                        // 获取调用函数的时候,传递进来的参数
                        var args = arguments
                        // 判断定时器是否存在,如果存在就表示当前的时间还没有过
                        if (timer) {
                            return
                        }
                        timer = setTimeout(function () {
                            fn.apply(_this, args)
                            timer = null
                        }, delay)
                    }
                }
            </script>
        </body>
    </html>
    View Code

    14.手写promise加载图片

    function getData(url) {
        return new Promise((resolve, reject) => {
            $.ajax({
                url,
                success(data) {
                    resolve(data)
                },
                error(err) {
                    reject(err)
                }
            })
        })
    }
    
    const url1 = 'image1Ur1'
    const url2 = 'image1Ur2'
    const url3 = 'image1Ur3'
    
    getData(url1)
        .then((data1) => {
            console.log(data1)
            getData(url2)
        })
        .then((data2) => {
            console.log(data2)
            getData(url3)
        })
        .then((data3) => {
            console.log(data3)
        })
        .catch((err) => {
            console.log(err)
        })
    View Code
  • 相关阅读:
    skywalking物理拓扑图
    检测HTTPS证书到期时间
    docker zipkin 链路监控
    定时任务和任务-示例
    小程序列表循环出来的list是不同接口赋的值
    小程序编辑器报-50003错误肿么办?
    uni-app小程序组建
    2019
    春日
    Mac安装软件提示破损
  • 原文地址:https://www.cnblogs.com/zhilili/p/14394362.html
Copyright © 2020-2023  润新知