js高階

竹搖風颯發表於2021-10-10

1. 物件導向程式設計介紹

1.1 兩大程式設計思想

--- 程式導向

--- 物件導向

1.2 程式導向程式設計 POP

程式導向就是分析出解決問題所需要的步驟,然後用函式把這些步驟一步一步實現,使用的時候在一個一個的依次呼叫就可以了。

舉個例子:將大象裝進冰箱-------程式導向做法

1、開啟冰箱門

2、把大象裝進去

3、關上冰箱門

程式導向,就是按照我們分析好了的步驟,按照步驟解決問題。

1.3 物件導向程式設計 OOP

物件導向就是把事務分解稱為一個個物件,然後由物件之間分工與合作。

舉個例子:將大象裝進冰箱-------物件導向做法

先找出物件。並寫出物件的功能:

1、大象物件---進去

2、冰箱物件

​ a.開啟

​ b.關閉

3、使用大象和冰箱的功能

物件導向是以物件功能來劃分問題,而不是步驟。

特性:封裝性、繼承性、多型性

1.4 物件導向程式設計和麵向過程程式設計的對比

2. ES6中的類和物件

物件導向的思維特點

1、抽取物件公用的屬性和行為組織(封裝)成一個類(模板)

2、物件進行例項化,獲取類的物件

物件導向程式設計我們考慮的是有哪些物件,按照物件導向的思維特點,不斷的建立物件,使用物件,指揮物件做事情。

2.1 物件

萬物皆物件:在JavaScript中,物件是一組無序的相關屬性和方法的集合,所有的事物都是物件,例如字串、陣列、數值、函式等。

2.2 類 class

在ES6 中新增加了類的概念,可以使用class 關鍵字宣告一個類,之後以這個類來例項化物件。

​ ---類 抽象了物件的公共部分,它泛指某一個大類

​ ---物件 特指某一個,通過例項化一個具體的物件

2.3 建立類

class phone { // 建立一個手機類
  //class body
}
// 利用類建立物件
new phone();

2.4 類 constructor 建構函式

constructor()方法是類的建構函式(預設方法),用於傳遞引數,返回例項物件,通過new命令生成物件例項時,自動呼叫該方法。如果沒有顯示定義,類內部會自動給我們建立一個constructor() 。

class Star {
  // 類的共有屬性放到 constructor 裡面
        constructor(uname, age) {
            this.uname = uname;
            this.age = age;
        }
    }

		// 利用類建立物件 new
    var ty = new Star("唐嫣", 18);
    console.log(ty);
		console.log(ty.uname);

(1) 通過class 關鍵字建立類,類名我們還是習慣性定義首字母大寫

(2) 類裡面通過constructor 函式,可以接收傳遞過來的引數,同時返回例項物件

(3) constructor 函式 只要new生成例項時,就會自動呼叫這個函式,如果我們不寫這個函式,物件也會自動生成這個函式

(4) 生成例項 new 不能省略

(5) 最後注意語法規範,建立類 類名後面不要加小括號,生成例項 累了嗎後面加小括號,建構函式不需要加function

2.5 類建立方法

        class Star {
            // 類的共有屬性放到 constructor 裡面
            constructor(uname, age) {
                this.uname = uname;
                this.age = age;
            }
            say(language) {
                console.log(this.uname + language);
                // console.log(this.uname + "你好");
            }
        }
        // 利用類建立物件 new
        var ty = new Star("唐嫣", 18);
        console.log(ty);
        console.log(ty.uname);
        // (1) 類裡面的所有函式不需要寫function
        // (2) 多個函式方法之間不需要用逗號間隔
        ty.say("我是恁爹");

(1) 類裡面的所有函式不需要寫function
​ (2) 多個函式方法之間不需要用逗號間隔

3. 類的繼承

3.1 繼承

現實中的繼承:子承父業,繼承父親的姓

程式中的繼承:子類可以繼承父類的一些屬性和方法

3.2 super 關鍵字

super 關鍵字用於訪問和呼叫物件父類上的函式,可以調傭父類的建構函式,也可以呼叫父類的普通函式

繼承中的屬性或者方法查詢原則:就近原則

1.繼承中,如果例項化子類輸出一個方法,先看子類有沒有這個方法,如果有就先執行子類的

2.繼承中,如果子類裡面沒有,就去查詢父類有沒有這個方法,如果有,就執行父類的這個方法(就近原則)

3.3 注意事項

  1. 在es6 中類沒有變數提升,所以必須先定義類,才能通過類例項化物件
  2. 類裡面的共有的屬性和方法一定要加this 使用

4. 建構函式和原型

4.1 概述

在典型的OOP 的語言中(如Java),都存在這個概念,類就是物件的模板,物件就是類的例項,但在ES6之前,js並沒有引入類的概念

4.2 建構函式

