JS advanced skills (deep and shallow copy, throttling, anti-shake)

Advanced JS skills

  1. Learn more about this until you know how to determine what this points to and how to change what this points to.
  2. Until how to handle exceptions in JS, learn deep and shallow copies, and understand recursion

Dark and shallow copy

Direct assignment and copied address will affect the original object. So there are dark and light copies

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

Shallow copy

Shallow copy copies the address, shallowly copying one layer

Summarize:

What is the difference between direct assignment and shallow copy?

  • Direct assignment methods, as long as they are objects, will affect each other because they directly copy the address in the object stack.
  • If the shallow copy is a layer of objects, they will not affect each other. If there are multiple layers of object copies, they will affect each other.

How do you understand shallow copy?

  • After copying the object, the attribute values ​​inside are simple data types and the values ​​are copied directly.
  • If the attribute value is a reference data type, the address is copied.

deep copy

Deep copy copies objects, not addresses

Common methods (three implementations of deep copy):

  1. Deep copy through recursion
  2. lodash/cloneDeep
  3. Realized by JSON.stringfy()
Deep copy through recursion

Insert image description here

Use recursive functions to implement setTimeout to simulate setInterval effects

need:

The page outputs the current time every 1s

To output the current time, you can use: new Date().toLocaleString()

<!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>Document</title>
</head>
<body>
    <div></div>
    <script>
        function getTime(){
      
      
            document.querySelector('div').innerHTML=new Date().toLocaleString()
            setTimeout(getTime,1000)
        }
        getTime()
    </script>
</body>
</html>

Simple deep copy function (object, array, two-dimensional array, but if object arrays refer to each other, this code cannot be implemented)

  //拷贝函数 简易的深拷贝(对象,数组,二维数组,但如果对象数组之间相互引用,这个代码无法实现)
        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 {
    
    
                    //k 属性名     oldObj[k] 属性值
                    newObj[k] = oldObj[k]   //到这儿只是浅拷贝,还需要判断数组这种复杂类型,所以加了一个if else
                }
            }
        }
Deep copy is implemented internally in cloneDeep in JS library lodash

lodash is a consistent, modular, high-performance JavaScript utility library.

Import directly and use

<script src="../js/lodash.min.js"></script>
    <script>
         const obj = {
            uname: 'pink',
            age: 18,
            hobby: ['羽毛球', '滑板',['111','111']],
            family:{
                baby:'小pink'
            }
        }
       const o= _.cloneDeep(obj)
       console.log(o);
       o.family.baby="老pink"
       console.log(obj);
    </script>
Deep copy via JSON.stringfy()
 <script>
           const obj = {
      
      
            uname: 'pink',
            age: 18,
            hobby: ['羽毛球', '滑板',['111','111']],
            family:{
      
      
                baby:'小pink'
            }
        }
        // JSON.stringify()//把对象转化为JSON 字符串, 打印出来的是一堆字符串
        // JSON.parse()//在把字符串转化为对象
        const o=JSON.parse(JSON.stringify(obj))
       console.log(o);
       o.family.baby="老pink"
       console.log(obj);
    </script>

exception handling

Exception handling refers to anticipating errors that may occur during code execution, and then minimizing the occurrence of errors that will cause the entire program to be unable to continue running.

1.throw throw exception

    function fn(x,y){
    
    
            if(!x||!y){
    
    
                //throw '没有参数传递进来'
                throw new Error('没有参数传递过来')
            }
            return x+y
        }
        console.log(fn());

Summarize:

  1. throw throws an exception message and the program will terminate execution
  2. Throw is followed by an error message.
  3. The Error object is used in conjunction with throw to set more detailed error information.

2.try/catch captures exceptions

We can capture error information through try/catch (error information provided by the browser) try try catch block finally finally

