[Interview questions] JavaScript advanced four, advanced skills

JavaScript advanced four, advanced skills

1. Deep and shallow copy

  • First, shallow copy and deep copy are only for reference types

(1) shallow copy

  • Shallow copy: copy the value of the attribute of the object ( 简单类型the stored value is 值本身, 引用类型the stored value is 对象的堆地址), so if there is one in the copied object value 引用类型属性, the copied new object attribute and the source object attribute point to the same address, 修改and the pointed object will 相互影响.

  • Common method:

    • Copy objects: Object.assgin() or the spread operator {...obj}
    • Copy array: Array.prototype.concat() or [...arr]

The case is as follows:

<!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>
        const user = {
            uname: 'pink',
            age: 18,
            family: {
                baby: '小pink'
            }
        }

        // 浅拷贝
        const o1 = {...user
        }
        o1.age = 20 //对象属性值类型修改不影响
        console.log(o1.age) //20
        console.log(user.age) //18

        // 浅拷贝
        const o2 = {}
        Object.assign(o2, user)
        o2.age = 20
        o2.family.baby = '老pink' //对象属性引用类型修改会相互影响
        console.log(o2.family.baby) //老pink
        console.log(user.family.baby) //老pink
    </script>
</body>

</html>

(2) Deep copy

  • Deep copy: When copying object properties, a new object is created when a reference type is encountered, and then recursively until all fields are copied.
  • Common method:
    • Recursive function implements deep copy
      • A function is recursive if it can call itself internally
      • Since recursion is prone to "stack overflow" errors (stack overflow), it is necessary to add the exit condition return
    • lodash.cloneDeep realizes deep copy
    • JSON.stringify realizes deep copy

Recursive function implements deep copy:

<!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>
        const obj = {
            uname: 'pink',
            age: 18,
            hobby: ['乒乓球', '足球'],
            family: {
                baby: '小pink'
            }
        }
        const o = {}

        // 拷贝函数
        function deepCopy(newObj, oldObj) {
            for (let k in oldObj) {
                // 处理数组
                if (oldObj[k] instanceof Array) {
                    newObj[k] = []
                    deepCopy(newObj[k], oldObj[k])
                        // 处理对象
                } else if (oldObj[k] instanceof Object) {
                    newObj[k] = {}
                    deepCopy(newObj[k], oldObj[k])
                        //值类型
                } else {
                    newObj[k] = oldObj[k]
                }
            }
        }

        // 函数调用  两个参数 o 新对象  obj 旧对象
        deepCopy(o, obj)
        o.age = 20
        o.hobby[0] = '篮球'
        o.family.baby = '老pink'
        console.log(o.age) //20
        console.log(o.hobby[0]) //篮球
        console.log(o.family.baby) //老pink
        console.log(obj.age) //18
        console.log(obj.hobby[0]) //乒乓球
        console.log(obj.family.baby) //小pink
    </script>
</body>

</html>

lodash.cloneDeep realizes deep copy:

<!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>lodash.cloneDeep实现深拷贝</title>
</head>

<body>
    <!-- 先引用 -->
    <script src="./lodash.min.js"></script>
    <script>
        const obj = {
            uname: 'pink',
            age: 18,
            hobby: ['乒乓球', '足球'],
            family: {
                baby: '小pink'
            }
        }
        const o = _.cloneDeep(obj)
        o.age = 20
        o.hobby[0] = '篮球'
        o.family.baby = '老pink'
        console.log(o.age) //20
        console.log(o.hobby[0]) //篮球
        console.log(o.family.baby) //老pink
        console.log(obj.age) //18
        console.log(obj.hobby[0]) //乒乓球
        console.log(obj.family.baby) //小pink
    </script>
</body>

</html>

JSON.stringify implements deep copy:

<!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>JSON.stringify实现深拷贝</title>
</head>

<body>
    <script>
        const obj = {
                uname: 'pink',
                age: 18,
                hobby: ['乒乓球', '足球'],
                family: {
                    baby: '小pink'
                }
            }
            // 把对象转换为 JSON 字符串    
        const o = JSON.parse(JSON.stringify(obj))
        o.age = 20
        o.hobby[0] = '篮球'
        o.family.baby = '老pink'
        console.log(o.age) //20
        console.log(o.hobby[0]) //篮球
        console.log(o.family.baby) //老pink
        console.log(obj.age) //18
        console.log(obj.hobby[0]) //乒乓球
        console.log(obj.family.baby) //小pink
    </script>
