JS の高度なスキル (深いコピーと浅いコピー、スロットル、手ぶれ補正)

JSの高度なスキル

  1. これが何を指しているのかを判断する方法と、これが指しているものを変更する方法を理解するまで、これについてさらに詳しく学習してください。
  2. JSで例外を処理する方法、深いコピーと浅いコピーを学び、再帰を理解するまで

ディープコピー

直接割り当てとコピーされたアドレスは、元のオブジェクトに影響します。つまり、暗いコピーと明るいコピーが存在します

まず、浅いコピーと深いコピーは参照型のみです。

浅いコピー

浅いコピーはアドレスをコピーし、1 つのレイヤーを浅くコピーします

要約:

直接代入と浅いコピーの違いは何ですか?

  • 直接代入メソッドは、オブジェクトである限り、オブジェクト スタック内のアドレスを直接コピーするため、相互に影響します。
  • 浅いコピーがオブジェクトのレイヤーである場合、それらは相互に影響を与えませんが、オブジェクトのコピーが複数のレイヤーである場合、それらは相互に影響を与えます。

浅いコピーをどのように理解しますか?

  • オブジェクトをコピーした後、内部の属性値は単純なデータ型であり、値が直接コピーされます。
  • 属性値が参照データ型の場合、アドレスがコピーされます。

ディープコピー

ディープコピーはアドレスではなくオブジェクトをコピーします

一般的な方法 (ディープ コピーの 3 つの実装方法):

  1. 再帰によるディープコピー
  2. lodash/cloneDeep
  3. JSON.stringfy()で実現
再帰によるディープコピー

ここに画像の説明を挿入

再帰関数を使用して setTimeout を実装し、setInterval 効果をシミュレートします

必要:

ページは 1 秒ごとに現在時刻を出力します

現在時刻を出力するには、 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>

単純なディープコピー関数 (オブジェクト、配列、2 次元配列、ただしオブジェクト配列が相互参照する場合、このコードは実装できません)

  //拷贝函数 简易的深拷贝(对象,数组,二维数组,但如果对象数组之间相互引用,这个代码无法实现)
        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
                }
            }
        }
ディープコピーはJSライブラリlodashのcloneDeepに内部実装されています。

lodash は、一貫性のあるモジュール式の高性能 JavaScript ユーティリティ ライブラリです。

直接インポートして使用する

<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>
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>

例外処理

例外処理とは、コードの実行中に発生する可能性のあるエラーを予測し、プログラム全体の実行を続行できなくなるようなエラーの発生を最小限に抑えることを指します。

1.throwは例外をスローします

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

要約:

  1. throw は例外メッセージをスローし、プログラムは実行を終了します。
  2. スローの後にエラー メッセージが表示されます。
  3. Error オブジェクトは throw と組み合わせて使用​​され、より詳細なエラー情報を設定します。

2. キャプチャ例外のトライ/キャッチ

try/catch (ブラウザによって提供されるエラー情報) を通じてエラー情報を取得できます。 try try catch ブロックを使用してください。

<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>

要約:

  1. try/catch はエラー情報を取得するために使用されます
  2. try code セクションに、起こり得るエラーを予測するコードを記述します。
  3. try コード セグメントでエラーが発生した場合、catch コード セグメントが実行され、エラー メッセージがインターセプトされます。
  4. エラーの有無に関係なく、finally が実行されます。

3.デバッガ

ブレークポイントと同じですが、ブレークポイントに直接ジャンプします

これを対応して

これは JavaScript の最も「魅力的な」知識ポイントであり、この値はさまざまなアプリケーション シナリオで予期しない結果をもたらす可能性があります。

this指向

これが指す通常の関数

this の値は、通常の関数の呼び出し方法によって決まります。つまり、 **【this の値を呼び出す人が誰を指すか】**

通常の関数に明確な呼び出し元がない場合、この値は window となり、厳密モード ('厳密な使用') で呼び出し元がない場合、この値は未定義になります。

    <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>
これが指すアロー関数

アロー関数のthisは通常の関数とは全く異なり、呼び出しメソッドの影響を受けません、実はアロー関数にはこれが存在しません。

プロトタイプとコンストラクターではインスタンス オブジェクトを指すためにアロー関数を多量に使用するため、プロトタイプとコンストラクターではアロー関数を使用しないようにしてください。

  1. アロー関数は、デフォルトで外側の this の値をバインドするのに役立ちます。そのため、アロー関数の this の値は外側の this と同じになります。
  2. アロー関数内の this は、最も近いスコープ内の this を参照します。
  3. この定義が存在するまで、外側のスコープでこのレイヤーをレイヤーごとに検索します。

要約:

  1. これは関数には存在せず、前のレベルが使用されます。この定義が存在するまで、外側のスコープでこのレイヤーをレイヤーごとに検索します。
  2. 適用できないシナリオ: コンストラクター、プロトタイプ関数、DOM イベント関数など。
  3. 該当するシナリオ: 上位層でこれを使用する必要がある場合

これを変える

JavaScript では関数内で this のポイントを指定することもできますが、通常の関数で this のポイントを動的に指定する方法は 3 つあります。

1.call() (理解しておいてください。使用シナリオはほとんどありません)

call メソッドを使用して関数を呼び出し、呼び出される関数に this の値を指定します。

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

apply メソッドを使用して関数を呼び出し、呼び出される関数で this の値を指定します。