<p>123</p>
    <script>
        function fn() {
      
      
            try {
      
      
                //可能发生错误的代码 要写到try里面
                const p = document.querySelector('.p')
                p.style.color = 'red'
            } catch (err) {
      
      
            //拦截错误,提示浏览器提供的错误信息,但是不中断程序的进程

                console.log(err.message);
                 throw new Error ('你看看,选择器错误了吧') //如果觉得浏览器提示的错误不够明显,可以搭配throw使用
                //想要中断程序,加上return
                // return
            }
            finally{
      
      
                //不管程序对不对,一定会执行的代码
                    alert('我无条件的执行')
                }
            console.log(11);
        }
        fn()
    </script>

Summarize:

  1. try/catch is used to capture error information
  2. Write the code that predicts possible errors in the try code section
  3. If an error occurs in the try code segment, the catch code segment will be executed and the error message will be intercepted.
  4. finally will be executed regardless of whether there is an error or not.

3.debugger

Same as breakpoint, but jumps directly to the breakpoint

handle this

This is the most "charming" knowledge point of JavaScript. The value of this may have unexpected results in different application scenarios.

this-oriented

Ordinary function this points to

The calling method of ordinary functions determines the value of this, that is, **[Whoever calls this value points to whom]**

When a normal function does not have a clear caller, the value of this is window. When there is no caller in strict mode ('use strict'), the value of this is undefined.

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

This in arrow functions is completely different from ordinary functions and is not affected by the calling method. In fact, this does not exist in arrow functions.

Try not to use arrow functions in prototypes and constructors, because prototypes and constructors use a lot of this to point to instance objects

  1. The arrow function will help us bind the value of the outer this by default, so the value of this in the arrow function is the same as the outer this
  2. The this in the arrow function refers to the this in the nearest scope
  3. In the outer scope, look up this layer by layer until there is a definition of this

Summarize:

  1. This does not exist in the function, and the previous level is used. Search this layer by layer in the outer scope until there is a definition of this
  2. Inapplicable scenarios: constructors, prototype functions, DOM event functions, etc.
  3. Applicable scenario: Where the upper layer this needs to be used

change this

JavaScript also allows you to specify the point of this in a function. There are three methods to dynamically specify the point of this in a normal function.

1.call() (you only need to understand it, and there are few usage scenarios)

Use the call method to call a function and specify the value of this in the called function.

     const obj={
    
    
            uname:"pink"
        }
        function fn(x,y){
    
    
            console.log(this);
        }
        //1.调用函数
        //2.改变this指向
        //fn()
        fn.call(obj,1,2)
2.apply()

Use the apply method to call the function while specifying the value of this in the called function

Usage scenario: Find the maximum value of an array

            const obj={
    
    
            age:18
        }
        function fn(x,y){
    
    
            console.log(this);
            console.log(x+y);
        }
        //1.调用函数
        //2.改变this指向
        // fn.apply(this指向谁,数组参数)
        fn.apply(obj,[1,2])
        //3.返回值 本身就是在调用函数,所以返回值就是函数的返回值
        //使用场景: 求数组最大值
        // const max=Math.max(1,2,3)
        // console.log(max);  //之前的写法
        const arr=[100,2,22]
        const max=Math.max.apply(Math,arr)
        const min=Math.min.apply(Math,arr)
        console.log(max,min);  

**Summary:** Find the maximum value of the array (at least three types):

  1. Loop through an array to find the maximum value
  2. spread operator
  3. apply method
3.bind()

The bind method does not call the function, but it can change the this pointer inside the function.