建構函式是一種特殊的函式,主要用來初始化物件,即為物件成員變數賦初始值,它總與new 一起使用.我們可以把物件中一些公共的屬性和方法抽取出來,然後封裝到這個函式裡面.

new 在執行時會做四件事情

---在記憶體中建立一個新的空物件

---讓this 指向這個新的物件

---執行建構函式裡面的程式碼,給這個新物件新增屬性和方法

---返回這個新物件(所以建構函式裡面不需要return)

4.3 建構函式的問題

建構函式方法很好用,但是存在浪費記憶體的問題.

建立了兩個例項化物件,兩個物件中都有sing 方法,sing方法又會在記憶體中新開闢一個來存放函式,兩個物件的sing 方法都會佔用記憶體空間用來存放同一個函式,就會比較浪費記憶體.

​兩個物件的sing方法不一樣.

4.4 建構函式原型 prototype

prototype 是一個物件,裡面存放的是constructor 函式,又叫做原型物件

我們可以把那些不變的方法,直接定義在prototype 物件上,這樣所有物件的例項就可以共享這個方法.

4.5 物件原型 __proto__

物件都會有一個屬性 __proto__指向建構函式的prototype 原型物件,之所以我們物件可以使用建構函式prototype 原型物件的屬性和帆帆發,就是因為物件有 ____proto__原型的存在.proto兩邊都有兩個下劃線(__)

4.6 constructor 建構函式

4.7 建構函式、例項、原型物件三者之間的關係

4.8 原型鏈

一層一層的往上找

4.9 JavaScript 的成員查詢機制(規則)

① 當訪問一個物件的屬性(包括方法)時,首先查詢這個物件自身有沒有該屬性

② 如果沒有就查詢它的原型(也就是__proto__指向的prototype原型物件)

③ 如果還沒有就查詢原型物件的原型(Object的原型物件)

④ 依此類推一直找到Object為止(null)

⑤ __proto__物件原型的意義就在於為物件成員查詢機制提一個方向,或者說時一條路線

4.10 原型物件中 this 指向問題

function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        var that;
        Star.prototype.sing = function () {
            console.log('我會唱歌');
            that = this;
        }
        var ldh = new Star('劉德華', 18);
        // 1. 在建構函式中,裡面this 指向的是物件例項 ldh 
        ldh.sing();
        console.log(that === ldh);

        // 2. 原型物件函式裡面的this 指向的是 例項物件 ldh 

4.11 擴充套件內建物件

可以通過原型物件,對原來的內建物件進行擴充套件自定義的方法。比如給陣列增加自定義求偶數和的功能

Array.prototype.sum = function () {
            var sum = 0;
            for (var i = 0; i < this.length; i++) {
                sum += this[i];
            }
            return sum;
        }
        var arr = [1, 2, 3, 4, 5];
        console.log(arr.sum());
        console.log(Array.prototype);

注意;陣列和字串內建物件不能給原型物件覆蓋操作Array.prototype = {},只能是Array.prototype.xxx = function(){}的方式。前者會把其他方法覆蓋掉、後者是在原有的基礎上追加一個函式。

5. 繼承

ES6 之前並沒有給我們提供extends 繼承,我們可以通過建構函式+原型物件模擬實現繼承,被稱為組合屬性。

5.1 call()函式

呼叫這個函式,並且修改函式執行時的this 指向

fun.call(thisArg,arg1,arg2,...)

thisArg:當前呼叫函式this的指向物件

arg1,arg2:傳遞的其他引數

// call 方法
        function fn(x, y) {
            console.log(this);
            console.log(x + y);
        };
        var obj = {
            name: 'john'
        };
        // fn();
        // 1. call() 可以呼叫其他函式

        // fn.call(obj);
        // 2. call() 可以改變這個函式的this 指向,此時這個函式的的this 就指向了這個obj 物件

        fn.call(obj, 1, 2);
        // 3. 函式指向了obj 這個物件,後面的值參與了運算

5.2 借用建構函式繼承父型別屬性

核心原理:通過call() 把父型別的this 指向子型別的this ,這樣就可以實現子型別繼承父型別的屬性。

// 1. 子建構函式
        function Father(uname, age) {
            // this 指向Father 建構函式的物件例項
            this.uname = uname;
            this.age = age;
        }

        Father.prototype.money = function () {
            console.log('掙錢了');
        }

        // 2. 子建構函式
        function Son(uname, age, score) {
            // this 指向Son 建構函式的物件例項
            Father.call(this, uname, age, score);
            this.score = score;
            // 使用call() 方法呼叫了Father() 方法,並且把其中的this 改變為Son() 方法的this 
        }
        // Son.prototype = Father.prototype;  這樣直接賦值會有問題,如果修改了子原型物件,父原型物件也會跟著一起變化
        Son.prototype = new Father();
        // 如果利用物件的形式修改了原型物件,別忘了利用constructor 指回原來的建構函式
        Son.prototype.constructor = Son;
        // 這個是子建構函式專門的方法
        Son.prototype.exam = function () {
            console.log('考試');
        }
        var ty = new Son('唐嫣', 18, 61);
        console.log(ty);
        console.log(Father.prototype);
        console.log(Son.prototype.constructor); // 這個指向的是 Son 建構函式

