一、塊級作用域系結
-
回顧:使用var關鍵字定義變數
2. 定義時可以只宣告不賦值
3. 定義之后可以隨時修改變數的值
4. 變數宣告會被提升
5. 可重復定義變數
6. 全域定義的變數會被作為全域物件(global/window)的屬性
7. 在代碼塊中使用 var 關鍵字宣告的變數不會被束縛在代碼塊中
1 if (true) { 2 3 var foo, bar = 'abc'; 4 } 5 6 console.log(foo, bar)
1.1 使用let關鍵字定義變數
- 可以一次定義多個變數
- 定義時可以只宣告不賦值
- 定義之后可以隨時修改變數的值
- 使用 let 關鍵字定義的變數,變數宣告不會被提升,因此我們需要先定義,后使用
- 在同一作用域下,不能重復定義同名變數
- 全域定義的變數會被作為全域物件(global/window)的屬性
- 在代碼塊中使用 let 關鍵字宣告的變數會被束縛在代碼塊中
1 // 0. 可以一次定義多個變數 2 // let foo = 123, bar = 'abc'; 3 4 // 1. 定義時可以只宣告不賦值 5 // let foo, bar; 6 7 8 // 2. 定義之后可以隨時修改變數的值 9 // let foo = 123, bar = 'abc'; 10 // foo = 'abc'; 11 // bar = 123; 12 // console.log(foo, bar); // 'abc' 123 13 14 // 3. 使用 let 關鍵字定義的變數,變數宣告不會被提升,因此我們需要先定義,后使用 15 // console.log(foo); // foo is not undefined 16 // let foo = 123; 17 18 19 // 4. 在同一作用域下,不能重復定義同名變數 20 // let foo = 123; 21 // let foo = 'abc'; // Identifier 'foo' has already been declared 22 // console.log(foo) 23 24 // 5. 全域定義的變數會被作為全域物件(global/window)的屬性 25 26 // 6. 在代碼塊中使用 let 關鍵字宣告的變數會被束縛在代碼塊中
1.2 使用const關鍵字定義常量
1. 在使用 const 關鍵字宣告常量時,必須要進行賦值(初始化),
2. 常量一旦初始化后,就不能被修改,
3. 在同一作用域下,不能重復定義同名的常量,
4. 常量的宣告不會被提升
5. 所有常量只在當前代碼塊內有效,一旦執行流到了代碼塊外,這些常量就會被立即銷毀,
6.不在Windows下
1 <script> 2 3 // 1. 在使用 const 關鍵字宣告常量時,必須要進行賦值(初始化), 4 // const foo = 123, bar = 'abc'; 5 // const bar; // 拋出錯誤:Missing initializer in const declaration 6 7 // 2. 常量一旦初始化后,就不能被修改, 8 // const foo = 123; 9 // foo = 'abc'; // 拋出錯誤:Assignment to constant variable. 10 11 // 3. 在同一作用域下,不能重復定義同名的常量 12 // const foo = 123; 13 // const foo = 'abc'; // 拋出錯誤:Identifier 'foo' has already been declared 14 15 // 4. 常量的宣告不會被提升 16 // console.log(foo) // 拋出錯誤:foo is not defined 17 // const foo = 123; 18 19 // 5. 所有常量只在當前代碼塊內有效,一旦執行流到了代碼塊外,這些常量就會被立即銷毀, 20 // if (true) { 21 // const foo = 123; 22 // console.log(foo) 23 // } 24 // console.log(foo) // 拋出錯誤:foo is not defined 25 26 </script>
1.3 模塊字面量
- 插入資料
1 <script> 2 3 let str1 = '你好,世界!'; 4 let str2 = `Hello 5 World!`; // 可以直接換行 6 let str3 = `Hello ${str1} World!`; // 向模板中插入資料 7 console.log(str3) 8 // 使用場景: 9 10 let students = [ 11 { 12 name: '黃聰聰', 13 age: 20, 14 gender: '男' 15 }, 16 { 17 name: '趙志剛', 18 age: 20, 19 gender: '男' 20 }, 21 { 22 name: '江蘆貴', 23 age: 20, 24 gender: '男' 25 } 26 ]; 27 28 let htmls = ''; 29 30 students.forEach(function(student) { 31 htmls += `<tr> 32 <td>${student.name }</td> 33 <td>${student.age }</td> 34 <td>${student.gender}</td> 35 </tr>`; 36 }); 37 38 console.log(htmls) 39 40 41 </script>
1.4 展開運算子 (...)
- 給變數前面加三個點(...),就不再列印出陣列格式了,(如控制臺列印的一二行,第一行是使用展開運算子,第二行是沒有使用展開運算子的)
- 使用場景:a.合并陣列 b.復制陣列
1 <script> 2 3 let colors1 = ['red', 'green', 'blue']; 4 5 // 使用展開運算子輸出陣列中的元素,類似于:console.log('red', 'green', 'blue') 6 console.log(...colors1) 7 console.log(colors1) //物件 8 9 // 使用場景一:合并陣列,將 colors1 中的元素合并到 colors2 中 10 let colors2 = ['black', 'white', 'purple', ...colors1]; 11 console.log(...colors2) 12 // 使用場景二:復制陣列 13 let colors3 = [...colors2] 14 console.log(...colors3) 15 </script>