Therefore, when we only want to change the this pointer and do not want to call this function, we can use bind, such as changing the this pointer inside the timer.

    <button>点击</button>
    <script>
          const obj={
      
      
            age:18
        }
        function fn(){
      
      
            console.log(this);
        }
        //1.bind 不会调用函数
        //2.能改变this指向
        //3.返回值是个函数, 但是这个函数里面的this是更改过的obj
        const fun=fn.bind(obj)
        console.log(fun);
        fun()
        const btn=document.querySelector('button')
        btn.addEventListener('click',function(){
      
      
            //禁用按钮
            this.disabled=true
            window.setTimeout(function(){
      
      
                //在这个普通函数里面,我们要this由原来的的window改为btn
                this.disabled=false
            }.bind(btn),2000)   //这里写this是调用上一级setTimeout的window  用bind解决问题
        })
        //需求:有一个按钮,点击里面就禁用,2秒之后开启
    </script>

[External link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-P7YKmk7T-1681567960123) (C:\Users\86184\Desktop\4.3-4.14\call, apply, bind. png)]

Performance optimization

Anti-shake (debounce)

Within a unit time, events are triggered frequently and only the last one is executed.

scenes to be used:

Use the search box to search . The user only needs to complete the last input before sending the request.

Mobile phone number, email verification input detection

Let’s make a small case. Without anti-shake, if the mouse slides, the number in the box will increase by 1.