6. 類的本質

ES6 之前通過 建構函式+原型實現物件導向程式設計

​ (1) 建構函式有原型物件prototype

​ (2) 建構函式原型物件prototype 裡面有constructor 指向建構函式本身

​ (3) 建構函式可以通過原型物件新增方法

​ (4) 建構函式建立的例項物件有__proto__原型指向建構函式的原型物件

ES6通過類實現物件導向程式設計

  1. 類的本質其實還是一個函式,我們也可以簡單的認為類就是建構函式的另外一種寫法

​ (1) 類有原型物件prototype.

​ (2) 類原型物件prototype 裡面有constructor 指向類本身

​ (3) 類可以通過原型物件新增方法

7. ES6 中的新增方法

7.1 ES6 中的新增方法概述

竹搖是以下三個

  • 陣列方法
  • 字串方法
  • 物件方法

7.2 陣列方法

迭代(遍歷)方法:forEach()、map()、filter()、some()、every();

7.2.1 陣列遍歷方法forEach()

array.forEach(function(currentValue,index,arr))

  • currentValue:陣列當前項的值
  • index:陣列當前的索引
  • arr:陣列物件本身
var arr = [1, 2, 3];
        var sum = 0;
        arr.forEach(function (value, index, array) {
            console.log('陣列元素' + value);
            console.log('陣列索引' + index);
            console.log('陣列本身' + array);
            sum += value
        });
        console.log(sum);

7.2.2 篩選陣列filter()

array.filter(function(currentValue,index,arr))

  • filter()方法建立一個新的陣列,新陣列中的元素是通過檢查指定陣列中符合條件的所有元素,主要用於篩選陣列
  • 注意它直接返回一個陣列
  • currentValue:陣列當前項的值
  • index:陣列當前的索引
  • arr:陣列物件本身
var arr = [10, 50, 4, 0, 56, 89];
        var newArr = arr.filter(function (value, index) {
            return value > 20
        })
        console.log(newArr);

7.2.3 查詢陣列中是否有滿足條件的元素some()

array.some(function(currentValue,index,arr))

  • some() 方法用於檢測陣列中的元素是否滿足指定條件,通俗點查詢陣列中是否有滿足條件的元素
  • 注意它返回值是布林值,如果查詢到這個元素,就返回true,如果查詢不到就返回false
  • 如果找到第一個滿足條件的元素,則終止迴圈,不在繼續查詢
  • currentValue:陣列當前項的值
  • index:陣列當前的索引
  • arr:陣列物件本身
var arr = [10, 42, 47, 16, 19];
        var flag = arr.some(function (value, index) {
            return value > 17;
        })
        console.log(flag);

7.2.4 forEach和some的區別

var arr = [1, 2, 4, 9];
        // arr.forEach(function (value) {
        //     if (value == 2) {
        //         console.log('找到了');
        //         return true; // forEach 裡面的return true 不會跳出迴圈   不會終止遍歷/迭代
        //     }
        //     console.log(1);
        // });

        // 如果查詢陣列中唯一的元素,用some 方法更合適

        arr.some(function (value) {
            if (value == 2) {
                console.log('找到了');
                return true; // some 裡面的return true 會跳出迴圈   會終止遍歷/迭代    效率更高
            }
            console.log(1);
        });

7.3 字串方法

trim() 去除字串兩邊的空格

var str = '   abc   ';
console.log(str);  //    abc   
var str1 = str.trim();
console.log(str1); // abc

不能去除字串中間的空格

var str = '   a b c   '
console.log(str); //    a b c   
var str1 = str.trim();
console.log(str1); // a b c

7.4 物件方法

Object.keys(obj) 用於獲取物件自身所有的屬性

  • 效果類似 for...in
  • 返回一個由屬性名組成的陣列
				// 用於獲取物件自身所有的屬性
        var obj = {
            age: 18,
            name: '李曉慧',
            price: Infinity,
        }
        var arr = Object.keys(obj);
        console.log(arr); // 物件的屬性名

        arr.forEach(function (value) {
            console.log(value);
        })

8. 函式進階

8.1 函式的定義和呼叫

8.1.1 函式的定義方式

  1. 函式宣告方式 function 關鍵字(命名函式)
  2. 函式表示式(匿名函式)
  3. neew Function()

