這實際上說明,對象的解構賦值是下面形式的簡寫。
let { foo: foo, bar: bar } = { foo: ‘aaa’, bar: ‘bbb’ };
也就是說,對象的解構賦值的內部機制,是先找到同名屬性,然后再賦給對應的變量。真正被賦值的是后者,而不是前者。
let { foo: baz } = { foo: ‘aaa’, bar: ‘bbb’ };
baz // “aaa”
foo // error: foo is not defined
上面代碼中,foo是匹配的模式,baz才是變量。真正被賦值的是變量baz,而不是模式foo。
(2)嵌套對象的解構賦值
與數組一樣,解構也可以用于嵌套結構的對象。
let obj = {
p: [
‘Hello’,
{ y: ‘World’ }
]
};
let { p: [x, { y }] } = obj;
x // “Hello”
y // “World”
注意,這時p是模式,不是變量,因此不會被賦值。如果p也要作為變量賦值,可以寫成下面這樣。
let obj = {
p: [
‘Hello’,
{ y: ‘World’ }
]
};
let { p, p: [x, { y }] } = obj;
x // “Hello”
y // “World”
p // [“Hello”, {y: “World”}]
下面是另一個例子。
4.5、解構賦值注意事項
(1)如果要將一個已經聲明的變量用于解構賦值,必須非常小心。
// 錯誤的寫法
let x;
{x} = {x: 1};
// SyntaxError: syntax error
上面代碼的寫法會報錯,因為 JavaScript 引擎會將{x}理解成一個代碼塊,從而發生語法錯誤。只有不將大括號寫在行首,避免 JavaScript 將其解釋為代碼塊,才能解決這個問題。
// 正確的寫法
let x;
({x} = {x: 1});
上面代碼將整個解構賦值語句,放在一個圓括號里面,就可以正確執行。關于圓括號與解構賦值的關系,參見下文。
(2)解構賦值允許等號左邊的模式之中,不放置任何變量名。因此,可以寫出非常古怪的賦值表達式。
({} = [true, false]);
({} = ‘abc’);
({} = []);
上面的表達式雖然毫無意義,但是語法是合法的,可以執行。
(3)由于數組本質是特殊的對象,因此可以對數組進行對象屬性的解構。
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
上面代碼對數組進行對象解構。數組arr的0鍵對應的值是1,[arr.length - 1]就是2鍵,對應的值是3。方括號這種寫法,屬于“屬性名表達式”。
4.6、解構賦值的用途
變量的解構賦值用途很多。
(1)交換變量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
上面代碼交換變量x和y的值,這樣的寫法不僅簡潔,而且易讀,語義非常清晰。
(2)從函數返回多個值
函數只能返回一個值,如果要返回多個值,只能將它們放在數組或對象里返回。有了解構賦值,取出這些值就非常方便。
// 返回一個數組
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一個對象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
(3)函數參數的定義
解構賦值可以方便地將一組參數與變量名對應起來。
// 參數是一組有次序的值
function f([x, y, z]) { … }
f([1, 2, 3]);
// 參數是一組無次序的值
function f({x, y, z}) { … }
f({z: 3, y: 2, x: 1});
(4)提取JSON數據
解構賦值對提取 JSON 對象中的數據,尤其有用。
let jsonData = {
id: 42,
status: “OK”,
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, “OK”, [867, 5309]
上面代碼可以快速提取 JSON 數據的值。
(5)函數參數的默認值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// … more config
} = {}) {
// … do stuff
};
指定參數的默認值,就避免了在函數體內部再寫var foo = config.foo || ‘default foo’;這樣的語句。
(6)遍歷Map結構
任何部署了 Iterator 接口的對象,都可以用for…of循環遍歷。Map 結構原生支持 Iterator 接口,配合變量的解構賦值,獲取鍵名和鍵值就非常方便。
const map = new Map();
map.set(‘first’, ‘hello’);
map.set(‘second’, ‘world’);
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
如果只想獲取鍵名,或者只想獲取鍵值,可以寫成下面這樣。
// 獲取鍵名
for (let [key] of map) {
// …
}
// 獲取鍵值
for (let [,value] of map) {
// …
}
(7)輸入模塊的指定方法
加載模塊時,往往需要指定輸入哪些方法。解構賦值使得輸入語句非常清晰。
const { SourceMapConsumer, SourceNode } = require(“source-map”);
5.1、模板字符串
傳統的 JavaScript 語言,輸出模板通常是這樣寫的(下面使用了 jQuery 的方法)。
$(‘#result’).append(
‘There are ’ + basket.count + ' ’ +
'items in your basket, ’ +
‘’ + basket.onSale +
‘ are on sale!’
);
上面這種寫法相當繁瑣不方便,ES6 引入了模板字符串解決這個問題。
$(‘#result’).append(`
There are ${basket.count} items
in your basket, ${basket.onSale}
are on sale!
`);
模板字符串(template string)是增強版的字符串,用反引號(`)標識。它可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。
// 普通字符串
In JavaScript '\n' is a line-feed.
// 多行字符串
`In JavaScript this is
not legal.`
console.log(`string text line 1
string text line 2`);
// 字符串中嵌入變量
let name = “Bob”, time = “today”;
Hello ${name}, how are you ${time}?
上面代碼中的模板字符串,都是用反引號表示。
轉義符號
如果在模板字符串中需要使用反引號,則前面要用反斜杠轉義。
let greeting = \
Yo` World!`;
多行字符串
如果使用模板字符串表示多行字符串,所有的空格和縮進都會被保留在輸出之中。
$(‘#list’).html(`
- first
- second
`);
上面代碼中,所有模板字符串的空格和換行,都是被保留的,比如
<ul>
標簽前面會有一個換行。如果你不想要這個換行,可以使用trim
方法消除它。$(‘#list’).html(`
- first
- second
`.trim());
插入變量
模板字符串中嵌入變量,需要將變量名寫在${}之中。
function authorize(user, action) {
if (!user.hasPrivilege(action)) {
throw new Error(
// 傳統寫法為
// 'User ’
// + user.name
// + ’ is not authorized to do ’
// + action
// + ‘.’
User ${user.name} is not authorized to do ${action}.
);}
}
插入表達式
大括號內部可以放入任意的 JavaScript 表達式,可以進行運算,以及引用對象屬性。
let x = 1;
let y = 2;
${x} + ${y} = ${x + y}
// “1 + 2 = 3”
${x} + ${y * 2} = ${x + y * 2}
// “1 + 4 = 5”
let obj = {x: 1, y: 2};
${obj.x + obj.y}
// “3”
調用函數
模板字符串之中還能調用函數。
function fn() {
return “Hello World”;
}
foo ${fn()} bar
// foo Hello World bar
如果大括號中的值不是字符串,將按照一般的規則轉為字符串。比如,大括號中是一個對象,將默認調用對象的
toString
方法。如果模板字符串中的變量沒有聲明,將報錯。
// 變量place沒有聲明
let msg =
Hello, ${place}
;// 報錯
由于模板字符串的大括號內部,就是執行 JavaScript 代碼,因此如果大括號內部是一個字符串,將會原樣輸出。
Hello ${'World'}
// “Hello World”
注意要點
模板字符串中的換行和空格都是會被保留的
innerHtml = `
- menu
- mine
`;
console.log(innerHtml);
// 輸出
- menu
- mine
5.2、字符串擴展方法
(1)子串的識別
ES6 之前判斷字符串是否包含子串,用 indexOf 方法,ES6 新增了子串的識別方法。
-
includes():返回布爾值,判斷是否找到參數字符串。
-
startsWith():返回布爾值,判斷參數字符串是否在原字符串的頭部。
-
endsWith():返回布爾值,判斷參數字符串是否在原字符串的尾部。
以上三個方法都可以接受兩個參數,需要搜索的字符串,和可選的搜索起始位置索引。
let s = ‘Hello world!’;
s.startsWith(‘Hello’) // true
s.endsWith(‘!’) // true
s.includes(‘o’) // true
這三個方法都支持第二個參數,表示開始搜索的位置。
let s = ‘Hello world!’;
s.startsWith(‘world’, 6) // true
s.endsWith(‘Hello’, 5) // true
s.includes(‘Hello’, 6) // false
上面代碼表示,使用第二個參數
n
時,endsWith
的行為與其他兩個方法有所不同。它針對前n
個字符,而其他兩個方法針對從第n
個位置直到字符串結束。注意點:
-
這三個方法只返回布爾值,如果需要知道子串的位置,還是得用 indexOf 和 lastIndexOf 。
-
這三個方法如果傳入了正則表達式而不是字符串,會拋出錯誤。而 indexOf 和 lastIndexOf 這兩個方法,它們會將正則表達式轉換為字符串并搜索它。
(2)字符串重復
repeat():返回新的字符串,表示將字符串重復指定次數返回。
‘x’.repeat(3) // “xxx”
‘hello’.repeat(2) // “hellohello”
‘na’.repeat(0) // “”
參數如果是小數,會被向下取整。
‘na’.repeat(2.9) // “nana”
如果
repeat
的參數是負數或者Infinity
,會報錯。‘na’.repeat(Infinity)
// RangeError
‘na’.repeat(-1)
// RangeError
但是,如果參數是 0 到-1 之間的小數,則等同于 0,這是因為會先進行取整運算。0 到-1 之間的小數,取整以后等于
-0
,repeat
視同為 0。‘na’.repeat(-0.9) // “”
參數
NaN
等同于 0。‘na’.repeat(NaN) // “”
如果
repeat
的參數是字符串,則會先轉換成數字。‘na’.repeat(‘na’) // “”
‘na’.repeat(‘3’) // “nanana”
(3)字符串補全
ES2017 引入了字符串補全長度的功能。如果某個字符串不夠指定長度,會在頭部或尾部補全。
-
padStart:返回新的字符串,表示用參數字符串從頭部(左側)補全原字符串。
-
padEnd:返回新的字符串,表示用參數字符串從尾部(右側)補全原字符串。
以上兩個方法接受兩個參數,第一個參數是指定生成的字符串的最小長度,第二個參數是用來補全的字符串。如果沒有指定第二個參數,默認用空格填充。
console.log(“h”.padStart(5,“o”)); // “ooooh”
console.log(“h”.padEnd(5,“o”)); // “hoooo”
console.log(“h”.padStart(5)); // " h"
console.log(‘x’.padStart(5, ‘ab’)); // ‘ababx’
console.log(‘x’.padStart(4, ‘ab’)); // ‘abax’
console.log(‘x’.padEnd(5, ‘ab’)); // ‘xabab’
console.log(‘x’.padEnd(4, ‘ab’)); // ‘xaba’
上面代碼中,
padStart()
和padEnd()
一共接受兩個參數,第一個參數是字符串補全生效的最大長度,第二個參數是用來補全的字符串。如果指定的長度小于或者等于原字符串的長度,則返回原字符串:
console.log(“hello”.padStart(5,“A”)); // “hello”
如果原字符串加上補全字符串長度大于指定長度,則截去超出位數的補全字符串:
console.log(“hello”.padEnd(10,“,world!”)); // “hello,worl”
如果省略第二個參數,默認使用空格補全長度。
console.log(‘x’.padStart(4)); // ’ x’
console.log(‘x’.padEnd(4)); // 'x ’
padStart()
的常見用途是為數值補全指定位數。下面代碼生成 10 位的數值字符串。console.log(‘1’.padStart(10, ‘0’)); // “0000000001”
console.log(‘12’.padStart(10, ‘0’)); // “0000000012”
console.log(‘123456’.padStart(10, ‘0’)); // “0000123456”
另一個用途是提示字符串格式。
console.log(‘12’.padStart(10, ‘YYYY-MM-DD’)); // “YYYY-MM-12”
console.log(‘09-12’.padStart(10, ‘YYYY-MM-DD’)); // “YYYY-09-12”
(4)消除空格
ES6對字符串實例新增了
trimStart()
和trimEnd()
這兩個方法。它們的行為與trim()
一致,trimStart()
消除字符串頭部的空格,trimEnd()
消除尾部的空格。它們返回的都是新字符串,不會修改原始字符串。const s = ’ abc ';
s.trim() // “abc”
s.trimStart() // "abc "
s.trimEnd() // " abc"
上面代碼中,
trimStart()
只消除頭部的空格,保留尾部的空格。trimEnd()
也是類似行為。除了空格鍵,這兩個方法對字符串頭部(或尾部)的 tab 鍵、換行符等不可見的空白符號也有效。
瀏覽器還部署了額外的兩個方法,
trimLeft()
是trimStart()
的別名,trimRight()
是trimEnd()
的別名。5.3、函數的擴展
(1)默認值
ES6 之前,不能直接為函數的參數指定默認值,只能采用變通的方法。
function log(x, y) {
y = y || ‘World’;
console.log(x, y);
}
log(‘Hello’) // Hello World
log(‘Hello’, ‘China’) // Hello China
log(‘Hello’, ‘’) // Hello World
上面代碼檢查函數
log
的參數y
有沒有賦值,如果沒有,則指定默認值為World
。這種寫法的缺點在于,如果參數y
賦值了,但是對應的布爾值為false
,則該賦值不起作用。就像上面代碼的最后一行,參數y
等于空字符,結果被改為默認值。為了避免這個問題,通常需要先判斷一下參數
y
是否被賦值,如果沒有,再等于默認值。if (typeof y === ‘undefined’) {
y = ‘World’;
}
ES6 允許為函數的參數設置默認值,即直接寫在參數定義的后面。
function log(x, y = ‘World’) {
console.log(x, y);
}
log(‘Hello’) // Hello World
log(‘Hello’, ‘China’) // Hello China
log(‘Hello’, ‘’) // Hello
可以看到,ES6 的寫法比 ES5 簡潔許多,而且非常自然。下面是另一個例子。
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
const p = new Point();
p // { x: 0, y: 0 }
除了簡潔,ES6 的寫法還有兩個好處:首先,閱讀代碼的人,可以立刻意識到哪些參數是可以省略的,不用查看函數體或文檔;其次,有利于將來的代碼優化,即使未來的版本在對外接口中,徹底拿掉這個參數,也不會導致以前的代碼無法運行。
參數變量是默認聲明的,所以不能用
let
或const
再次聲明。function foo(x = 5) {
let x = 1; // error
const x = 2; // error
}
上面代碼中,參數變量
x
是默認聲明的,在函數體中,不能用let
或const
再次聲明,否則會報錯。使用參數默認值時,函數不能有同名參數。
// 不報錯
function foo(x, x, y) {
// …
}
// 報錯
function foo(x, x, y = 1) {
// …
}
// SyntaxError: Duplicate parameter name not allowed in this context
另外,一個容易忽略的地方是,參數默認值不是傳值的,而是每次都重新計算默認值表達式的值。也就是說,參數默認值是惰性求值的。
let x = 99;
function foo(p = x + 1) {
console.log§;
}
foo() // 100
x = 100;
foo() // 101
上面代碼中,參數
p
的默認值是x + 1
。這時,每次調用函數foo
,都會重新計算x + 1
,而不是默認p
等于 100。(2)不定參數
不定參數用來表示不確定參數個數,形如,…變量名,由…加上一個具名參數標識符組成。具名參數只能放在參數組的最后,并且有且只有一個不定參數。
基本用法
function f(…values){
console.log(values.length);
}
f(1,2); //2
f(1,2,3,4); //4
(3)箭頭函數
箭頭函數提供了一種更加簡潔的函數書寫方式。基本語法是:
參數 => 函數體
基本用法:
var f = v => v;
//等價于
var f = function(a){
return a;
}
f(1); //1
當箭頭函數沒有參數或者有多個參數,要用 () 括起來。
var f = (a,b) => a+b;
f(6,2); //8
當箭頭函數函數體有多行語句,用 {} 包裹起來,表示代碼塊,當只有一行語句,并且需要返回結果時,可以省略 {} , 結果會自動返回。
var f = (a,b) => {
let result = a+b;
return result;
}
f(6,2); // 8
當箭頭函數要返回對象的時候,為了區分于代碼塊,要用 () 將對象包裹起來
// 報錯
var f = (id,name) => {id: id, name: name};
f(6,2); // SyntaxError: Unexpected token :
// 不報錯
var f = (id,name) => ({id: id, name: name});
f(6,2); // {id: 6, name: 2}
注意點:沒有 this、super、arguments 和 new.target 綁定。
var func = () => {
// 箭頭函數里面沒有 this 對象,
// 此時的 this 是外層的 this 對象,即 Window
console.log(this)
}
func(55) // Window
var func = () => {
console.log(arguments)
}
func(55); // ReferenceError: arguments is not defined
箭頭函數體中的 this 對象,是定義函數時的對象,而不是使用函數時的對象。
function fn(){
setTimeout(()=>{
// 定義時,this 綁定的是 fn 中的 this 對象
console.log(this.a);
},0)
}
var a = 20;
// fn 的 this 對象為 {a: 19}
fn.call({a: 18}); // 18
不可以作為構造函數,也就是不能使用 new 命令,否則會報錯
5.4、數組的擴展
(1)擴展運算符
擴展運算符(spread)是三個點(
...
)。它好比 rest 參數的逆運算,將一個數組轉為用逗號分隔的參數序列。console.log(…[1, 2, 3])
// 1 2 3
console.log(1, …[2, 3, 4], 5)
// 1 2 3 4 5
[…document.querySelectorAll(‘div’)]
// [
,,]該運算符主要用于函數調用。
function push(array, …items) {
array.push(…items);
}
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(…numbers) // 42
上面代碼中,
array.push(...items)
和add(...numbers)
這兩行,都是函數的調用,它們都使用了擴展運算符。該運算符將一個數組,變為參數序列。(2)擴展運算符的應用
復制數組
數組是復合的數據類型,直接復制的話,只是復制了指向底層數據結構的指針,而不是克隆一個全新的數組。
const a1 = [1, 2];
const a2 = a1;
a2[0] = 2;
a1 // [2, 2]
上面代碼中,
a2
并不是a1
的克隆,而是指向同一份數據的另一個指針。修改a2
,會直接導致a1
的變化。ES5 只能用變通方法來復制數組。
const a1 = [1, 2];
const a2 = a1.concat();
a2[0] = 2;
a1 // [1, 2]
上面代碼中,
a1
會返回原數組的克隆,再修改a2
就不會對a1
產生影響。擴展運算符提供了復制數組的簡便寫法。
const a1 = [1, 2];
// 寫法一
const a2 = […a1];
// 寫法二
const […a2] = a1;
上面的兩種寫法,
a2
都是a1
的克隆。合并數組
擴展運算符提供了數組合并的新寫法。
const arr1 = [‘a’, ‘b’];
const arr2 = [‘c’];
const arr3 = [‘d’, ‘e’];
// ES5 的合并數組
arr1.concat(arr2, arr3);
// [ ‘a’, ‘b’, ‘c’, ‘d’, ‘e’ ]
// ES6 的合并數組
[…arr1, …arr2, …arr3]
// [ ‘a’, ‘b’, ‘c’, ‘d’, ‘e’ ]
不過,這兩種方法都是淺拷貝,使用的時候需要注意。
const a1 = [{ foo: 1 }];
const a2 = [{ bar: 2 }];
const a3 = a1.concat(a2);
const a4 = […a1, …a2];
a3[0] === a1[0] // true
a4[0] === a1[0] // true
上面代碼中,
a3
和a4
是用兩種不同方法合并而成的新數組,但是它們的成員都是對原數組成員的引用,這就是淺拷貝。如果修改了原數組的成員,會同步反映到新數組。(3)數組實例的find()和findIndex()
數組實例的
find
方法,用于找出第一個符合條件的數組成員。它的參數是一個回調函數,所有數組成員依次執行該回調函數,直到找出第一個返回值為true
的成員,然后返回該成員。如果沒有符合條件的成員,則返回undefined
。[1, 4, -5, 10].find((n) => n < 0)
// -5
上面代碼找出數組中第一個小于 0 的成員。
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
上面代碼中,
find
方法的回調函數可以接受三個參數,依次為當前的值、當前的位置和原數組。數組實例的
findIndex
方法的用法與find
方法非常類似,返回第一個符合條件的數組成員的位置,如果所有成員都不符合條件,則返回-1
。[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
5.5、對象的擴展
ES6 允許在大括號里面,直接寫入變量和函數,作為對象的屬性和方法。這樣的書寫更加簡潔。
const foo = ‘bar’;
const baz = {foo};
baz // {foo: “bar”}
// 等同于const baz = {foo: foo};
除了屬性簡寫,方法也可以簡寫。
const o = {
method() {
return “Hello!”;
}
};
// 等同于
const o = {
method: function() {
return “Hello!”;
}
};
對象的新方法
Object.assign(target, source_1, ···)
用于將源對象的所有可枚舉屬性復制到目標對象中。
基本用法
let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3); // 第一個參數是目標對象,后面的參數是源對象
target; // {a: 1, b: 2, c: 3}
6.1、類的由來
JavaScript 語言中,生成實例對象的傳統方法是通過構造函數。下面是一個例子。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return ‘(’ + this.x + ', ’ + this.y + ‘)’;
};
var p = new Point(1, 2);
上面這種寫法跟傳統的面向對象語言(比如 C++ 和 Java)差異很大,很容易讓新學習這門語言的程序員感到困惑。
ES6 提供了更接近傳統語言的寫法,引入了 Class(類)這個概念,作為對象的模板。通過
class
關鍵字,可以定義類。基本上,ES6 的
class
可以看作只是一個語法糖,它的絕大部分功能,ES5 都可以做到,新的class
寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。上面的代碼用 ES6 的class
改寫,就是下面這樣。class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return ‘(’ + this.x + ', ’ + this.y + ‘)’;
}
}
上面代碼定義了一個“類”,可以看到里面有一個
constructor
方法,這就是構造方法,而this
關鍵字則代表實例對象。也就是說,ES5 的構造函數Point
,對應 ES6 的Point
類的構造方法。Point
類除了構造方法,還定義了一個toString
方法。注意,定義“類”的方法的時候,前面不需要加上function
這個關鍵字,直接把函數定義放進去了就可以了。另外,方法之間不需要逗號分隔,加了會報錯。6.2、constructor方法
constructor
方法是類的默認方法,通過new
命令生成對象實例時,自動調用該方法。一個類必須有constructor
方法,如果沒有顯式定義,一個空的constructor
方法會被默認添加。class Point {
}
// 等同于
class Point {
constructor() {}
}
上面代碼中,定義了一個空的類
Point
,JavaScript 引擎會自動為它添加一個空的constructor
方法。6.3、類的實例
生成類的實例的寫法,與 ES5 完全一樣,也是使用
new
命令。前面說過,如果忘記加上new
,像函數那樣調用Class
,將會報錯。class Point {
// …
}
// 報錯
var point = Point(2, 3);
// 正確
var point = new Point(2, 3);
6.4、類的繼承
Class 可以通過
extends
關鍵字實現繼承,這比 ES5 的通過修改原型鏈實現繼承,要清晰和方便很多。class Point {
}
class ColorPoint extends Point {
}
super關鍵字
super
這個關鍵字,既可以當作函數使用,也可以當作對象使用。在這兩種情況下,它的用法完全不同。第一種情況,
super
作為函數調用時,代表父類的構造函數。ES6 要求,子類的構造函數必須執行一次super
函數。class A {}
class B extends A {
constructor() {
super();
}
}
上面代碼中,子類
B
的構造函數之中的super()
,代表調用父類的構造函數。這是必須的,否則 JavaScript 引擎會報錯。第二種情況,
super
作為對象時,在普通方法中,指向父類的原型對象;在靜態方法中,指向父類。class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}
let b = new B();
上面代碼中,子類
B
當中的super.p()
,就是將super
當作一個對象使用。這時,super
在普通方法之中,指向A.prototype
,所以super.p()
就相當于A.prototype.p()
。6.5、靜態方法
類相當于實例的原型,所有在類中定義的方法,都會被實例繼承。如果在一個方法前,加上
static
關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,這就稱為“靜態方法”。class Foo {
static classMethod() {
return ‘hello’;
}
}
Foo.classMethod() // ‘hello’
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
上面代碼中,
Foo
類的classMethod
方法前有static
關鍵字,表明該方法是一個靜態方法,可以直接在Foo
類上調用(Foo.classMethod()
),而不是在Foo
類的實例上調用。如果在實例上調用靜態方法,會拋出一個錯誤,表示不存在該方法。6.6、靜態屬性
靜態屬性指的是 Class 本身的屬性,即
Class.propName
,而不是定義在實例對象(this
)上的屬性。class Foo {
}
Foo.prop = 1;
Foo.prop // 1
上面的寫法為
Foo
類定義了一個靜態屬性prop
。目前,只有這種寫法可行,因為 ES6 明確規定,Class 內部只有靜態方法,沒有靜態屬性。現在有一個提案提供了類的靜態屬性,寫法是在實例屬性的前面,加上
static
關鍵字。class MyClass {
static myStaticProp = 42;
constructor() {
console.log(MyClass.myStaticProp); // 42
}
}
這個新寫法大大方便了靜態屬性的表達。
// 老寫法
class Foo {
// …
}
Foo.prop = 1;
// 新寫法
class Foo {
static prop = 1;
}
上面代碼中,老寫法的靜態屬性定義在類的外部。整個類生成以后,再生成靜態屬性。這樣讓人很容易忽略這個靜態屬性,也不符合相關代碼應該放在一起的代碼組織原則。另外,新寫法是顯式聲明(declarative),而不是賦值處理,語義更好。
7.1、Set
ES6 提供了新的數據結構 Set。它類似于數組,但是成員的值都是唯一的,沒有重復的值。
基礎用法:
let mySet = new Set();
mySet.add(1); // Set(1) {1}
mySet.add(5); // Set(2) {1, 5}
mySet.add(5); // Set(2) {1, 5} 這里體現了值的唯一性
mySet.add(“some text”);
// Set(3) {1, 5, “some text”} 這里體現了類型的多樣性
var o = {a: 1, b: 2};
mySet.add(o);
mySet.add({a: 1, b: 2});
// Set(5) {1, 5, “some text”, {…}, {…}}
// 這里體現了對象之間引用不同不恒等,即使值相同,Set 也能存儲
上面代碼通過
add()
方法向 Set 結構加入成員,結果表明 Set 結構不會添加重復的值。Set
函數可以接受一個數組(或者具有 iterable 接口的其他數據結構)作為參數,用來初始化。// 例一
const set = new Set([1, 2, 3, 4, 4]);
[…set]
// [1, 2, 3, 4]
// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5
數據類型轉換
Array與Set類型轉換
// Array 轉 Set
var mySet = new Set([“value1”, “value2”, “value3”]);
// 用…操作符,將 Set 轉 Array
var myArray = […mySet];
//Array.from方法可以將 Set 結構轉為數組。
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
String與Set類型轉換
// String 轉 Set
var mySet = new Set(‘hello’); // Set(4) {“h”, “e”, “l”, “o”}
// 注:Set 中 toString 方法是不能將 Set 轉換成 String
Set實例的屬性
-
Set.prototype.constructor
:構造函數,默認就是Set
函數。 -
Set.prototype.size
:返回Set
實例的成員總數。
Set實例的操作方法
-
Set.prototype.add(value)
:添加某個值,返回 Set 結構本身。 -
Set.prototype.delete(value)
:刪除某個值,返回一個布爾值,表示刪除是否成功。 -
Set.prototype.has(value)
:返回一個布爾值,表示該值是否為Set
的成員。 -
Set.prototype.clear()
:清除所有成員,沒有返回值。
代碼示例:
s.add(1).add(2).add(2);
// 注意2被加入了兩次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
Set實例的遍歷方法
-
Set.prototype.keys()
:返回鍵名的遍歷器 -
Set.prototype.values()
:返回鍵值的遍歷器 -
Set.prototype.entries()
:返回鍵值對的遍歷器 -
Set.prototype.forEach()
:使用回調函數遍歷每個成員
需要特別指出的是,
Set
的遍歷順序就是插入順序。這個特性有時非常有用,比如使用 Set 保存一個回調函數列表,調用時就能保證按照添加順序調用。代碼示例:
keys
方法、values
方法、entries
方法返回的都是遍歷器對象(詳見《Iterator 對象》一章)。由于 Set 結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值),所以keys
方法和values
方法的行為完全一致。let set = new Set([‘red’, ‘green’, ‘blue’]);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// [“red”, “red”]
// [“green”, “green”]
// [“blue”, “blue”]
forEach()代碼示例:
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ’ : ’ + value))
// 1 : 1
// 4 : 4
// 9 : 9
遍歷的應用
(1)數組去重
var mySet = new Set([1, 2, 3, 4, 4]);
[…mySet]; // [1, 2, 3, 4]
(2)并集
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var union = new Set([…a, …b]); // {1, 2, 3, 4}
(3)交集
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var intersect = new Set([…a].filter(x => b.has(x))); // {2, 3}
(4)差集
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var difference = new Set([…a].filter(x => !b.has(x))); // {1}
7.2、Map
Map 對象保存鍵值對。任何值(對象或者原始值) 都可以作為一個鍵或一個值。
基本用法:
const m = new Map();
const o = {p: ‘Hello World’};
m.set(o, ‘content’)
m.get(o) // “content”
m.has(o) // true
m.delete(o) // true
m.has(o) // false
Map中的key
key是字符串
var myMap = new Map();
var keyString = “a string”;
myMap.set(keyString, “和鍵’a string’關聯的值”);
myMap.get(keyString); // “和鍵’a string’關聯的值”
myMap.get(“a string”); // “和鍵’a string’關聯的值”
// 因為 keyString === ‘a string’
key是對象
ajax
1)ajax請求的原理/ 手寫一個ajax請求?
2)readyState?
3)ajax異步與同步的區別?
4)ajax傳遞中文用什么方法?, “e”, “l”, “o”}
// 注:Set 中 toString 方法是不能將 Set 轉換成 String
Set實例的屬性
-
Set.prototype.constructor
:構造函數,默認就是Set
函數。 -
Set.prototype.size
:返回Set
實例的成員總數。
Set實例的操作方法
-
Set.prototype.add(value)
:添加某個值,返回 Set 結構本身。 -
Set.prototype.delete(value)
:刪除某個值,返回一個布爾值,表示刪除是否成功。 -
Set.prototype.has(value)
:返回一個布爾值,表示該值是否為Set
的成員。 -
Set.prototype.clear()
:清除所有成員,沒有返回值。
代碼示例:
s.add(1).add(2).add(2);
// 注意2被加入了兩次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
Set實例的遍歷方法
-
Set.prototype.keys()
:返回鍵名的遍歷器 -
Set.prototype.values()
:返回鍵值的遍歷器 -
Set.prototype.entries()
:返回鍵值對的遍歷器 -
Set.prototype.forEach()
:使用回調函數遍歷每個成員
需要特別指出的是,
Set
的遍歷順序就是插入順序。這個特性有時非常有用,比如使用 Set 保存一個回調函數列表,調用時就能保證按照添加順序調用。代碼示例:
keys
方法、values
方法、entries
方法返回的都是遍歷器對象(詳見《Iterator 對象》一章)。由于 Set 結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值),所以keys
方法和values
方法的行為完全一致。let set = new Set([‘red’, ‘green’, ‘blue’]);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// [“red”, “red”]
// [“green”, “green”]
// [“blue”, “blue”]
forEach()代碼示例:
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ’ : ’ + value))
// 1 : 1
// 4 : 4
// 9 : 9
遍歷的應用
(1)數組去重
var mySet = new Set([1, 2, 3, 4, 4]);
[…mySet]; // [1, 2, 3, 4]
(2)并集
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var union = new Set([…a, …b]); // {1, 2, 3, 4}
(3)交集
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var intersect = new Set([…a].filter(x => b.has(x))); // {2, 3}
(4)差集
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var difference = new Set([…a].filter(x => !b.has(x))); // {1}
7.2、Map
Map 對象保存鍵值對。任何值(對象或者原始值) 都可以作為一個鍵或一個值。
基本用法:
const m = new Map();
const o = {p: ‘Hello World’};
m.set(o, ‘content’)
m.get(o) // “content”
m.has(o) // true
m.delete(o) // true
m.has(o) // false
Map中的key
key是字符串
var myMap = new Map();
var keyString = “a string”;
myMap.set(keyString, “和鍵’a string’關聯的值”);
myMap.get(keyString); // “和鍵’a string’關聯的值”
myMap.get(“a string”); // “和鍵’a string’關聯的值”
// 因為 keyString === ‘a string’
key是對象
ajax
1)ajax請求的原理/ 手寫一個ajax請求?
2)readyState?
3)ajax異步與同步的區別?
4)ajax傳遞中文用什么方法?[外鏈圖片轉存中…(img-r9nfexn9-1719211722806)]
[外鏈圖片轉存中…(img-xzvp885N-1719211722807)]
-