使用シナリオ: 配列の最大値を見つける

            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);  

**概要:** 配列の最大値を見つけます (少なくとも 3 つのタイプがあります)。

  1. 配列を走査して最大値を見つける
  2. スプレッド演算子
  3. 適用メソッド
3.バインド()

バインド メソッドは関数を呼び出しませんが、関数内の this ポインターを変更できます。

したがって、this ポインタを変更するだけで、この関数を呼び出したくない場合は、タイマー内で this ポインタを変更するなど、bind を使用できます。

    <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>

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムが存在する可能性があります。画像を保存して直接アップロードすることをお勧めします (img-P7YKmk7T-1681567960123) (C:\Users\86184\Desktop\4.3-4.14\)呼び出し、適用、バインド。png)]

パフォーマンスの最適化

手ぶれ補正(デバウンス)

単位時間内にイベントは頻繁にトリガーされ、最後のイベントのみが実行されます。

使用するシーン:

検索ボックスを使用して を検索しますユーザーは、リクエストを送信する前に最後の入力を完了するだけで済みます。

携帯電話番号、メール認証入力検知

小さなケースを作りましょう。手ぶれ補正なしの場合、マウスをスライドさせるとボックス内の数字が 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>

手ぶれ補正の追加: マウスがボックス上で移動すると、マウスが 500 ミリ秒停止した後に内部の数値が +1 変化します。

実装方法(2種類):

1. lodashによる手ぶれ補正処理

2. 手ぶれ補正関数を手書きして処理します (中心的なアイデア: タイマー setTimeout を使用して実装します)

<!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>

スロットル

単位時間内では、イベントは頻繁にトリガーされ、実行されるのは 1 回だけです。

使用するシーン:

高頻度イベント: マウスの移動、ページ サイズの変更、スクロール バーのスクロールなど。

スロットリングの追加: マウスがボックス上で何回移動しても、内部の数値は 500 ミリ秒ごとに +1 ずつ変化します。

実装方法(2種類):

1. lodashが提供するスロットル処理

2. それを処理するスロットリング関数を手書きします (中心となるアイデア: タイマー setTimeout を使用して実装します)

<!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>

タイマー問題をクリアする

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

実行コンテキストスタック

JavaScript エンジンは順次実行されますが、コードを 1 行ずつ分析して実行するのではなく、セクションごとに分析して実行します。各コードを実行する前に、まず「準備作業」を行います。

より専門的な用語は、「実行コンテキスト」を作成することです。

例: 変数宣言の推進、関数宣言の全体的な推進など。

**質問:** 私たちは多くの関数を作成しますが、JavaScript エンジンはどのようにして非常に多くの実行コンテキストを作成および管理するのでしょうか?

JavaScript エンジンは、実行コンテキスト スタック (ECS) を作成することで実行コンテキストを管理します。

JavaScript がコードの解釈と実行を開始するとき、最初に遭遇するのはグローバル コードであるため、グローバル実行コンテキストを実行コンテキスト スタックにプッシュします。これを表すために globalContext を使用します。プログラム全体が終了した場合にのみ、ECStack はクリアされるため、プログラムが終了する前に ECStack の下部に常に globalContext が存在します。

JavaScript が関数を実行すると、実行コンテキストが作成され、実行コンテキスト スタックにプッシュされます。関数の実行が完了すると、関数の実行コンテキストがスタックからポップされます。

プロセスとスレッド

プロセス

プログラムが実行されると、独立したメモリ空間を占有します。

プロセスは Windows タスク マネージャーから表示できます

これはプロセス内の独立した実行単位です。

プログラム実行の完全なプロセスです

CPUの最小スケジューリング単位です。

ここに画像の説明を挿入

関連情報

  1. アプリケーションはプロセスのスレッド上で実行する必要があります
  2. プロセス内には少なくとも 1 つの実行スレッドがあります。メイン スレッドは、プロセスの開始後に自動的に作成されます。
  3. プロセスは同時に複数のスレッドを実行することもでき、プログラムが複数のスレッドで実行されていると言うことにします。
  4. プロセス内のデータは、プロセス内の複数のスレッドで直接共有できます。
  5. 複数のスレッド間でデータを直接共有することはできません
  6. スレッド プールは、スレッド オブジェクトを繰り返し使用できるようにするために、複数のスレッド オブジェクトを格納するコンテナです。

質問:

1. マルチプロセスとマルチスレッドとは何ですか?

マルチプロセスの実行: アプリケーションは複数のインスタンスを起動して同時に実行できます。

マルチスレッド: プロセス内で複数のスレッドが同時に実行されます。

2. シングルスレッドとマルチスレッドの比較

マルチスレッド化

利点: CPU 使用率を効果的に改善できる

短所: 複数のスレッド作成のオーバーヘッド、スレッド間の切り替えのオーバーヘッド、デッドロックと状態の同期の問題

シングルスレッド

利点: シーケンシャルプログラミングはシンプルで理解しやすい

短所: 効率が低い

3. JS は単一スレッドで実行されますが、H5 の Web Worker を使用すると複数のスレッドで実行できます。
4. ブラウザーは複数のスレッドで実行されます。

ブラウザには単一のプロセスがあります (Firefox、IE の古いバージョン)

マルチプロセス (Chrome、IE の新しいバージョン)

おすすめ

転載: blog.csdn.net/L19541216/article/details/130176277