var fn = new Function('引數1','引數2',...,'函式體')

  • 裡面的函式體必須是字串形式
  • 第三種方式執行效率低,也不方便書寫,因此較少使用
  • 所有函式都是Function 的例項(物件)
  • 函式也屬於物件

        // 函式的定義方式

        // 1. 自定義函式(命名函式)
        function fn() {};

        // 2. 函式表示式(匿名函式)

        var fun = function () {}

        // 3. 利用 new Function('引數1','引數2','函式體')

        var f = new Function('a', 'b', 'console.log(a+b)');
        f(1, 2);

        // 4. 所有函式都是 Function 的例項(物件)
        console.dir(f);
				// 5. 函式也屬於物件
        console.log(f instanceof Object); // true 這個是判斷前者是否屬於後者,f 屬於物件

8.1.2 函式的呼叫方式

 				// 1. 普通函式
        function fn() {
            console.log('普通函式呼叫方式:函式名.()');
        }
        fn();

        // 2. 物件的方法
        var obj = {
            transfer: function () {
                console.log('物件的方法呼叫方式:物件.函式名()');
            }
        }
        obj.transfer();

        // 3. 建構函式
        function Star() {
            console.log('建構函式的使用方法:new 建構函式名()');
        }
        var fun = new Star();

        // 4. 繫結事件函式
        var btn = document.querySelector('button')
        btn.onclick = function () {
            console.log('繫結事件函式使用方法:1.先獲取元素;2.Element.事件 = 函式;3.點選之後即可呼叫函式');
        } // 點選了之後就可以呼叫

        // 5. 定時器函式
        setInterval(function () {}, 1000); // 這個函式是一秒鐘呼叫一次

        // 6. 立即執行函式
        (function () {
            console.log('立即自行函式呼叫方法:在後面()就可以實現自執行');
        })();
        // 立即執行函式是直接呼叫

8.1.3 this 的指向

// 函式的不同呼叫方式決定了this 的指向不同
        // 1. 普通函式 this 指向window
        function fn() {
            console.log('普通函式的this' + this);
        }
        window.fn();
        // 2. 物件的方法 this指向的是物件 o
        var o = {
            sayHi: function() {
                console.log('物件方法的this:' + this);
            }
        }
        o.sayHi();
        // 3. 建構函式 this 指向 ldh 這個例項物件 原型物件裡面的this 指向的也是 ldh這個例項物件
        function Star() {};
        Star.prototype.sing = function() {

        }
        var ldh = new Star();
        // 4. 繫結事件函式 this 指向的是函式的呼叫者 btn這個按鈕物件
        var btn = document.querySelector('button');
        btn.onclick = function() {
            console.log('繫結時間函式的this:' + this);
        };
        // 5. 定時器函式 this 指向的也是window
        window.setTimeout(function() {
            console.log('定時器的this:' + this);

        }, 1000);
        // 6. 立即執行函式 this還是指向window
        (function() {
            console.log('立即執行函式的this' + this);
        })();

8.1.4 改變函式內this 指向

1. call 方法

JavaScript 為我們專門提供了一些函式方法來幫我們更優雅的處理函式內部this的指向問題,常用的有bind().call().apply()三種方法。

call()方法呼叫一個物件。簡單理解為呼叫函式的方式,但是它可以改變函式的 this指向。

fun.call(thisArg,arg1,arg2,...)

        // 改變函式內this 指向  js 提供了三種方法 call() apply() bind()
        // 1. call()
        var o = {
            name: 'andy'
        }

        function fn(a, b) {
            console.log(this);
            console.log(a + b);
        }

        fn.call(o, 1, 2)
        // call() 第一個可以呼叫函式 第二個可以改變函式內的this 指向
        // call() 的主要作用可以實現繼承

        function Father(uname, age, sex) {
            this.uname = uname;
            this.age = age;
            this.sex = sex;
        }

        function Son( uname, age, sex) {
            Father.call(this, uname, age, sex)
        }
        var son = new Son('唐嫣', 20, '女');
        console.log(son);

2. apply 方法

fun.apply(thisArg,[argsArray])

// 改變函式內this 指向  js 提供了三種方法 call() apply() bind()
        // 2. apply() 方法    應用 運用的意思

        var obj = {
            name: 'marry'
        };

        function fn(arr) {
            console.log(this); // obj 
            console.log(arr); // ['john']
        }
        fn.apply(obj, ['john']);
        // 1. 也是呼叫函式 第二個可以改變函式內部的this 指向
        // 2. 但是他的引數必須是陣列(偽陣列)
        // 3. apply() 的主要應用 比如說我們可以利用apply 藉助於數學內建物件求最大值
        // Math.max()
        var arr = [1, 2, 4, 22, 93, 56];
        // var max = Math.max.apply(null, arr); // null 不改變函式指向  但是null 可能有問題
        var max = Math.max.apply(Math, arr); // 可以指回Math  更加嚴格
        var min = Math.min.apply(Math, arr); // 可以指回Math  更加嚴格
        console.log(max, min);

3. bind 方法(用得較多)