<!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>Document</title>
    <style>
        .box{
      
      
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>
<body>
    <div class="box"></div>
    <script>
        //需求:鼠标在盒子上移动,里面的数字就会变化+1
        const box=document.querySelector('.box')
        let i=1
        function mouseMove(){
      
      
            box.innerHTML=i++
            //如果里面存在大量消耗性能的代码,就会使性能大大降低
        }
        //添加事件
        box.addEventListener('mousemove',mouseMove)
    </script>
</body>
</html>

Add anti-shake: when the mouse moves on the box, the number inside will change +1 after the mouse stops for 500ms.

Implementation methods (two):

1. Anti-shake processing provided by lodash

2. Handwrite an anti-shake function to handle it (core idea: use the timer setTimeout to implement)

<!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>Document</title>
    <style>
        .box{
      
      
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>
<body>
    <div class="box"></div>
    <script src="../js/lodash.min.js"></script>
    <script>
        //利用防抖实现性能优化
        //需求:鼠标在盒子上移动,里面的数字就会变化+1
        const box=document.querySelector('.box')
        let i=1
        function mouseMove(){
      
      
            box.innerHTML=i++
            //如果里面存在大量消耗性能的代码,就会使性能大大降低
        }
        // //添加事件
        // box.addEventListener('mousemove',mouseMove)

        //方法一.利用Lodash库实现防抖 -500ms之后采取+1
        //语法:_.debounce(fun,时间)
        // box.addEventListener('mousemove',_.debounce(mouseMove,500))
        
        //方法二.手写一个防抖函数来处理(核心思路:利用定时器setTimeout来实现)
        //1.声明定时器变量
        //2.每次鼠标移动(事件触发)的时候都要先判断是否有定时器,如果有先清除之前的定时器
        //3.如果没有定时器,则开启定时器,存入到定时器变量中
        //4.定时器里面写函数调用
        function debounce(fn,t){
      
      
            let timer
            //return 返回一个匿名函数
            return function(){
      
      
                if(timer) clearTimeout(timer)
                timer =setTimeout(function(){
      
      
                    fn()  //加小括号调用fn函数
                },t)
            }
        }
        box.addEventListener('mousemove',debounce(mouseMove,500))
    </script>
</body>
</html>

throttling

Within a unit time, events are triggered frequently and executed only once.

scenes to be used:

High-frequency events: mousemove, page size resize, scroll bar scroll, etc.

Add throttling: No matter how many times the mouse moves on the box, the number inside will change by +1 every 500ms.

Implementation methods (two):

1.Throttling processing provided by lodash

2. Handwrite a throttling function to handle it (core idea: use the timer setTimeout to implement)

<!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>Document</title>
    <style>
        .box {
      
      
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script src="../js/lodash.min.js"></script>
    <script>
        //利用节流实现性能优化
        //需求:鼠标在盒子上移动,不管移动多少次,每隔500ms里面的数字才会变化+1
        const box = document.querySelector('.box')
        let i = 1
        function mouseMove() {
      
      
            box.innerHTML = i++
            //如果里面存在大量消耗性能的代码,就会使性能大大降低
        }
        // //添加事件
        // box.addEventListener('mousemove',mouseMove)

        //方法一.利用Lodash库实现防抖 -500ms之后采取+1
        //语法:_.throttle(fun,时间)
        // box.addEventListener('mousemove',_.throttle(mouseMove,3000))

        // 方法二.手写一个节流函数来处理(核心思路:利用定时器setTimeout来实现)
        // 1.声明定时器变量
        // 2.每次鼠标移动(事件触发)的时候都要先判断是否有定时器,如果有先清除之前的定时器
        // 3.如果没有定时器,则开启定时器,存入到定时器变量中
        // 4.定时器里面写函数调用
        function throttle(fn, t) {
      
      
            let timer = null
            //return 返回一个匿名函数
            return function () {
      
      
                if (!timer) {
      
      
                    timer = setTimeout(function () {
      
      
                        fn()  //加小括号调用fn函数
                        //清空定时器
                        timer=null
                    }, t)
                }
            }
        }
        box.addEventListener('mousemove', throttle(mouseMove, 3000))
    </script>
</body>

</html>

Clear the timer problem

 let timer =null
        timer =setTimeout(()=>{
    
    
            clearTimeout(timer)
            console.log(timer);//结果为1
        },1000)
//在setTimeout中是无法删除定时器的,因为定时器还在运作,所以使用timer=null  而不是clearTimeout(timer)
    

Execution context stack

JavaScript engine execution is executed sequentially, but it does not analyze and execute code line by line, but analyze and execute piece by piece. Before executing each piece of code, he will do a "preparation work".

A more technical term is to create an "execution context"

For example: promotion of variable declarations, overall promotion of function declarations, etc.

**Question:** We have written many functions. How does the JavaScript engine create and manage so many execution contexts?

The JavaScript engine manages execution context by creating an execution context stack (ECS).

When JavaScript starts to interpret and execute code, the first thing it encounters is the global code, so it will push a global execution context into the execution context stack, which we use globalContext to represent, and only when the entire program ends, ECStack will be Cleared, so there will always be a globalContext at the bottom of ECStack before the program ends.

When JavaScript executes a function, it creates an execution context and pushes it onto the execution context stack. When the function completes execution, the function's execution context will be popped from the stack.

Processes and Threads

process

Once the program is executed, it occupies an independent memory space

Processes can be viewed through Windows Task Manager

Thread

Is an independent execution unit in the process

It is a complete process of program execution

Is the smallest scheduling unit of the CPU

Illustration

Insert image description here

related information

  1. The application must run on a thread of a process
  2. There is at least one running thread in a process: the main thread, which is automatically created after the process starts
  3. A process can also run multiple threads at the same time, we will say that the program is multi-threaded
  4. Data within a process can be directly shared by multiple threads in it
  5. Data cannot be shared directly between multiple threads
  6. Thread pool is a container that stores multiple thread objects to enable repeated use of thread objects.

question:

1. What are multi-processes and multi-threads?

Multi-process running: An application can start multiple instances to run at the same time

Multi-threading: Within a process, multiple threads are running at the same time

2. Compare single-threading and multi-threading

Multithreading

Advantages: Can effectively improve CPU utilization

Disadvantages: overhead of creating multiple threads, overhead of switching between threads, deadlock and state synchronization issues

single thread

Advantages: Sequential programming is simple and easy to understand

Disadvantages: low efficiency

3. JS runs in a single thread, but it can run in multiple threads using the Web Worker in H5.
4. The browser runs in multiple threads.

The browser has a single process (firefox, old version of IE)

There are also multiple processes (Chrome, new version of IE)

Guess you like

Origin blog.csdn.net/L19541216/article/details/130176277