</body>

</html>

2. Exception handling

  • Exception handling refers to estimating errors that may occur during code execution, and then avoiding the occurrence of errors to the greatest extent so that the entire program cannot continue to run

(1) throw throws an exception

  • throw  抛出异常信息, the program also会终止执行
  • throw followed by the error message
  • The Error object is used in conjunction with throw to set more detailed error information

The case is as follows:

<!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>throw抛出异常</title>
</head>

<body>
    <script>
        function fn(x, y) {
            if (!x || !y) {
                // throw '没有参数传递进来'
                throw new Error('没有参数传递过来')
            }

            return x + y
        }
        console.log(fn())
    </script>
</body>

</html>

image-20230530140409885

(2) try /catch capture exception

  • try...catch is used to capture error messages
  • Write the code that estimates that errors may occur in the try code segment
  • If an error occurs in the try code segment, the catch code segment will be executed and the error message will be intercepted
  • finally will be executed regardless of whether there is an error

The case is as follows:

<!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>try-catch捕获异常</title>
</head>

<body>
    <p>123</p>
    <script>
        function fn() {
            try {
                // 可能发送错误的代码 要写到 try
                const p = document.querySelector('.p')
                p.style.color = 'red'
            } catch (err) {
                // 拦截错误,提示浏览器提供的错误信息,但是不中断程序的执行
                console.log(err.message)
            } finally {
                // 不管你程序对不对,一定会执行的代码
                console.log('不管你程序对不对,一定会执行的代码')
            }
            console.log(11)
        }
        fn()
    </script>
</body>

</html>

image-20230530140903442

(3)debugger

  • You can add a debugger in the code to breakpoint debug the code

image-20230530141922325

3. Handle this

(1) this orientation

  • Ordinary function this points to

    • Who calls the value of this to whom
    • When an ordinary function has no clear caller, the value of this is window, and when there is no caller in strict mode, the value of this is undefined
  • The arrow function this points to

  • The this in the arrow function does not exist, the this in the arrow function is the this in the nearest bound scope, and this is searched for this in the outer scope layer by layer until there is a definition of this

  • Not applicable: constructors, prototype functions, functions in literal objects, dom event functions, etc.

  • Applicable situation: where the upper layer this needs to be used

The case is as follows:

<!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>普通函数的this指向</title>
</head>

<body>
    <button>点击</button>
    <script>
        // 普通函数:谁调用我,this就指向谁
        console.log(this) // window
        function fn() {
            console.log(this) // window    
        }
        window.fn()
        window.setTimeout(function() {
            console.log(this) // window 
        }, 1000)
        document.querySelector('button').addEventListener('click', function() {
            console.log(this) // 指向 button
        })
        const obj = {
            sayHi: function() {
                console.log(this) // 指向 obj
            }
        }
        obj.sayHi()
    </script>
</body>

</html>

(2) change this

  • JavaScript also allows specifying the pointing of this in a function, and there are 3 methods to dynamically specify the pointing of this in a normal function
  • call()
    • Using the call method 调用函数,  the value 指定in the called function  at the same timethis
    • grammar:fun.call(thisArg, arg1, arg2, ...)
      • thisArg: the this value specified when the fun function is running
      • arg1, arg2: additional arguments passed
      • The return value is the return value of the function, because it is the calling function
  • apply()
    • Use the apply method 调用函数, while  the value 指定in the called function this
    • grammar:fun.apply(thisArg, [argsArray])
      • thisArg: the this value specified when the fun function is running
      • argsArray: passed values,必须包含在数组里面
      • The return value is the return value of the function, because it is the calling function
      • 因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值
  • bind()
    • bind() method 不会调用函数. 指定the value in the called function  this , returning the new function
    • grammar:fun.bind(thisArg, arg1, arg2, ...)
      • thisArg: the this value specified when the fun function is running
      • arg1, arg2: additional arguments passed
      • Returns the modified by the specified this value and initialization parameters 原函数拷贝 (新函数)
      • 因此只改变 this 指向,不调用函数时,使用 bind,比如改变定时器内部的this指向.

The call case is as follows:

<!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>call</title>
</head>

<body>
    <script>
        const obj = {
            uname: 'pink'
        }

        function fn(x, y) {
            console.log(this) // window
            console.log(x + y)
        }

        // 1. 调用函数  
        // 2. 改变this指向obj,原来是window调用指向window
        // 3. 返回值就是函数的返回值
        fn.call(obj, 1, 2)
    </script>