fun.bind(thisArg,[argsArray])

				// 改變函式內this 指向  js 提供了三種方法 call() apply() bind()
        // 3. apply() 方法    捆綁 繫結的意思    不會去呼叫函式
        var obj = {
            name: 'andy'
        };

        function f(a, b) {
            console.log(this);
            console.log(a + b);
        };
        var fn = f.bind(obj, 5, 9)
        fn();
        // 1. 不會呼叫原來的函式,可以改變原來函式內部的this 指向
        // 2. 返回的是原函式改變this 之後的新函式
        // 3. 如果有的函式我們不需要立即呼叫,但是又想改變當前函式的指向此時用bind    
        // 4. 如果我們有一個按鈕,點選之後禁用,三秒之後在開啟
        // var btn = document.querySelector('button');
        // btn.onclick = function () {
        //     this.disabled = true; // 這個函式的this 指向的是當前函式的呼叫者 btn 按鈕
        //     // var that = this;
        //     setTimeout(function () {
        //         // this.disabled = false; // 此時寫this 是沒有用的 定時器函式的this 是window 
        //         // that.disabled = false; // 常規做法  也可把that 手動換成btn
        //         this.disabled = false;
        //     }.bind(this), 3000)
        // }

        var btns = document.querySelectorAll('button');
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function () {
                this.disabled = true;
                setTimeout(function () {
                    this.disabled = false;
                }.bind(this), 2000)
            }
        }

4. 總結

9. 嚴格模式

9.1 什麼是嚴格模式

9.2 開啟嚴格模式

​ 嚴格模式可以應用到整個指令碼或個別函式中,因此在使用中,我們可以將嚴格模式分為為指令碼開啟嚴格模式和為函式開啟嚴格模式兩種情況

1. 為指令碼開啟嚴格模式

​ 為整個指令碼檔案開啟嚴格模式,需要在所有語句之前放一個特定語句"use strict";(或'use strict')

    <script>
        "use strict" // 為整個指令碼檔案開啟嚴格模式
    </script>
    <script>
        (function () {
            'use strict'// 為函式開啟嚴格模式
        })()
    </script>

有的script 基本是嚴格模式,有的script 指令碼是正常模式,這樣不利於檔案合併,所以可以將整個指令碼檔案放在一個立即執行的匿名函式之中。這樣獨立建立一個作用域而不影響其他script 指令碼檔案。

2. 為函式開啟嚴格模式

​ 要給某個函式開啟嚴格模式,需要把"use strict";宣告放在函式體所有語句之前

    <script>
        function fn() {
            'use strict' // 下面的程式碼按照嚴格模式執行
        }

        function fun() {
            // 這裡面的還是按照普通模式執行
        }
    </script>

10. 閉包

10.1 變數作用域

變數根據作用域的不同可以分成兩種:全域性變數和區域性變數

  • 函式內部可以使用全域性變數
  • 函式外部不可以使用區域性變數
  • 當函式執行完畢,本作用域內的變數會銷燬

10.2 什麼是閉包

閉包是指有權訪問另一個函式作用域中變數的函式。

簡單理解就是,一個作用域可以訪問另外一個函式內部的區域性變數

        // 閉包:我們fun 這個函式作用域  訪問了另外一個函式作用域內的區域性變數 
        function fn() {
            var num = 10;

            function fun() {
                console.log(num);
            }
            fun();
        }
        fn();
        // 在一個函式裡面巢狀一個函式,裡面的函式需要用到外面函式定義的變數,將裡面函式的 return 出來
        function inner() {
            var i = 10;

            function outer() {
                i++;
                console.log(i);
            }
            return outer;
        }
        var f = inner(); // 將函式的返回值 賦值給f變數 f變數的值是一個函式
        f(); // 11
        f(); // 12

閉包的主要作用:延伸了變數的作用範圍

11. 遞迴

11.1 什麼是遞迴

如果一個函式可以在內部呼叫其自身,那麼這個函式就是遞迴函式

函式內部自己呼叫自己,這個函式就是遞迴函式

遞迴函式的作用和迴圈效果一樣

由於遞迴很容易發生“棧溢位”錯誤,所以必須要加上退出條件return

        // 遞迴函式:函式內部自己呼叫自己
        var num = 1;

        function fn() {
            console.log('列印幾句話');
            if (num == 6) {
                return; // 遞迴函式必須要加退出條件
            }
            num++;
            fn();
        }
        fn();

12. 淺拷貝和深拷貝

  1. 淺拷貝只是拷貝一層,更深層次物件級別的只拷貝引用
  2. 深拷貝拷貝多層,每一級別的資料都會拷貝
  3. Object.assign(target,...sources)es6新增方法可以淺拷貝

12.1 淺拷貝

        // 1. 淺拷貝只是拷貝一層,更深層次物件級別的只拷貝引用
        // 2. 深拷貝拷貝多層,每一級別的資料都會拷貝
        var obj = {
            id: 1,
            name: 'lxh',
            msg: {
                age: 18
            }
        };
        var o = {};
            // for迴圈賦值也算是一種淺拷貝
        for (var k in obj) {
            // k 是屬性名    obj[k] 屬性值  o.k
            o[k] = obj[k];
        }
        console.log(o);