1.5 剩余運算子(又名不定引數)
不定引數:顧名思義,不知道還有多少個引數,剩余的統統接收,用來代替arguments;
- 函式中剩余運算子只能是函式中最后的一個形參,格式:...bar;
- 第一個實參會被賦值給第一個形參,剩余所有的實參都會被交給形參 bar,bar會自動變成一個陣列;
1 <script> 2 // 實參 12 會被賦值給形參 a,剩余的所有實參都會被交給形參 bar,bar 會自動變成一個陣列, 3 function foo (a, ...bar) { 4 console.log(a, bar) 5 } 6 7 foo(12, 34, 45, 67, 89) 8 // 注意,剩余運算子只能應用到最后一個形參上,否則會拋出錯誤:Rest parameter must be last formal parameter 9 // 下面的寫法是錯誤的: 10 // function foo (a, ...bar, b) { 11 // console.log(a, bar, b) 12 // } 13 14 // foo(12, 34, 45, 67, 89) 15 </script>

二、解構
2.1解構的實用性
在 ECMAScript 5 或更早的版本中,從物件或陣列中獲取特定的資料并賦值給本地變數需要書寫很多并且相似的代碼,例如:
1 let options = { 2 repeat: true, 3 save: false 4 }; 5 6 // 從物件中提取資料 7 8 let repeat = options.repeat, 9 save = options.save;
這段代碼反復地提取在 options 上存盤地屬性值并將它們傳遞給同名的本地變數,雖然這些看起來不是那么復雜,不過想象一下如果你的一大批變數有著相同的需求,你就只能一個一個地賦值,而且,如果你需要從物件內部嵌套的結構來查找想要的資料,你極有可能為了一小塊資料而訪問了整個資料結構,
這也是 ECMAScript 6 給物件和陣列添加解構的原因,當你想要把資料結構分解為更小的部分時,從這些部分中提取資料會更容易些,很多語言都能使用精簡的語法來實作解構操作
2.2 物件解構 -解構賦值 ( { } 花括號)
- 宣告的變數是同名變數,寫在{}里即可,變數之間用“,”隔開就行;
- 宣告的變數是非同名變數,在{}里寫上" “:”+ “變數名” " ,變數之間用“,”隔開 例如:
let { firstName: first_name, lastName: last_name } = obj; - 為變數指定默認值,當物件中沒有某個屬性時,直接寫上變數的名字加“=”即可, 例如:
let { firstName, lastName, myAge = 20 } = obj;let { firstName: first_name, lastName: last_name, myAge: my_age = 20} = obj; - 為已有變數重新賦值,({}=obj),因為JS決議引擎不允許操作賦值符(=)左邊出現花括號所以使用 "()"將整個賦值陳述句包起來,使他成為一個運算式
- 嵌套物件解構,(俄羅斯套娃,哈哈~~)例如:obj里面的phone里面的number賦值給同名變數number
let { phone: { number, brand } } = obj;
1 <script> 2 3 const obj = { 4 firstName: '張康', 5 lastName: '尼古拉斯', 6 myAge: 30, 7 phone: { 8 number: 110, 9 brand: '魅族', 10 color: '黑色' 11 } 12 }; 13 14 // 1. 使用物件解構初始化同名變數 15 // 告訴 obj 物件,把 firstName 屬性的值賦值給同名變數 firstName,把 lastName 屬性的值賦值給同名變數 lastName 16 // let { firstName, lastName } = obj; 17 // console.log(firstName, lastName) // '張康' '尼古拉斯' 18 19 // 2. 使用物件解構初始化非同名變數 20 // 告訴 obj 物件,把 firstName 屬性的值賦值給變數 first_name,把 lastName 屬性的值賦值給變數 last_name 21 22 // let { firstName: first_name, lastName: last_name } = obj; 23 // console.log(first_name, last_name) // '張康' '尼古拉斯' 24 25 // 3. 為變數指定默認值 26 // 當 obj 物件中沒有 myAge 屬性時,變數 myAge 默認會被賦值為 20 27 // let { firstName, lastName, myAge = 20 } = obj; 28 // console.log(firstName, lastName, myAge) // '張康' '尼古拉斯' 20 29 30 // 當 obj 物件中沒有 myAge 屬性時,變數 my_age 默認會被賦值為 20 31 // let { firstName: first_name, lastName: last_name, myAge: my_age = 20} = obj; 32 // console.log(first_name, last_name, my_age) // '張康' '尼古拉斯' 20 33 34 // 4. 使用物件解構為已有變數重新賦值 35 // let first_name = '郭帥', last_name = '萊昂納多'; 已有變數 36 37 // JS 決議引擎不允許賦值運算子(=)左邊出現花括號,但是可以使用小括號將整個賦值陳述句包裹起來,這樣一來,整個賦值陳述句就變成了一個運算式, 38 // ({ firstName: first_name, lastName: last_name } = obj); 重新賦值 39 // console.log(first_name, last_name) // '張康' '尼古拉斯' 40 41 // 5. 嵌套物件解構 42 // 告訴 obj 物件,把 phone 物件中的 number 屬性的值賦值給變數 number,把 phone 物件中的 brand 屬性的值賦值給變數 brand 43 // let { phone: { number, brand } } = obj; 44 // console.log(number, brand) 45 46 </script>
2.3 陣列解構([ ]中括號)
- 陣列解構初始化變數:陣列中的元素會按照順序賦值給變數
- 使用陣列解構為變數重新賦值,[ ]=colors,
[ firstColor, secondColor ] = colors;我們 - 為變數指定默認值,當陣列中沒有第四個元素,而我們又需要第四個元素時,我們可以給變數賦值,但是當元素中有第四個元素,我們再給第四個元素賦值是無效的;
- 跳過陣列中的指定元素,當我們不需要第二,三個元素,又需要第四個元素,中間可以用“ ,”隔開,
- 嵌套解構前面不需要的資料用 “ ,”隔開,嵌套的用“ [ ]”l來表示
1 <script> 2 3 const colors = ['red', 'green', 'blue', 'orange', [ 'black', 'white' ] ]; 4 5 // 1. 使用陣列解構初始化變數 6 // 陣列中的元素會按照順序賦值給這 3 個變數 7 // let [ firstColor, secondColor, thirdColor ] = colors; 8 // console.log(firstColor, secondColor, thirdColor) 9 10 // 2. 使用陣列解構為變數重新賦值 11 // let firstColor = 'black', secondColor = 'white'; 12 // 陣列中的元素會按照順序賦值給這 2 個變數 13 // [ firstColor, secondColor ] = colors; 14 // console.log(firstColor, secondColor) 15 16 // 3. 為變數指定默認值 17 // 當陣列中沒有第 4 個元素時,變數 fourthColor 會被賦值成默認值 'pi但是nk' 18 // let [ firstColor, secondColor, thirdColor, fourthColor = 'pink' ] = colors; 19 // console.log(firstColor, secondColor, thirdColor, fourthColor) 20 21 // 4. 跳過陣列中的指定元素 22 // let [ firstColor, , ,fourthColor ] = colors; 23 // console.log(firstColor, fourthColor) 24 25 // 5. 嵌套陣列解構 26 let [,,,,[nestedFirstColor, nestedSecondColor]] = colors; 27 console.log(nestedFirstColor, nestedSecondColor) 28 29 30 </script>
2.4 混合解構
1 <script> 2 3 let node = { 4 type: 'Identifier', 5 name: 'foo', 6 loc: { 7 start: { 8 line: 1, 9 column: 1 10 }, 11 end: { 12 line: 4, 13 column: 4 14 } 15 }, 16 range: [0, 3] 17 }; 18 19 // 把 node 物件中 loc 物件的 start 屬性的值賦值給變數 start,把 range 陣列中第 2 個元素賦值給變數 endIndex 20 let { loc: { start }, range: [, endIndex] } = node; 21 22 console.log(start, endIndex) 23 24 </script>
2.5 引數解構
1 <script> 2 // 1. 以普通方式傳參時,相當于把 obj 物件賦值給形參 student 3 // student = obj 4 5 // function getFullName (student) { 6 // return student.firstName + ' · ' + student.lastName; 7 // } 8 9 // let obj = { 10 // firstName: '劉旭凱', 11 // lastName: '約翰尼' 12 // }; 13 14 // console.log(getFullName(obj)) 15 16 17 // 2. 以物件解構的方式傳參 18 // 把 obj 物件中的 firstName 屬性的值傳遞給形參 firstName,把 lastName 屬性的值傳遞給形參 lastName 19 // { firstName, lastName } = obj; 20 21 // function getFullName ({ firstName, lastName }) { 22 // return firstName + ' · ' + lastName; 23 // } 24 25 // let obj = { 26 // firstName: '劉旭凱', 27 // lastName: '約翰尼' 28 // }; 29 30 // console.log(getFullName(obj)) 31 32 // 3. 為形參指定默認值 33 // 當 obj 物件沒有 lastName 屬性時,形參 lastName 就會使用默認值 '尼古拉斯' 34 function getFullName ({ firstName, lastName = '尼古拉斯' }) { 35 return firstName + ' · ' + lastName; 36 } 37 38 let obj = { 39 firstName: '劉旭凱', 40 lastName: '克里斯蒂安' 41 }; 42 43 console.log(getFullName(obj)) 44 45 46 </script>
三、函式
3.1 默認引數
ES5 語法中,為函式形參指定的默認值寫法
1 <script> 2 // 1. 在 ES5 語法中,為函式形參指定默認值的寫法: 3 // 寫法一: 4 function foo (bar) { 5 bar = bar || 'abc'; 6 console.log(bar) 7 } 8 foo('xyz') 9 10 // 寫法二: 11 function foo (bar) { 12 if (typeof bar === 'undefined') { 13 bar = 'abc'; 14 } 15 console.log(bar) 16 } 17 18 foo('xyz'); 19 20 21 22 </script>
ES6中為函式形參指定默認值
a.除了為形參直接指定默認值以外,形參的默認值還可以是運算式,例如,timeout = 5 * 1000
b.在預編譯階段,形參運算式不會執行,只有在調函函式,并且沒有為形參傳遞實參的情況下才執行
1 <script> 2 // 2. 使用 ES6 的語法為函式形參指定默認值 3 function post (url, data = {}, timeout = foo * 1000) { 4 console.log(arguments) 5 console.log(url, data, timeout) 6 } 7 8 // post('xyz', {uname: 'zhangsan', upass: '123'}, 3000); 9 post('xyz', null, 3000); 10 11 // 注意事項: 12 // 1> 除了為形參直接指定默認值以外,形參的默認值還可以是運算式,例如,timeout = 5 * 1000 13 // 2> 在預編譯階段,形參運算式不會執行,只有在調函函式,并且沒有為形參傳遞實參的情況下才執行, 14 15 </script>
3.2 不定引數
1 <script> 2 3 // 不定引數,使用剩余運算子接收剩余的實參,這些實參會被保存到一個不定引數(args)中 4 function foo (...args) { 5 return args.reduce(function (previousValue, currentValue) { 6 console.log(previousValue, currentValue) 7 return previousValue += currentValue; 8 }) 9 } 10 11 // 將上面的函式改成箭頭函式的形式 12 var foo = (...args) => args.reduce((a, b) =>a += b) 13 14 15 console.log(foo(1, 32, 34, 5, 6)) 16 17 </script>
previousValue:上一個值
currentValue:當前的值
reduce()方法:
1.接收一個函式作為累加器,將陣列元素計算為一個值(從左到右),
2.需要接收四個引數( 必需:1.acc 累加器 2.cur 當前值 ;可選:1.idx 當前索引 2 .src 源陣列)
3.3 箭頭函式
凡是以后遇到匿名函式都可以使用箭頭函式
省略等號和function
- 如果形引數量為 0,則必須加上小括號,箭頭后面的運算式的結果會被作為函式的回傳值,
-
如果形參的數量為 1,則可以省略小括號,
-
如果形引數量大于 1,則不能省略小括號,
-
如果函式的執行體比較簡單(直接量或運算式),可以省略大括號,箭頭后面的直接量或運算式會被自動作為回傳值
-
如果函式的執行體比較復雜,則不能省略大括號,
1 <script> 2 // 1. 形式一: 3 // var foo = function () { 4 // return 'Hello World!'; 5 // }; 6 7 // 如果形引數量為 0,則必須加上小括號,箭頭后面的運算式的結果會被作為函式的回傳值, 8 // var foo = () => { 9 // return 'Hello World!'; 10 // } 11 12 // 2. 形式二: 13 // var foo = function (greeting) { 14 // return greeting; 15 // } 16 17 // 如果形參的數量為 1,則可以省略小括號, 18 // var foo = greeting => { 19 // return greeting; 20 // } 21 22 // 3. 形式三: 23 // var foo = function (firstName, lastName) { 24 // return firstName + ' · ' + lastName; 25 // } 26 27 // 如果形引數量大于 1,則不能省略小括號, 28 // var foo = (firstName, lastName) => { 29 // return firstName + ' · ' + lastName; 30 // } 31 32 // 4. 形式四: 33 // var foo = function (a, b) { 34 // return a > b ? a : b; 35 // } 36 37 // 如果函式的執行體比較簡單(直接量或運算式),可以省略大括號,箭頭后面的直接量或運算式會被自動作為回傳值, 38 // var foo = (a, b) => a > b ? a : b; 39 40 // 5. 形式五: 41 // var foo = function (a, b) { 42 // let max = a; 43 // if (b > a) { 44 // max = b; 45 // } 46 // return max; 47 // } 48 49 // 如果函式的執行體比較復雜,則不能省略大括號, 50 // var foo = (a, b) => { 51 // let max = a; 52 // if (b > a) { 53 // max = b; 54 // } 55 // return max; 56 // } 57 // console.log(foo('賈樹華', '尼古拉斯')) 58 59 60 61 </script>
3.4 箭頭函式沒有this系結
-
普通函式作用域中的 this 已經被系結成 window 物件,因此當我們放問 this 時,直接在當前作用域下就能訪問的到,
-
箭頭函式的作用域中沒有系結 this,因此,當我們訪問 this 時,會去上一層作用域中查找 this,
1 <script> 2 3 // 普通函式作用域中的 this 已經被系結成 window 物件,因此當我們放問 this 時,直接在當前作用域下就能訪問的到, 4 // var foo = function () { 5 // console.log(this) 6 // return 'Hello World!'; 7 // }; 8 9 // 箭頭函式的作用域中沒有系結 this,因此,當我們訪問 this 時,會去上一層作用域中查找 this, 10 // var bar = () => { 11 // console.log(this) 12 // return 'Hello World!'; 13 // }; 14 15 16 // 示例,由于 sayName 箭頭函式中沒有系結 this,因此我們訪問 this 時,會去全域作用域中查找, 17 // 查找的結果是 this 指向 window,因此輸出的 name 的值是 '常軍輝' 18 19 // var name = '常軍輝'; 20 // var obj = { 21 // name: '呂鑫洋', 22 // sayName: function () { 23 // // this = obj 24 // return this.name; 25 // } 26 // }; 27 28 // console.log(obj.sayName()) 29 30 31 32 </script>
3.5 箭頭函式沒有arguments系結
1 <script> 2 3 // 普通函式 4 var foo = function (greeting) { 5 console.log(arguments) 6 return greeting; 7 }; 8 9 // 箭頭函式中沒有系結 arguments 物件,因此下面的輸入陳述句會報錯:arguments is not defined 10 var bar = (greeting) => { 11 console.log(arguments) 12 return greeting;0 13 }; 14 15 console.log(foo('Hello World!')) 16 console.log(bar('你好世界!')) 17 18 </script>
3.6 箭頭函式中不能手動系結this
1 <script> 2 3 // this 指向的物件 4 var obj = { 5 fullName: '譚文華' 6 }; 7 8 // 1. 普通函式,可以使用 call() 方法改變函式中 this 的系結 9 // var foo = function (greeting) { 10 // return this.fullName + '說:' + greeting; 11 // }; 12 // console.log(foo.call(obj, 'Hello World!')) 13 14 15 // 2. 箭頭函式,不能使用 call() 方法改變函式中 this 的系結,箭頭函式中不能系結 this, 16 var bar = (greeting) => { 17 return this.fullName + '說:' + greeting; 18 }; 19 20 // 下面的代碼不會報錯,但是也不起作用 21 console.log(bar.call(obj, '你好世界!')) 22 23 </script>
3.7 new.target
1 <script> 2 // 1. ECMAScript 5 中判斷建構式是否通過 new 關鍵字呼叫的寫法 3 // function Person (fullName) { 4 // if (this instanceof Person) { 5 // this.fullName = fullName; 6 // } else { 7 // return new Person(fullName); 8 // } 9 // } 10 11 // let student = Person('孟天樂') 12 13 // 2. ECMASript 6 引入一個 new.target 屬性,當我們使用 new 運算子呼叫建構式時,new.target 屬性的值為建構式,否則為 undefined 14 // function Person (fullName) { 15 // if (typeof new.target !== 'undefined') { 16 // this.fullName = fullName; 17 // } else { 18 // throw new Error('必須通過 new 關鍵字來呼叫 Person,'); 19 // } 20 // } 21 // let student = new Person('孟天樂'); 22 // console.log(student) 23 24 // 3. 除此之外,還可以檢查 new.target 是否被某個特定建構式所有呼叫, 25 // 例如,Person 建構式中的 new.target 屬性的值被限定為 Person 26 // function Person (fullName, age) { 27 // if (typeof new.target === Person) { 28 // this.fullName = fullName; 29 // this.age = age; 30 // } else { 31 // throw new Error('必須通過 new 關鍵字來呼叫 Person,'); 32 // } 33 // } 34 35 // function Dog (fullName, age) { 36 // Person.call(this, fullName, age) 37 // } 38 39 // let dog = new Dog('HeHe', 3) 40 41 // console.log(dog) 42 43 // 4. 不能在函式外部使用 new.target,否則會報錯 44 function Person () { 45 console.log(new.target) 46 } 47 48 // 下面代碼會拋出錯誤:new.target expression is not allowed here 49 // console.log(new.target) 50 51 let student = new Person('崔景龍') 52 53 </script>
四、物件
4.1 物件-屬性初始值的簡寫
- 當一個物件的屬性與本地變數同名時,不需要再寫冒號和值,直接寫屬性名即可
1 <script> 2 3 let fullName = '楊柯', age = 19; 4 5 let obj = { 6 fullName: fullName, 7 age: age 8 }; 9 10 // 1. 當一個物件的屬性與本地變數同名時,不需要再寫冒號和值,直接寫屬性名即可, 11 let obj = { 12 fullName, 13 age 14 }; 15 16 17 </script>
4.2 物件方法的簡寫
1 <script> 2 3 4 // 在 ES 5 中,如果為物件添加方法,必須要通過指定名稱并完整定義函式來實作, 5 let obj = { 6 fullName: '楊柯', 7 sayName: function () { 8 return this.fullName; 9 } 10 }; 11 12 // 在 ES 6 中,語法更簡潔,取消了冒號和 function 關鍵字,如下所示: 13 let obj = { 14 fullName: '楊柯', 15 sayName () { 16 return this.fullName; 17 } 18 }; 19 20 21 </script>
4.3 可計算屬性名
1 <script> 2 3 // 在物件字面量中使用方括號表示該屬性名是可計算的,方括號中的內容會被計算求值,最終轉化成一個字串,該字串就是最終的屬性名, 4 let suffix = 'name'; 5 6 let person = { 7 ['first' + suffix]: '楊帥', 8 ['last' + suffix]: '泰坦尼' 9 }; 10 11 console.log(person) 12 13 </script>
4.4 物件 新增的兩個方法
1 <script> 2 3 4 // 1. 在有些情況下,既是全等運算子比較出來的結果也是不正確的,例如,在下面兩種情況下: 5 6 // +0 和 -0 在 JS 決議引擎中被表示為兩個完全不同的物體,而如果使用全等運算子(===)對兩者進行比較,得到的結果是兩者相等, 7 console.log(+0 == -0); // true 8 console.log(+0 === -0); // true 9 console.log(Object.is(+0, -0)); // false 10 11 // NaN 和 NaN 在 JS 決議引擎中被表示為兩個完全相同的物體,但是無論使用等于(==)還是全等(===),得到的結果都是 false, 12 console.log(NaN == NaN); // false 13 console.log(NaN === NaN); // false 14 console.log(Object.is(NaN, NaN)); // true 15 16 // 在大多數情況下,Object.is() 方法的比較結果與全等運算子完全相同,唯一的區別在于 +0 和 -0 會被識別為不相等,NaN 和 NaN 會被識別為相等, 17 18 // 2. Object.assign() 方法可以接收任意數量的源物件(obj2,obj3),并按照指定的順序將屬性復制到接收物件(obj1), 19 // 如果多個源物件具有同名屬性,則排位靠后的源物件會覆寫排外靠前的物件, 20 21 let obj1 = { 22 fullName: '陳世壯', 23 sayName () { 24 return this.fullName; 25 } 26 }; 27 28 let obj2 = { 29 fullName: '任俊玖', 30 age: 20 31 }; 32 33 let obj3 = { 34 fullName: '朱亞鵬', 35 gender: '男' 36 }; 37 38 // 通過自定義方法實作了一個可以合并多個物件的方法, 39 // function mixin(receiver, ...suppliers) { 40 // suppliers.forEach(supplier => { 41 // Object.keys(supplier).forEach(key => { 42 // receiver[key] = supplier[key] 43 // }) 44 // }) 45 // return receiver; 46 // } 47 // console.log(mixin(obj1, obj2, obj3)) 48 49 // 使用 ES6 新增 Object.assgin() 方法將多個物件的屬性合并到第一個物件中, 50 // Object.assign(obj1, obj2, obj3); 51 52 // console.log(obj1) 53 // console.log(obj2) 54 // console.log(obj3) 55 56 淺拷貝:(拷貝的是地址)參考型別拷貝的都是地址
// 使用 ES6 新增 Object.assgin() 方法將多個物件的屬性合并到第一個物件中, let obj1={ hobby:['eat', 'sleep'] } let obj2=Object.assign({},obj1); obj2.hobby[0]='play' console.log(obj2); console.log(obj1);
57 58 </script>
4.5 重復的物件字面量屬性
1 <script> 2 3 // 對于每一組重復屬性,都會選取最后一個取值, 4 let obj = { 5 fullName: '陳世壯', 6 fullName: '李忠易', 7 age: 18, 8 age: 20 9 }; 10 11 console.log(obj.fullName); // '李忠易' 12 console.log(obj.age); // 20 13 14 </script>
4.6 自有屬性列舉順序
1 <script> 2 3 // ES 5 中并沒有定義物件屬性的列舉順序,有 JavaScript 引擎廠商自行決定, 4 // ES 6 中明確規定了物件的自有屬性被列舉時的回傳順序, 5 // 自有屬性列舉順序的基本規則: 6 // 1. 所有數字按升序 7 // 2. 所有字串按照它們被加入物件時的順序排序 8 9 let obj = { 10 a: 1, 11 0: 2, 12 c: 3, 13 2: 4, 14 b: 5, 15 1: 6 16 }; 17 18 console.log(Object.getOwnPropertyNames(obj)); // ["0", "1", "2", "a", "c", "b"] 19 20 </script>
4.7 改變物件的屬性
1 <script> 2 3 let person = { 4 getGreeting () { 5 return 'Hello'; 6 } 7 }; 8 9 let dog = { 10 getGreeting () { 11 return 'woof'; 12 } 13 }; 14 15 // 使用 create() 方法將 person 物件作為原型物件 16 let friend = Object.create(person); // {} 17 console.log(friend.getGreeting()); // 'Hello' 18 console.log(Object.getPrototypeOf(friend) === person); // true 19 20 // 使用 setPrototypeOf() 方法將 friend 物件的原型物件修改成 dog 21 Object.setPrototypeOf(friend, dog); 22 console.log(friend.getGreeting()); // 'Hello' 23 console.log(Object.getPrototypeOf(friend) === dog); // true 24 25 </script>
4.8 使用super關鍵字訪問原型物件(建構式類)
1 <script> 2 3 let person = { 4 getGreeting () { 5 return 'Hello'; 6 } 7 }; 8 9 let dog = { 10 getGreeting () { 11 return 'woof'; 12 } 13 }; 14 15 // let friend = { 16 // getGreeting () { 17 // return Object.getPrototypeOf(this).getGreeting.call(this) + ', hi'; 18 // } 19 // }; 20 21 // ES 6 引入了 super 關鍵字,super 指向當前物件的原型物件,實際上也就是 Object.getPrototypeOf(this) 的值,于是,上面的代碼可以簡化成如下形式: 22 let friend = { 23 getGreeting () { 24 return super.getGreeting() + ', hi'; 25 } 26 }; 27 28 // 使用 setPrototypeOf() 方法將 friend 物件的原型物件修改成 person 29 Object.setPrototypeOf(friend, person); 30 console.log(friend.getGreeting()); // 'Hello' 31 console.log(Object.getPrototypeOf(friend) === person); // true 32 33 // 使用 setPrototypeOf() 方法將 friend 物件的原型物件修改成 dog 34 Object.setPrototypeOf(friend, dog); 35 console.log(friend.getGreeting()); // 'Hello' 36 console.log(Object.getPrototypeOf(friend) === dog); // true 37 38 39 40 </script>
五、類
5.1 ES5 中與類相似的結構
1 <script> 2 3 // 建構式類似于面向物件編程語言中類(class) 4 function PersonType(fullName) { 5 this.fullName = fullName; 6 } 7 8 Object.defineProperty(PersonType.prototype, 'sayName', { 9 configurable: true, 10 enumerable: false, 11 writable: true, 12 value: function () { 13 return this.fullName; 14 } 15 }) 16 17 let student1 = new PersonType('苑文浩'); 18 19 console.log(student1) 20 21 22 </script>
5.2 ES6 中新增類的概念
1 <script> 2 3 // 使用 class 關鍵字定義 PersonClass 類 4 class PersonClass { 5 // PersonClass 類的建構式,等價于建構式 PersonType 6 // 當我們創建 PersonClass 的實體時,該方法會自動被呼叫, 7 constructor(fullName) { 8 this.fullName = fullName; 9 } 10 11 // 為原型物件添加的方法,等于下面的代碼: 12 // Object.defineProperty(PersonType.prototype, 'sayName', { 13 // configurable: true, 14 // enumerable: false, 15 // writable: true, 16 // value: function () { 17 // return this.fullName; 18 // } 19 // }) 20 21 sayName() { 22 return this.fullName; 23 } 24 } 25 26 let student1 = new PersonClass('劉旭凱'); 27 28 console.log(student1) 29 30 // 通過與之前的建構式對比,類和建構式的作用是一樣的, 31 // 其實類的語法只是一種語法糖,實質上等價于一個自定義型別的建構式, 32 33 </script>
5.3 類和建構式的區別
1. 類的宣告不會被提升,而建構式的宣告會被提升, 2. 使用除 new 以外的方式呼叫類的建構式(constructor)會導致程式拋出錯誤, 3. 在類中宣告的方法都是不可列舉的,5.4 類-建構式的屬性
1 <script> 2 3 function PersonType(fullName) { 4 this.fullName = fullName; 5 // 實體物件的方法 6 this.greeting = function () { 7 return 'Hello World!'; 8 }; 9 } 10 11 // 建構式的方法 12 PersonType.create = function (fullName) { 13 return new PersonType(fullName) 14 }; 15 16 // 原型物件的方法 17 Object.defineProperty(PersonType.prototype, 'sayName', { 18 configurable: true, 19 enumerable: false, 20 writable: true, 21 value: function () { 22 return this.fullName; 23 } 24 }); 25 26 // 建構式的方法,可以在不創建實體的情況下直接使用, 27 // PersonType.create('朱帥旗') 28 29 // 在使用實體物件的屬性時,必須要先創建實體物件, 30 // let student1 = new PersonType('苑文浩'); 31 // console.log(student1.fullName) 32 // console.log(student1.sayName()) 33 34 </script>
5.5 類-類的靜態方法
1 <script> 2 3 // 類的語法 4 class PersonClass { 5 6 constructor(fullName) { 7 this.fullName = fullName; 8 // 實體物件的方法 9 this.greeting = function () { 10 return '你好世界!'; 11 }; 12 } 13 14 // PersonClass 類的靜態方法,等價于建構式的方法 15 static create (fullName) { 16 return new PersonClass(fullName); 17 } 18 19 // 原型物件的方法 20 sayName () { 21 return this.fullName; 22 } 23 24 } 25 26 // 類的靜態方法,不用創建實體即可呼叫 27 PersonClass.create('張尊娟') 28 29 // 在使用實體物件的屬性時,必須要先創建實體物件, 30 let student1 = new PersonClass('張康'); 31 console.log(student1.fullName) 32 console.log(student1.sayName()) 33 34 </script>
5.6 類-建構式的繼承
1 <script> 2 3 // 宣告 Rectangle 建構式 4 function Rectangle (width, height) { 5 this.width = width; 6 this.height = height; 7 } 8 9 Object.defineProperty(Rectangle.prototype, 'getArea', { 10 enumerable: false, 11 configurable: true, 12 writable: true, 13 value: function () { 14 return '矩形面積為:' + this.width * this.height; 15 } 16 }); 17 18 // 宣告 Square 建構式 19 function Square (size) { 20 Rectangle.call(this, size, size); 21 } 22 23 // 讓 Square 建構式繼承 Rectangle 建構式 24 Square.prototype = Object.create(Rectangle.prototype, { 25 constructor: { 26 configurable: true, 27 enumerable: false, 28 writable: true, 29 value: Square 30 } 31 }); 32 33 let square = new Square(20); 34 35 console.log(square) 36 </script>
5.7 類-類的繼承
1 <script> 2 3 class Rectangle { 4 5 constructor(width, height) { 6 this.width = width; 7 this.height = height; 8 } 9 10 getArea () { 11 return '矩形面積為:' + this.width * this.height; 12 } 13 } 14 15 // 讓 Square 類繼承 Rectangle 構類 16 class Square extends Rectangle { 17 18 constructor (size) { 19 // 等價于 Rectangle.call(this, size, size) 20 super(size, size) 21 } 22 } 23 24 let square1 = new Square(20) 25 26 // square1.__proto__ --> obj.__proto__ --> Rectange.prototype.__proto__ --> Object.prototype 27 28 console.log(square1) 29 30 </script>
5.8 類-類的建構式
1 <script> 2 3 // 1. 當我們創建某個類的實體時,類的建構式(constructor)會自動被呼叫, 4 class Rectangle { 5 6 constructor(width, height) { 7 this.width = width; 8 this.height = height; 9 } 10 11 getArea() { 12 return '矩形面積為:' + this.width * this.height; 13 } 14 } 15 16 // 2. 如果子類中具有建構式,則必須在建構式中使用 super 方法呼叫父類的建構式,并傳入所需的引數, 17 class Square extends Rectangle { 18 constructor (size) { 19 super(size, size) 20 } 21 } 22 23 24 // 3. 建構式不是必需的,當我們不需要為實體物件初始化屬性時,可以省略建構式, 25 // 當創建類的實體時,會自動地為類添加一個默認的 constructor 建構式,并在建構式中使用 super() 方法呼叫父類的建構式,并傳入所有引數, 26 // class Square extends Rectangle { 27 28 // } 29 30 // JS 引擎在后臺為我們做了哪些事情呢?下面的代碼等價于上面的代碼: 31 // class Square extends Rectangle { 32 // constructor (...args) { 33 // super(...args) 34 // } 35 // } 36 37 let square1 = new Square(20) 38 39 console.dir(Square) 40 console.log(square1) 41 42 </script>
5.9 覆寫父類的方法
1 <script> 2 3 // 4 class Rectangle { 5 6 constructor(width, height) { 7 this.width = width; 8 this.height = height; 9 } 10 11 getArea () { 12 return '矩形面積為:' + this.width * this.height; 13 } 14 } 15 16 // 17 class Square extends Rectangle { 18 constructor (size) { 19 super(size, size) 20 } 21 22 // 創建一個與父類同名的方法就可以覆寫父類的方法, 23 // 在順著原型物件鏈查找方法時,該方法會被優先找到, 24 getArea () { 25 return '正方形的面積為:' + this.width * this.height; 26 } 27 } 28 29 let square1 = new Square(20) 30 31 console.log(square1) 32 console.log(square1.getArea()) 33 34 </script>
5.10 呼叫父類的方法
1 <script> 2 3 // 4 class Rectangle { 5 6 constructor(width, height) { 7 this.width = width; 8 this.height = height; 9 } 10 11 getArea () { 12 return '矩形面積為:' + this.width * this.height; 13 } 14 } 15 16 // 17 class Square extends Rectangle { 18 constructor (size) { 19 super(size, size) 20 } 21 22 // 在子類中使用 super 關鍵字就可以呼叫父類的方法 23 // Rectangle.prototype.getArea.call(this).replace('矩形', '正方形'); 24 getArea () { 25 return super.getArea().replace('矩形', '正方形'); 26 } 27 28 } 29 30 let square1 = new Square(20) 31 32 console.log(square1) 33 console.log(square1.getArea()) 34 35 </script>
5.11 繼承類的靜態方法
1 <script> 2 3 class Animal { 4 constructor(name) { 5 this.name = name; 6 } 7 8 static greeting () { 9 return 'Hello'; 10 }; 11 } 12 13 class Dog extends Animal { 14 constructor (name) { 15 super(name) 16 } 17 } 18 19 console.log(Animal.greeting()); 20 21 // 父類的靜態方法也會被子類繼承,因此我們可以通過 Dog 呼叫父類的 greeting() 方法, 22 console.log(Dog.greeting()); 23 24 </script>
5.12 ES5中自己實作的模塊
1 <script> 2 3 // 1、張三和李四在專案開發中遇到的問題如下: 4 5 // 張三創建了一個 index.js 檔案,內容如下: 6 let foo = 123; 7 8 // 李四創建了一個 home.js 檔案,內容如下: 9 let foo = 'abc'; 10 11 // 當 index.js 和 home.js 檔案同時引入到一個 index.html 檔案中時,就會拋出錯誤, 12 13 // 2、ES 5 沒有模塊的概念,但是我們又需要類似于模塊的功能,于是我們想了一系列的方式去自己實作模塊的功能, 14 // 最常用的一種方式就是利用立即執行的函式運算式及其作用域實作模塊的功能,例如: 15 16 // 在 index.js 中創建模塊 module1 17 let module1 = (function() { 18 19 let foo = 123; 20 21 function greeting () { 22 return 'Hello'; 23 } 24 25 return { 26 foo, 27 greeting 28 }; 29 30 }()) 31 32 // 在 home.js 中創建模塊 module2 33 let module2 = (function() { 34 35 let foo = 'abc'; 36 37 function greeting () { 38 return 'Hello'; 39 } 40 41 return { 42 foo, 43 greeting 44 }; 45 46 }()) 47 48 // 我們只需要保證兩個模塊的名稱不同即可,在使用模塊中的函式或變數時,只需要在前面加上模塊名即可,如下所示: 49 console.log(module1.foo) 50 console.log(module2.foo) 51 52 53 54 </script>
5-13 ES6模塊化規范
1 <body> 2 3 <!-- 要想讓瀏覽器把 module1.js 和 module2.js 當成兩個模塊去決議,你要為 script 標簽添加 type="module" 屬性 --> 4 <!-- 要想然下面的代碼正確執行,不能使用檔案協議(file://),也就是說不能直接使用瀏覽器打開該頁面,需要將其放入服務器中,然后再打開, --> 5 <script src="./13-module1.js" type="module"></script> 6 <script src="./14-module2.js" type="module"></script> 7 <script src="./15-module3.js" type="module"></script> 8 </body>
- module1.js
1 // 崔景龍 2 3 // 1. 模塊私有的內容 4 // function multiply (x, y) { 5 // return x * y; 6 // } 7 8 9 // 2. 模塊匯出的內容 10 // export function sum (x, y) { 11 // return x + y; 12 // } 13 14 // export function substract (x, y) { 15 // return x - y; 16 // } 17 18 // 3. 一次匯出多個模塊成員 19 function divide (x, y) { 20 return x / y; 21 } 22 23 function remainder (x, y) { 24 return x % y; 25 } 26 27 export { divide, remainder }; 28 29 // 4. 為匯出成員重命名 30 // export { divide as div, remainder as rem }; 31 32 33 // 5. 默認匯出,模塊默認匯出成員,其他模塊匯入該成員時,名稱不能寫到大括號中,并且無需與匯出時的名稱一致, 34 // export default function max (x, y) { 35 // return x > y ? x : y; 36 // } 37 38 39 // 6. 每個模塊只能有一個默認匯出,因此下面的代碼會拋出錯誤, 40 // export default function min (x, y) { 41 // return x < y ? x : y; 42 // }
- module2.js
1 // 劉旭凱 2 3 // 1. 從 module1.js 中匯入 sum() 和 substract() 方法 4 // import { sum, substract } from './13-module1.js'; 5 6 // 使用從 module1.js 中匯入的方法 7 // console.log(sum(12, 48)) 8 // console.log(substract(12, 48)) 9 10 // 2. 匯入重命名之后的模塊成員 11 // 注意,必須要寫成重命名之后的名稱(div,rem),不能寫成重命名之前的名稱(divide,remainder) 12 // import { div, rem } from './13-module1.js'; 13 // console.log(div(48, 12)) 14 // console.log(rem(48, 12)) 15 16 // 3. 匯入其他模塊默認匯出成員 17 // 注意,名稱不能寫到大括號中,并且無需與匯出時的名稱一致, 18 // import abc from './13-module1.js'; 19 // console.log(abc(12, 48)) 20 21 // 4. 同時匯入其他模塊的默認匯出的成員和普通匯出的成員 22 // import abc, {sum, div} from './13-module1.js' 23 // console.log(abc(12, 48)) 24 // console.log(sum(12, 48)) 25 // console.log(div(12, 48)) 26 27 // 5. 為匯入的成員重命名 28 // 注意,重命名之后不能再使用之前的名稱了,否則會拋出錯誤, 29 // import { substract as sub} from './13-module1.js'; 30 // console.log(sub(12, 48)) 31 // console.log(substract(12, 48)); // 拋出錯誤 32 33 // 6. 匯入其他模塊匯出的所有成員 34 // import * as module1 from './13-module1.js' 35 // console.log(module1.sum(12, 34)) 36 // console.log(module1.default(12, 34)) 37 38 // 7. 匯入沒有匯出任何成員的模塊,目的是想讓模塊中的代碼執行 39 // 注意,模塊中的代碼只在第一次被匯入時執行, 40 // import './13-module1.js'; 41 42 // 下面是一個簡單的使用場景: 43 44 // module1.js 檔案中有如下代碼: 45 // let age = 20; 46 47 // export function getAge () { 48 // return age; 49 // } 50 51 // export function setAge () { 52 // age++; 53 // } 54 55 // module2.js 檔案中的代碼如下: 56 // import { setAge } from './13-module1.js'; 57 // setAge(); 58 59 // module3.js 檔案中的代碼如下: 60 // import './module2.js'; 61 // import { getAge } from './module1.js'; 62 // console.log(getAge()); // 21 63 64 // 8. 將匯入的成員重命名之后再次匯出 65 export { divide as div, remainder as rem } from './13-module1.js';
- module3.js
1 // 匯入 module2.js 模塊中匯出的成員(對應 module2.js 檔案中第8個示例) 2 import { div, rem } from './14-module2.js'; 3 console.log(div(48, 12)) 4 console.log(rem(48, 12))
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/165095.html
標籤:JavaScript