</body>

</html>

The apply case is as follows:

<!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>apply</title>
</head>

<body>
    <script>
        const obj = {
            age: 18
        }

        function fn(x, y) {
            console.log(this)
            console.log(x + y)
        }
        // 1. 调用函数
        // 2. 改变this指向
        // 3. 返回值就是函数的返回值
        fn.apply(obj, [1, 2])

        // 使用场景:求数组最大值最小值
        const arr = [100, 44, 77]
        const max = Math.max.apply(Math, arr)
        const min = Math.min.apply(null, arr)
        console.log(max, min)
    </script>
</body>

</html>

The bind case is as follows:

<!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>bind</title>
</head>

<body>
    <button>发送短信</button>
    <script>
        const obj = {
            age: 18
        }

        function fn() {
            console.log(this)
        }

        // 1. bind不会调用函数 
        // 2. 能改变this指向
        // 3. 返回值是个函数,但是这个函数里面的this是更改过的obj
        const fun = fn.bind(obj)
        fun()

        // 需求:有一个按钮,点击里面就禁用,2秒钟之后开启
        document.querySelector('button').addEventListener('click', function() {
            // 禁用按钮
            this.disabled = true
            window.setTimeout(function() {
                    // 在这个普通函数里面,我们要this由原来的window 改为 btn
                    this.disabled = false
                }.bind(this), 2000) // 这里的this 和 btn 一样
        })
    </script>
</body>

</html>

4. Performance optimization

(1) Anti-shake

  • The so-called anti-shake means that after the event is triggered, the function can only be executed once within n seconds. If the event is triggered again within n seconds, the 重新计算function execution time will be reduced.
  • Usage scenario: input in the search box, set the request to be sent after n seconds after each input, if there is still input during the period, recalculate the time

The case is as follows:

<!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>
    <style>
        .box {
            width: 500px;
            height: 500px;
            background-color: #ccc;
            color: #fff;
            text-align: center;
            font-size: 100px;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script>
        const box = document.querySelector('.box')
        let i = 1

        //数值加1
        function mouseMove() {
            box.innerHTML = ++i
        }

        // 防抖函数
        function debounce(fn, t) {
            let timeId
            return function() {
                // 如果有定时器就清除
                if (timeId) clearTimeout(timeId)

                // 开启定时器
                timeId = setTimeout(function() {
                    fn()
                }, t)
            }
        }
        //鼠标移动触发函数
        box.addEventListener('mousemove', debounce(mouseMove, 1000))
    </script>
</body>

</html>

(2) throttling

  • The so-called throttling refers to triggering events continuously but executing a function only once in n seconds
  • Scenarios: When the mouse moves, the page size changes, and the scroll bar scrolls, etc., the overhead is relatively high

The case is as follows:

<!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>
    <style>
        .box {
            width: 500px;
            height: 500px;
            background-color: #ccc;
            color: #fff;
            text-align: center;
            font-size: 100px;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script>
        const box = document.querySelector('.box')
        let i = 1

        //数值加1
        function mouseMove() {
            box.innerHTML = ++i
        }

        // 节流函数
        function throttle(fn, t) {
            // 起始时间
            let startTime = 0
            return function() {
                // 得到当前的时间
                let now = Date.now()

                // 判断如果大于等于 500 调用函数
                if (now - startTime >= t) {
                    // 调用函数
                    fn()

                    // 起始的时间 = 现在的时间
                    startTime = now
                }
            }
        }
        box.addEventListener('mousemove', throttle(mouseMove, 1000))
    </script>
</body>

</html>

(3) lodash throttling and anti-shake

<!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>lodash节流和防抖</title>
    <style>
        .box {
            width: 500px;
            height: 500px;
            background-color: #ccc;
            color: #fff;
            text-align: center;
            font-size: 100px;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script src="./lodash.min.js"></script>
    <script>
        const box = document.querySelector('.box')
        let i = 1

        //数值加1
        function mouseMove() {
            box.innerHTML = ++i
        }

        // lodash 节流
        // box.addEventListener('mousemove', _.throttle(mouseMove, 500))
        // lodash 防抖
        box.addEventListener('mousemove', _.debounce(mouseMove, 500))
    </script>
</body>

</html>

Front-end interview question bank ( necessary for interview) recommendation: ★★★★★            

Address: front-end interview question bank

Guess you like

Origin blog.csdn.net/weixin_42981560/article/details/132304176