對於更深層次的資料只是拷貝了地址,兩個物件資料指向同一個地址,原來的物件修改了資料,拷貝過來的資料也會跟著改變。

        // 1. 淺拷貝只是拷貝一層,更深層次物件級別的只拷貝引用
        // 2. 深拷貝拷貝多層,每一級別的資料都會拷貝
        var obj = {
            id: 1,
            name: 'lxh',
            msg: {
                age: 18
            }
        };
        var o = {};
        for (var k in obj) {
            // k 是屬性名    obj[k] 屬性值  o.k
            o[k] = obj[k];
        }
        console.log(o);
        o.msg.age = 20;
        console.log(obj);

        // 1. 淺拷貝只是拷貝一層,更深層次物件級別的只拷貝引用
        // 2. 深拷貝拷貝多層,每一級別的資料都會拷貝
        var obj = {
            id: 1,
            name: 'lxh',
            msg: {
                age: 18
            }
        };
        var o = {};
        // for (var k in obj) {
        //     // k 是屬性名    obj[k] 屬性值  o.k
        //     o[k] = obj[k];
        // }
        Object.assign(o, obj)
        console.log(o);

12.2 深拷貝

深拷貝拷貝多層,每一級別的資料都會拷貝

        // 深拷貝拷貝多層,每一級別的資料都會拷貝
        // 可以用遞迴來做
        var obj = {
            id: 1,
            name: 'lxh',
            msg: {
                age: 18
            },
            color: ['pink', 'red']
        };
        var o = {};
        // 封裝函式
        function deepCopy(newObj, oldObj) {
            for (var k in oldObj) {
                // 判斷我們的資料屬於那種資料型別
                // 1. 獲取屬性值    oldObj[k]
                var item = oldObj[k];
                // 2. 判斷這個值是否是陣列
                // 需要把判斷陣列放在判斷物件的前面,因為陣列也屬於物件
                if (item instanceof Array) {
                    newObj[k] = [];
                    deepCopy(newObj[k], item)
                } else if (item instanceof Object) {
                    // 3. 判斷這個值是否是物件
                    newObj[k] = {};
                    deepCopy(newObj[k], item)
                } else {
                    // 4. 屬於簡單資料型別
                    newObj[k] = item;
                }
            }
        }
        deepCopy(o, obj);
        console.log(o);

        var arr = [];
        console.log(arr instanceof Object);
        o.msg.age = 20;
        console.log(obj);

深拷貝拷貝深層次資料時,會開闢一個新的記憶體空間來存放資料,修改原來資料對拷貝後的資料沒有影響

13. 正規表示式

14. ES6 的新增語法

14.1 let 關鍵字

ES6 中新增的用於變數的關鍵字

  • let 宣告的變數只在所處於的塊級有效(塊級作用域,內部有效)

注意:使用let 關鍵字宣告的變數才具有塊級作用域,使用var 宣告的變數不具備塊級作用域特性

if (true) {
  let a = 10;
  var b = 10;
}
console.log(b); // 10
console.log(a); // a is not defined
  • 防止迴圈變數變成全域性變數(塊級作用域)
// 防止迴圈變數變成全域性變數
// for (var i = 0; i < 3; i++) {}
// console.log(i); // 3
for (let i = 0; i < 3; i++) {}
console.log(i); // i is not defined
  • 不存在變數提升
console.log(a); // a is not defined
let a = 10;
  • 暫時性死區

在塊級作用域內使用let 宣告的變數會被整體繫結在塊級區,不再受外部程式碼影響

var num = 10;
if (true) {
  console.log(num); // num is not defined
  let num = 20;
}

14.2 const 關鍵字

作用:宣告變數,常量就是值(記憶體地址)不能變化的量;

  • 具有塊級作用域,同let 一樣
if (true) {
    const a = 10;
    if (true) {
        const a = 20;
        console.log(a); // 20
        }
        console.log(a); // 10
}
console.log(a); // a is not defined

  • 宣告常量時必須賦值

const PI; // Missing initializer in const declaration

  • 常量賦值後,值不能修改
// const PI = 3.14;
// PI = 10; // Assignment to constant variable.
const arr = [100, 200];
arr[0] = 10;
console.log(arr); // [10,200]
arr = [10, 20];
console.log(arr); // Assignment to constant variable.

// 對於複雜資料型別,可以修改內部值,但是不能重新賦值,重新賦值相當於修改了常量在記憶體中的地址,常量不可以更改地址

對於複雜資料型別,可以修改內部值,但是不能重新賦值,重新賦值相當於修改了常量在記憶體中的地址,常量不可以更改地址

14.3 let const var 的區別

14.4 解構賦值

ES6 中允許從陣列中提取值,按照對應位置,對變數賦值。物件也可以實現解構

1. 陣列解構

陣列解構允許我們按照一一對應的關係從陣列中提取值然後將值賦值給變數

let arr = [1, 2, 3];

// 變數和值是一一對應的
// let [a, b, c] = arr;
// console.log(a); // 1
// console.log(b); // 2
// console.log(c); // 3

// 變數和值不是一一對應
let [a, b, c, d] = arr;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(d); // undefined

2. 物件解構

物件解構允許我們使用變數的名字匹配物件的屬性,匹配成功將物件屬性的值賦值給變數

let person = {
    name: 'lxh',
    age: 18,
}
let {
    name,
    age,
    sex
} = person;
console.log(name); // lxh
console.log(age); // 18
console.log(sex); // undefined

第二種寫法

let person = {
    name: 'lxh',
    age: 18,
}
let {
    name: myName, // myName 相當於變數,接收person 裡面和name 匹配的值
    age: myAge,
    sex: mySex
} = person;
console.log(myName); // lxh
console.log(myAge); // 18
console.log(mySex); // undefined

14.5 箭頭函式

ES6 中新增的定義函式的方式

()=>{} // 定義箭頭函式
// 箭頭函式是用來簡化函式定義語法
// 把箭頭函式賦值給變數
const fn = () => {
  console.log(123);
}
fn(); // 123

函式體中只有一句程式碼,且程式碼的執行結果就是返回值,可以省略大括號

// 函式體中只有一句程式碼,且程式碼的執行結果就是返回值,可以省略大括號
// function sum(num1, num2) {
//     return num1 + num2;
// }
// console.log(sum(1, 2)); // 3


// 箭頭函式 省略大括號寫法
const sum = (num1, num2) => num1 + num2;
console.log(sum(1, 2)); // 3

如果形參只有一個,可以省略小括號

// 如果形參只有一個,可以省略形參外面的小括號
// function sum(num1) {
//     return num1;
// }
// console.log(sum(1)); // 1

// 省略小括號寫法
const sum = num1 => num1
console.log(sum(1)); // 1

箭頭函式不繫結this 關鍵字,箭頭函式中的this,指向的時函式定義位置的上下文this

function fn() {
    console.log(this); // {name: 'lxh'}
    return () => {
        console.log(this); // {name: 'lxh'}
    }
}
const obj = {
    name: 'lxh'
}
fn.call(obj)();

一個面試題

var obj = {
    age: 18,
    say: () => {
        console.log(this.age); // undefined 
        // 此時的this 指向的是window     obj 是一個物件,沒有作用域,箭頭函式的this 指向的是上下文中的this,指向的是window
    }
}

obj.say();
var name = 'lxh';
var obj1 = {
    name: 18,
    say: () => {
        console.log(this.name); // lxh 此時this 指向的是window,因為變數name 是全域性變數,所以可以輸出 
    }
}
obj1.say();

14.6 剩餘引數

剩餘引數語法允許我們將一個不定數量的參數列示為一個陣列

const sum = (...args) => { // ...args 接收所有的引數,箭頭函式沒有arguments 屬性
    let total = 0;
    args.forEach(item => total += item);
    return total
}
console.log(sum(10, 20)); // 30
console.log(sum(10, 20, 30)); // 60

剩餘引數和解構配合使用

// 剩餘引數與解構一起使用
let arr = ['lxh', 'ljm', 'sxm'];
let [s1, ...s2] = arr;
console.log(s1); // lxh
console.log(s2); // ['ljm', 'sxm']

14.6 ES6 的內建物件擴充套件

1. Array 的擴充套件方法

1.1 擴充套件運算子...args

擴充套件運算子可以將陣列或物件轉為用逗號分隔的引數序列

// 擴充套件運算子可以將陣列拆分成以逗號分隔的引數序列
let arr = ['1', '2', '3'];
// ...arr // '1', '2', '3'
console.log(...arr); // 逗號在log 中被當作引數分隔符,不會顯示在結果中
console.log('1', '2', '3');

擴充套件運算子可以應用於合併陣列

// 擴充套件運算子可以用來合併陣列
let arr1 = ['1', '2', '3'];
let arr2 = ['4', '5', '6'];
let arr3 = [...arr1, ...arr2]
console.log(...arr3); // 1 2 3 4 5 6
console.log(arr3); // ['1', '2', '3', '4', '5', '6']

// 合併陣列方法2
arr1.push(...arr2)
console.log(arr1); // ['1', '2', '3', '4', '5', '6']

將類陣列或可遍歷物件轉換為真正的陣列

// 將類陣列或可遍歷物件轉換為真正的陣列
var div = document.querySelectorAll('div');
var arr01 = [...div];
console.log(arr01); // [div, div, div, div, div, div]

1.2 建構函式方法:Array.from()

建構函式方法:Array.from() 該方法的返回值是一個陣列

var obj = {
    '0': 'lxh',
    '1': 'ljm',
    '2': 'hs',
    'length': 3
}
// 該方法的返回值是一個陣列
var ary = Array.from(obj);
console.log(ary); // ['lxh', 'ljm', 'hs']

Array.from() 還可以傳遞第二個引數,第二個引數可以用函式對要被處理的物件的引數進行操作

// Array.from()    還可以傳遞第二個引數,第二個引數可以用函式對要被處理的物件的引數進行操作
var obj1 = {
    '0': '1',
    '1': '2',
    'length': 2
}
var arr = Array.from(obj1, item => item * 2)
console.log(arr);

1.3 find() 方法

find() 方法 用於找出第一個符合條件的陣列成員,如果沒有找到返回undefined

// find() 方法  用於找出第一個符合條件的陣列成員,如果沒有找到返回undefined
var arr = [{
    id: 1,
    name: 'lxh'
}, {
    id: 2,
    name: 'ljm'
}];
var obj = arr.find(item => item.id == 2);
var obj1 = arr.find(item => item.id == 3);
console.log(obj); // {id: 2, name: 'ljm'}
console.log(obj1); // undefined

1.4 findIndex() 方法

findIndex() 方法 查詢符合條件的第一個元素,返回當前元素的索引,如果沒有就返回-1

// findIndex() 方法 查詢符合條件的第一個元素,返回值為當前元素的索引,如果沒有就返回-1
var arr = [0, 5, 10, 15, 20, 35];
var i = arr.findIndex(item => item > 10);
var j = arr.findIndex(item => item > 40);
console.log(i); // 3
console.log(j); // -1

1.5 includes() 方法

includes() 方法 表示某個陣列是否包含給定的值,返回布林值

// includes() 方法 表示某個陣列是否包含給定的值,返回布林值
var arr = [1, 2, 3];
console.log(arr.includes(2)); // true
console.log(arr.includes(4)); // false

2. String 的擴充套件方法

1. 模板字串

模板字串中可以解析變數

// 模板字串中可以解析變數
let name = `張三`;
let sayHello = `hello,我叫${name}`;
console.log(sayHello); // hello,我叫張三

模板字串中可以換行

// 模板字串中可以換行
let result = {
    name: 'lxh',
    age: 18
}
let html = `
<div>
    <span>${result.name}</span>
    <span>${result.age}</span>
</div>`;
console.log(html);

模板字串可以呼叫函式

// 模板字串可以呼叫函式
const Hello = function () {
    return 'hello';
};
let greet = `${Hello()},你好`;
console.log(greet);

2.例項方法

2.1 startsWith() 和endsWith()

startsWith():表示引數字串是否在原字串的頭部,返回布林值

endsWith():表示引數字串是否在原字串的尾部,放回布林值

let str = 'Hello World!';
if (str.startsWith('Hello')) {
    console.log('以Hello開頭');
    if (str.endsWith('!')) {
        console.log('以!結尾');
    }
}

2.2 repeat()

repeat方法將原字串重複n 次,返回一個新字串

let str = 'Who are you';
let str01 = str.repeat(3);
console.log(str01);
console.log('abc'.repeat(3));

3. Set 資料結構

ES6 提供了新的資料結構Set ,它類似與陣列,但是成員的值都是唯一的,沒有重複的值

Set 本身是一個建構函式,用來生產Set 資料結構。

const s = new Set();

Set 函式可以接受一個陣列作為引數,用來初始化。

const set = new Set([1,2,3,4,5]);

const arr1 = new Set();
console.log(arr1.size); // 0     
const arr2 = new Set([1, 2, 3, 4, 5]);
console.log(arr2.size); // 5
const arr3 = new Set([1, 1, 3, 4, 4]);
console.log(arr3.size); // 3
// size 表示不同元素的個數,相同的元素只記作1,會把重複的元素 過濾掉

例項方法

  • add(value):新增某個值,返回Set 結構本身,一次只能新增一個引數,可以多次新增
  • delete(value):刪除某個值,返回一個布林值,表示刪除是否成功
  • has(value):返回一個布林值,表示該值是否為Set 的成員
  • clear():清除所有成員,沒有返回值
const s1 = new Set([1]);
// 新增一個值
s1.add(2, ).add(3);
console.log(s1, s1.size);

// 刪除一個沒有的值 返回false
console.log(s1.delete(4)); // false
console.log(s1, s1.size);

// 刪除存在的成員
console.log(s1.delete(3)); // true
console.log(s1, s1.size);

// 判斷該值是否為Set 的成員
console.log(s1.has(1)); // true
console.log(s1.has(4)); // false

// 清除Set 所有成員
// s1.clear();

遍歷

Set結構的例項與陣列一樣,也擁有forEach方法,用於對每個成員執行某種操作,沒有返回值

s.forEach(value => console.log(value))

const s1 = new Set([1, 2]);
// 遍歷Set 資料結構
s1.forEach(element => {
    console.log(element);
});

相關文章