1.新的標準規(guī)范
ECMAScript2015 是 js 的一種的新的標準規(guī)范念祭,就是對 js 的寫法上提出了新的語法要求和寫法格式。
2.ECMAScript 和 javaScript 關(guān)系
ECMAScript 和 JavaScript 的關(guān)系是群嗤,前者是后者的規(guī)格,后者是前者的一種實現(xiàn)获黔。javascript 是 netscape 創(chuàng) 造的并交給了國際標準化組織 ECMA痹雅,之所以不叫做 JavaScript 由于商標的問題仰担,java 是 sun 公司的商標,根據(jù) 授權(quán)協(xié)議只有 Netscape 公司可以合法使用 JavaScript 這個名字绩社,另外就是為了體現(xiàn) JavaScript 的標準的制定者 不是 ECMA 所以取名為 ECMAScript惰匙。
3.ES6 與 ECMAScript 2015 的關(guān)系
ES6 是 ECMA 為 JavaScript 制定的第 6 個版本的標準技掏,標準委員會最終決定铃将,標準在每年的 6 月份正式發(fā)布一 次项鬼,作為當年的正式版本。ECMAscript 2015 是在 2015 年 6 月份發(fā)布的 ES6 的第一個版本劲阎。依次類推 ECMAscript 2016 是 ES7, ECMAscript 2017 是 ES8, 后續(xù)版本都以年份為命名
塊級作用域
1.塊級作用域的種類
ECMAScript2015 為 js 提出的第三個作用域绘盟,凡是帶{}的都是一個塊級作用域。
if 語句的{},for 循環(huán)中的{},while 中的{},或者是我們單獨寫的{} try{}catch(error){}這些都提供了塊級作用域悯仙。
塊級作用域
為什么需要塊級作用域
-
內(nèi)層變量會覆蓋外層變量
var a = 'glh'; function fn() { console.log(a); //undefined if (false) { var a = 'hello'; } } fn();
這是因為 fn 函數(shù)體內(nèi)以及有 var 聲明的變量 a龄毡,只是還沒有賦值,默認為 undefined锡垄。
-
用來計數(shù)的循環(huán)變量泄露為全局變量
for (var i = 0; i < 10; i++) {} // 10 console.log(i);
let const
1.let
基本用法
ECMAScript2015 新增了let
命令沦零,用來聲明變量。它的用法類似于var
货岭,但是所聲明的變量路操,只在let
命令所在的代碼塊內(nèi)有效。
let 主要聲明塊級作用域下的成員千贯,這個成員變量的作用域范圍只能在當前塊級作用域以及子作用域鏈中屯仗。
2.const
const 聲明變量的同時必須要賦值。
const 聲明之后搔谴,不允許去修改它的值魁袜,這里面的值說的是不允許修改它,是聲明之后不允許重新指向一個新
的內(nèi)存地址敦第,可以去修改內(nèi)存地址中的屬性成員峰弹。
數(shù)組
1.數(shù)組解構(gòu)
ES6 允許按照一定模式,從數(shù)組和對象中提取值芜果,對變量進行賦值鞠呈,這被稱為解構(gòu)。
-
完全解構(gòu) 將數(shù)組中的每一個值都對應(yīng)上相應(yīng)的變量师幕。
var arr = ['a', 'b', 'c']; let [com, ind, work] = arr; console.log(work); // c
-
不完全解構(gòu) 數(shù)組中的部分值對應(yīng)上了相應(yīng)的變量粟按。
var arr = ['a', 'b', 'c']; let [, , work] = arr; console.log(work); // c
注意:模式?jīng)]有匹配上的可以不填,但是必須要加逗號隔開霹粥。
-
解構(gòu)不成功
右邊的變量的個數(shù)超過了等號左邊中數(shù)組的元素
let [a, b, c] = [12]; console.log(b); //undefined
如果解構(gòu)沒有成功灭将,則變量的值是 undefined,如果是展開運算的變量則是空數(shù)組后控。
let [a, b, ...c] = [12]; console.log(c); // []
2.數(shù)組的擴展
-
展開運算符說明
三個點(…)是一個展開運算符庙曙,其功能為對三個點后面的變量進行展開操作
三個點(…)展開運算符:只能對具有 Iterator 接口的對象進行展開操作
-
替代 apply()的使用技巧
我們之前在求一個數(shù)組中的最大值得時候采用得方式是 Math.max.apply(null,[12,34,56,43]) ==56
var max = Math.max.apply(null, [12, 34, 56, 43]); console.log(max); //56 var max2 = Math.max(...[12, 34, 56, 43]); console.log(max2); //56
-
Array 類的擴展方法
- Array.from()
Array.from 方法用于將兩類對象轉(zhuǎn)為真正的數(shù)組:類似數(shù)組的對象(array-like object)和可遍歷(iterable)的對象(包括 ES6 新增的數(shù)據(jù)結(jié)構(gòu) Set 和 Map)
var arraylike = { 0: 'a', 1: 'b', 2: 'c', length: 3, }; var arr = Array.from(arraylike); ['a', 'b', 'c'];
Array.from 還可以接受第二個參數(shù),作用類似于數(shù)組的
map
方法浩淘,用來對每個元素進行處理捌朴,將處理后的值放入返回的數(shù)組var arr = Array.from([1, 2, 3], function (x) { return x * x; }); console.log(arr); //[1,4,9]
-
Array.of 方法用于將一組值吴攒,轉(zhuǎn)換為數(shù)組,這個方法的主要目的砂蔽,是彌補數(shù)組構(gòu)造函數(shù)
Array()
的不足洼怔。因為參數(shù)個數(shù)的不同,會導(dǎo)致Array()
的行為有差異左驾。var arr = Array(3); //[emptyx3]
這里面 3 表示數(shù)組中元素的長度镣隶。
var arr = Array(2, 3, 4); //[2,3,4]
當
Array()
里的參數(shù)個數(shù)大于 1 的時候,表示的是數(shù)組元素诡右。Array.of()
方法不管里面參數(shù)的個數(shù)多少安岂,都將其轉(zhuǎn)為數(shù)組的元素。var arr = Array.of(3); console.log(arr); //[3]
對象
1.對象解構(gòu)賦值
解構(gòu)不僅可以用于數(shù)組帆吻,還可以用于對象域那。對象的解構(gòu)與數(shù)組有一個重要的不同。數(shù)組的元素是按次序排列的猜煮,變量的取值由它的位置決定次员;而對象的屬性沒有次序,變量必須與屬性同名友瘤,才能取到正確的值翠肘。
let { name, work } = { name: 'glh', work: 'code' };
console.log(name, work); // glh code
2.對象的擴展
-
對象的簡寫
-
當變量名和屬性名同名式,省略同名的屬性值
const foo = 'bar'; const baz = { foo }; // 等同于 const baz = { foo: foo };
-
省略方法中的 function
const obj = { method() { return 'hello'; }, }; // 等同于 const obj = { method: function () { return 'hello'; }, };
-
屬性的賦值器(setter)和取值器(getter)
const obj = { name: 'glh', get com() { return this.name; }, set work(value) { this.name = this.name + value; }, }; console.log(obj.com); // glh obj.work = 'hello'; console.log(obj.name); //glhhello
-
屬性名表達式
es5 中定義對象的屬性有兩種方法辫秧,一種是用標識符(本質(zhì)為字符串)做屬性束倍,一種是用表達式做屬性
方法一; obj.name = 'a'; // 方法二 obj['name'] = 'a'; var obj = { name: 'a', };
如果使用大括號定義對象,那么在 es5 中只能使用標識符定義屬性盟戏。
var obj = { name: 'a', };
但是 ECMAScript2015 在使用大括號定義對象的時候绪妹,允許使用表達式定義屬性,把表達式放在方括號中柿究。
let name = 'a'; const obj = { [name]: 'glh', }; console.log(obj); //{a: "glh"}
-
3.擴展運算符
-
用于對象的解構(gòu)
-
對象的解構(gòu)賦值用于從一個對象取值邮旷,相當于將目標對象自身的所有可遍歷的(enumerable)、但尚未被讀取的屬性蝇摸,分配到指定的對象上面婶肩。所有的鍵和它們的值,都會拷貝到新對象上面貌夕。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; console.log(z); //{a: 3, b: 4}
上面代碼中律歼,變量
z
是解構(gòu)賦值所在的對象。它獲取等號右邊的所有尚未讀取的鍵(a
和b
)啡专,將它們連同值一起拷貝過來险毁。注意: 1.解構(gòu)賦值必須是最后一個參數(shù)。2.解構(gòu)賦值的拷貝是淺拷貝。
-
-
用于擴展運算
-
對象的擴展運算符(
...
)用于取出參數(shù)對象的所有可遍歷屬性畔况,拷貝到當前對象之中鲸鹦。let z = { name: 'a', work: 'b' }; let n = { ...z }; n; // { name: "a", work: "b" }
-
字符串
1.字符串模板
傳統(tǒng)的字符串里不能使用換行符,必須使用轉(zhuǎn)義符\n 替代跷跪,字符串模板里可以使用馋嗜。模板字符串(template string)是增強版的字符串,用反引號(`)標識域庇。它可以當作普通字符串使用嵌戈,也可以用來定義多行字符串,或者在字符串中嵌入變量听皿。
-
模板字符串中嵌入變量,需要將變量名寫在
${}
之中宽档。大括號內(nèi)部可以放入任意的 JavaScript 表達式尉姨,可以進行運算,以及引用對象屬性var name = 'glh'; var newName = `歡迎來到${name}`; console.log(newName);
2.標簽?zāi)0?/h4>
-
模板字符串的功能吗冤,不僅僅是上面這些又厉。它可以緊跟在一個函數(shù)名后面,該函數(shù)將被調(diào)用來處理這個模板字符串椎瘟。這被稱為“標簽?zāi)0濉惫δ堋?/p>
console.log`hello`;
等同于;
console.log(['hello']);
標簽?zāi)0迤鋵嵅皇悄0甯仓拢呛瘮?shù)調(diào)用的一種特殊形式》挝担“標簽”指的就是函數(shù)煌妈,緊跟在后面的模板字符串就是它的參數(shù)。
注意:如果模板字符里面有變量宣羊,就不是簡單的調(diào)用了璧诵,而是會將模板字符串先處理成多個參數(shù),再調(diào)用函數(shù)仇冯。
const name = 'Tony';
const age = 18;
// const string = `My name is ${name}, Age is ${age}`;
// console.log(string);
// 標簽函數(shù)
// const str = console.log`My name is ${name}, Age is ${age}`;
// 標簽函數(shù)作用是對字符串加工 返回一個新值之宿。利用這個特性可以做語言切換(國際化),模板引擎插件
const Tag = (string, ...rest) => {
console.log(string);
console.log(rest);
return string.join();
};
const result = Tag`My name is ${name}, Age is ${age}`;
console.log(result);
3.擴展的方法
-
字符串實例的方法
-
includes()
返回布爾值苛坚,表示是否找到了參數(shù)字符串
const name = 'glh';
const a = `Error: foo is not defined: ${name}`;
var b = a.includes('Err');
console.log(b); // true
-
startsWith()
返回布爾值比被,表示參數(shù)字符串是否在原字符串的頭部
const name = 'glh';
const a = `Error: foo is not defined: ${name}`;
const b = a.startsWith('Err');
console.log(b); / /true
-
endsWith()
返回布爾值,表示參數(shù)字符串是否在原字符串的尾部
const name = 'glh';
const a = `Error: foo is not defined: ${name}`;
const b = a.endsWith('glh');
console.log(b); // true
函數(shù)
參數(shù)默認值
-
ES6 允許為函數(shù)的參數(shù)設(shè)置默認值泼舱,即直接寫在參數(shù)定義的后面等缀。
function fn(a, b = 'glh ') {
console.log(a + b);
}
fn('hello'); //hello glh
-
注意:
- 參數(shù)變量是默認聲明的,所以不能用
let
或const
再次聲明.
- 使用參數(shù)默認值時柠掂,函數(shù)不能有同名參數(shù)
-
參數(shù)默認值的位置
-
通常情況下项滑,定義了默認值的參數(shù),應(yīng)該是函數(shù)的尾參數(shù)。因為這樣比較容易看出來枪狂,到底省略了哪些參數(shù)危喉。如果非尾部的參數(shù)設(shè)置默認值,實際上這個參數(shù)是沒法省略的州疾。
function f(x = 1, y) {
return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined]
f(, 1) // 報錯
rest 參數(shù)
-
ES6 引入 rest 參數(shù)(形式為...變量名
)辜限,用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments
對象了严蓖。rest 參數(shù)搭配的變量是一個數(shù)組薄嫡,該變量將多余的參數(shù)放入數(shù)組中。
function add(...values) {
console.log(values);
}
add(2, 5, 3); // [2, 5, 3]
-
rest 參數(shù)和函數(shù)中的參數(shù)解構(gòu)有什么區(qū)別
- rest 參數(shù)是發(fā)生在函數(shù)的定義階段颗胡,函數(shù)的額參數(shù)解構(gòu)是發(fā)生在函數(shù)的調(diào)用階段
- 二者互為逆運算
function add(...values) {
//這是rest參數(shù)
console.log(values);
}
add(2, 5, 3); // [2, 5, 3]
var arr = [1, 2, 3];
function fn(a, b, c) {
console.log(a + b + c);
}
fn(...arr); //6 這是參數(shù)的解構(gòu)
箭頭函數(shù)
- ES6 允許使用“箭頭”(
=>
)定義函數(shù)毫深。
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
模板字符串的功能吗冤,不僅僅是上面這些又厉。它可以緊跟在一個函數(shù)名后面,該函數(shù)將被調(diào)用來處理這個模板字符串椎瘟。這被稱為“標簽?zāi)0濉惫δ堋?/p>
console.log`hello`;
等同于;
console.log(['hello']);
標簽?zāi)0迤鋵嵅皇悄0甯仓拢呛瘮?shù)調(diào)用的一種特殊形式》挝担“標簽”指的就是函數(shù)煌妈,緊跟在后面的模板字符串就是它的參數(shù)。
注意:如果模板字符里面有變量宣羊,就不是簡單的調(diào)用了璧诵,而是會將模板字符串先處理成多個參數(shù),再調(diào)用函數(shù)仇冯。
const name = 'Tony';
const age = 18;
// const string = `My name is ${name}, Age is ${age}`;
// console.log(string);
// 標簽函數(shù)
// const str = console.log`My name is ${name}, Age is ${age}`;
// 標簽函數(shù)作用是對字符串加工 返回一個新值之宿。利用這個特性可以做語言切換(國際化),模板引擎插件
const Tag = (string, ...rest) => {
console.log(string);
console.log(rest);
return string.join();
};
const result = Tag`My name is ${name}, Age is ${age}`;
console.log(result);
字符串實例的方法
-
includes()
返回布爾值苛坚,表示是否找到了參數(shù)字符串
const name = 'glh'; const a = `Error: foo is not defined: ${name}`; var b = a.includes('Err'); console.log(b); // true
-
startsWith()
返回布爾值比被,表示參數(shù)字符串是否在原字符串的頭部
const name = 'glh'; const a = `Error: foo is not defined: ${name}`; const b = a.startsWith('Err'); console.log(b); / /true
-
endsWith()
返回布爾值,表示參數(shù)字符串是否在原字符串的尾部
const name = 'glh'; const a = `Error: foo is not defined: ${name}`; const b = a.endsWith('glh'); console.log(b); // true
ES6 允許為函數(shù)的參數(shù)設(shè)置默認值泼舱,即直接寫在參數(shù)定義的后面等缀。
function fn(a, b = 'glh ') {
console.log(a + b);
}
fn('hello'); //hello glh
注意:
- 參數(shù)變量是默認聲明的,所以不能用
let
或const
再次聲明. - 使用參數(shù)默認值時柠掂,函數(shù)不能有同名參數(shù)
參數(shù)默認值的位置
-
通常情況下项滑,定義了默認值的參數(shù),應(yīng)該是函數(shù)的尾參數(shù)。因為這樣比較容易看出來枪狂,到底省略了哪些參數(shù)危喉。如果非尾部的參數(shù)設(shè)置默認值,實際上這個參數(shù)是沒法省略的州疾。
function f(x = 1, y) { return [x, y]; } f() // [1, undefined] f(2) // [2, undefined] f(, 1) // 報錯
ES6 引入 rest 參數(shù)(形式為...變量名
)辜限,用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments
對象了严蓖。rest 參數(shù)搭配的變量是一個數(shù)組薄嫡,該變量將多余的參數(shù)放入數(shù)組中。
function add(...values) {
console.log(values);
}
add(2, 5, 3); // [2, 5, 3]
rest 參數(shù)和函數(shù)中的參數(shù)解構(gòu)有什么區(qū)別
- rest 參數(shù)是發(fā)生在函數(shù)的定義階段颗胡,函數(shù)的額參數(shù)解構(gòu)是發(fā)生在函數(shù)的調(diào)用階段
- 二者互為逆運算
function add(...values) {
//這是rest參數(shù)
console.log(values);
}
add(2, 5, 3); // [2, 5, 3]
var arr = [1, 2, 3];
function fn(a, b, c) {
console.log(a + b + c);
}
fn(...arr); //6 這是參數(shù)的解構(gòu)
=>
)定義函數(shù)毫深。var f = v => v;
// 等同于
var f = function (v) {
return v;
};
如果箭頭函數(shù)不需要參數(shù)或需要多個參數(shù),就使用一個圓括號代表參數(shù)部分毒姨。
var f = () => 5;
// 等同于
var f = function () {
return 5;
};
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function (num1, num2) {
return num1 + num2;
};
如果箭頭函數(shù)的代碼塊部分多于一條語句哑蔫,就要使用大括號將它們括起來,并且使用return
語句返回弧呐。
var sum = (num1, num2) => {
return num1 + num2;
};
由于大括號被解釋為代碼塊闸迷,所以如果箭頭函數(shù)直接返回一個對象,必須在對象外面加上括號俘枫,否則會報錯腥沽。
let getItem = id => { id: id, name: "Temp" }; //報錯
let getItem = id => ({ id: id, name: "Temp" });//不報錯
- 箭頭函數(shù)有幾個使用注意點
- 函數(shù)體內(nèi)的
this
對象,就是定義時所在的對象鸠蚪,而不是調(diào)用時所在的對象今阳。 - 不可以當作構(gòu)造函數(shù),也就是說邓嘹,不可以使用
new
命令酣栈,否則會拋出一個錯誤。 - 不可以使用
arguments
對象汹押,該對象在函數(shù)體內(nèi)不存在矿筝。如果要用,可以用 rest 參數(shù)代替棚贾。
- 函數(shù)體內(nèi)的
var name = "web"
var obj={
name: "a",
fn(){
var t = setTimeout(function() {
console.log(this.name)// web this是window(瀏覽器環(huán)境)
},1000)
}
}
obj.fn()
-----------------------------------
var name = "web"
var obj={
name: "a",
fn(){
var t = setTimeout(() => {
console.log(this.name)// a this是obj
},1000)
}
}
obj.fn()
Object
Object.assign()
-
Object.assign
方法用于對象的合并窖维,將源對象(source)的所有可枚舉屬性,復(fù)制到目標對象(target)妙痹。const target = { a: 123, b: 123, }; const sourcel = { a: 456, c: 456, }; const result = Object.assign(target, sourcel); console.log(target); //{a: 456, b: 123, c: 456} console.log(target === result); //true
如果目標對象與源對象有同名屬性铸史,則后面的屬性會覆蓋前面的屬性。且
assign()
的返回值就是第一個對象怯伊。如果有多個源對象有同名屬性琳轿,依然是后面的會覆蓋前面的屬性
const target = { a: 1, b: 1 }; const source1 = { b: 2, c: 2 }; const source2 = { c: 3 }; Object.assign(target, source1, source2); target; // {a:1, b:2, c:3}
-
利用
Object.assign()
復(fù)制一個對象,且其中一個對象的修改不會影響到另一個對象const source = { a: 123, }; var obj = Object.assign({}, source); obj.a = 456; console.log(obj); //{a: 456} console.log(source); //{a: 123}
Object.is()
-
Object.is
就是用來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行為基本一致崭篡。ES5 比較兩個值是否相等挪哄,只有兩個運算符:相等運算符(
==
)和嚴格相等運算符(===
)。它們都有缺點琉闪,前者會自動轉(zhuǎn)換數(shù)據(jù)類型迹炼,后者的NaN
不等于自身,以及+0
等于-0
颠毙。JavaScript 缺乏一種運算斯入,在所有環(huán)境中,只要兩個值是一樣的蛀蜜,它們就應(yīng)該相等刻两。console.log(Object.is(+0, -0)); //false console.log(+0 === -0); //true console.log(Object.is(NaN, NaN)); //true console.log(NaN === NaN); //false
Proxy
概述
Proxy 可以理解成一個快遞員,我們發(fā)快遞還是接受快遞涵防,都需要這個快遞員充當一個代理的作用闹伪。ES6 原生提供 Proxy 構(gòu)造函數(shù),用來生成 Proxy 實例壮池,這個實例就是一個代理對象(快遞員)。
var proxy = new Proxy(target, handler);
目標對象
這個代理對象有兩個參數(shù)杀怠,一個是代理的目標對象椰憋,第二個也是一個對象,它是配置對象赔退,用來定制代理的攔截行為橙依。
const person = {
name: 'glh',
age: 20,
};
const personProxy = new Proxy(person, {
get(target, property) {
console.log(target, property); //person{name:"glh",age:20}
return 100;
},
set() {},
});
配置對象
-
配置對象中一般有兩個方法
get
和set
,get
是用來攔截對目標對象屬性的訪問請求。get
方法中有兩個參數(shù)硕旗,第一個參數(shù)是目標對象窗骑,第二個參數(shù)是訪問的那個屬性。const person = { name: 'glh', age: 20, }; const personProxy = new Proxy(person, { get(target, property) { console.log(target, property); return 100; }, set() {}, }); console.log(personProxy.name); //100
注意漆枚,這個get
方法的返回值就是我們獲取的這個屬性的返回值创译。
-
這個
get
方法中有三個參數(shù),一個是代理的目標對象墙基,一個是代理的處理對象软族,第三個參數(shù)是 proxy 實例本身,且第三個參數(shù)是可選參數(shù)残制。const person = { name: 'glh', age: 20, }; const personProxy = new Proxy(person, { get(target, property, o) { console.log(o); //proxy {name:"glh",age:20} return property in target ? target[property] : undefined; }, set() {}, }); console.log(personProxy.age); //20
-
這個
set
方法用來攔截某個屬性的賦值操作立砸,可以接受四個參數(shù),依次為目標對象初茶、屬性名颗祝、屬性值和 Proxy 實例本身,其中最后一個參數(shù)可選const person = { name: 'glh', age: 20, }; const personProxy = new Proxy(person, { get(target, property, o) { return property in target ? target[property] : undefined; }, set(obj, pro, value, o) { console.log(obj, pro, value, o); }, }); console.log((personProxy.name = 'x')); //{name: "glh", age: 20} "name" "x" Proxy {name: "zce", age: 20}
可以去設(shè)置一些屬性或修改
const person = { name: 'glh', age: 20, }; const personProxy = new Proxy(person, { get(target, property, o) { return property in target ? target[property] : undefined; }, set(target, pro, value, o) { //可以做一些內(nèi)部校驗 target[pro] = value; }, }); console.log((personProxy.name = 'hello')); person; // {name:"glh",age:20}
Reflect
概述
Reflect
對象與Proxy
對象一樣,也是 ES6 為了操作對象而提供的新 API螺戳。Reflect
對象的設(shè)計目的有這樣幾個搁宾。
將
Object
對象的一些明顯屬于語言內(nèi)部的方法(比如Object.defineProperty
),放到Reflect
對象上∥虑停現(xiàn)階段猛铅,某些方法同時在Object
和Reflect
對象上部署,未來的新方法將只部署在Reflect
對象上凤藏。也就是說奸忽,從Reflect
對象上可以拿到語言內(nèi)部的方法-
修改某些
Object
方法的返回結(jié)果,讓其變得更合理揖庄。比如栗菜,Object.defineProperty(obj, name, desc)
在無法定義屬性時,會拋出一個錯誤蹄梢,而Reflect.defineProperty(obj, name, desc)
則會返回false
// 老寫法 try { Object.defineProperty(target, property, attributes); // success } catch (e) { // failure } // 新寫法 if (Reflect.defineProperty(target, property, attributes)) { // success } else { // failure }
-
讓
Object
操作都變成函數(shù)行為疙筹。某些Object
操作是命令式,比如name in obj
和delete obj[name]
禁炒,而Reflect.has(obj, name)
和Reflect.deleteProperty(obj, name)
讓它們變成了函數(shù)行為而咆。// 老寫法 'assign' in Object; // true // 新寫法 Reflect.has(Object, 'assign'); // true
靜態(tài)方法
-
Reflect.get
-
Reflect.get(target, name, receiver)
,Reflect.get
方法查找并返回target
對象的name
屬性,如果沒有該屬性幕袱,則返回undefined
var myObject = { foo: 1, bar: 2, get baz() { return this.foo + this.bar; }, }; Reflect.get(myObject, 'foo'); // 1 Reflect.get(myObject, 'bar'); // 2 Reflect.get(myObject, 'baz'); // 3
-
如果
name
屬性部署了讀取函數(shù)(getter)暴备,則讀取函數(shù)的this
綁定receiver
。var myObject = { foo: 1, bar: 2, get baz() { return this.foo + this.bar; }, }; var myReceiverObject = { foo: 4, bar: 4, }; Reflect.get(myObject, 'baz', myReceiverObject); // 8
-
如果第一個參數(shù)不是對象们豌,
Reflect.get
方法會報錯涯捻。Reflect.get(1, 'foo'); // 報錯 Reflect.get(false, 'foo'); // 報錯
-
-
Reflect.set
-
Reflect.set(target, name, value, receiver)
,Reflect.set
方法設(shè)置target
對象的name
屬性等于value
var myObject = { foo: 1, }; myObject.foo; // 1 Reflect.set(myObject, 'foo', 2); myObject.foo; // 2
-
如果
name
屬性設(shè)置了賦值函數(shù),則賦值函數(shù)的this
綁定receiver
望迎。var myObject = { foo: 4, set bar(value) { return (this.foo = value); }, }; var myReceiverObject = { foo: 0, }; Reflect.set(myObject, 'bar', 1, myReceiverObject); myObject.foo; // 4 myReceiverObject.foo; // 1
-
-
Reflect.has
-
Reflect.has(obj, name)
,Reflect.has
方法對應(yīng)name in obj
里面的in
運算符var myObject = { foo: 1, }; // 舊寫法 'foo' in myObject; // true // 新寫法 Reflect.has(myObject, 'foo'); // true
如果
Reflect.has()
方法的第一個參數(shù)不是對象障癌,會報錯。
-
-
Reflect.deleteProperty
-
Reflect.deleteProperty(obj, name),
Reflect.deleteProperty
方法等同于delete obj[name]
辩尊,用于刪除對象的屬性const myObj = { foo: 'bar' }; // 舊寫法 delete myObj.foo; // 新寫法 Reflect.deleteProperty(myObj, 'foo');
該方法返回一個布爾值涛浙。如果刪除成功,或者被刪除的屬性不存在对省,返回
true
蝗拿;刪除失敗,被刪除的屬性依然存在蒿涎,返回false
哀托。如果Reflect.deleteProperty()
方法的第一個參數(shù)不是對象,會報錯.
-
Promise
概述
Promise 是異步編程的一種解決方案劳秋,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大仓手。它由社區(qū)最早提出和實現(xiàn)胖齐,ES6 將其寫進了語言標準,統(tǒng)一了用法嗽冒,原生提供了Promise
對象呀伙。
所謂Promise
,簡單說就是一個容器添坊,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果剿另。從語法上說,Promise 是一個對象贬蛙,從它可以獲取異步操作的消息雨女。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進行處理阳准。
promise 特點
Promise
對象有以下兩個特點氛堕。
(1)對象的狀態(tài)不受外界影響。Promise
對象代表一個異步操作野蝇,有三種狀態(tài):pending
(進行中)讼稚、fulfilled
(已成功)和rejected
(已失敗)绕沈。只有異步操作的結(jié)果锐想,可以決定當前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài)乍狐。這也是Promise
這個名字的由來痛倚,它的英語意思就是“承諾”,表示其他手段無法改變澜躺。
(2)一旦狀態(tài)改變,就不會再變抒蚜,任何時候都可以得到這個結(jié)果掘鄙。Promise
對象的狀態(tài)改變,只有兩種可能:從pending
變?yōu)?code>fulfilled和從pending
變?yōu)?code>rejected嗡髓。只要這兩種情況發(fā)生操漠,狀態(tài)就凝固了,不會再變了饿这,會一直保持這個結(jié)果浊伙,這時就稱為 resolved(已定型)。如果改變已經(jīng)發(fā)生了长捧,你再對Promise
對象添加回調(diào)函數(shù)嚣鄙,也會立即得到這個結(jié)果。這與事件(Event)完全不同串结,事件的特點是哑子,如果你錯過了它舅列,再去監(jiān)聽,是得不到結(jié)果的卧蜓。
有了Promise
對象帐要,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調(diào)函數(shù)弥奸。此外榨惠,Promise
對象提供統(tǒng)一的接口,使得控制異步操作更加容易盛霎。
promise 使用方法
ES6 規(guī)定赠橙,
Promise
對象是一個構(gòu)造函數(shù),用來生成Promise
實例摩渺。-
Promise
構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù)简烤,該函數(shù)的兩個參數(shù)分別是resolve
和reject
。它們是兩個函數(shù)摇幻,由 JavaScript 引擎提供横侦,不用自己部署。var p = new Promise(function (resolve, reject) { if (true) { resolve(data); } else { reject(data); } });
resolve
函數(shù)的作用是绰姻,將Promise
對象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?pending 變?yōu)?resolved)枉侧,在異步操作成功時調(diào)用葫慎,并將異步操作的結(jié)果担巩,作為參數(shù)傳遞出去;reject
函數(shù)的作用是酝陈,將Promise
對象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending 變?yōu)?rejected)帜矾,在異步操作失敗時調(diào)用翼虫,并將異步操作報出的錯誤,作為參數(shù)傳遞出去屡萤。 -
Promise
實例生成以后珍剑,可以用then
方法分別指定resolved
狀態(tài)和rejected
狀態(tài)的回調(diào)函數(shù)。p.then( function (value) { // success業(yè)務(wù)處理 }, function (error) { // failure } );
then
方法可以接受兩個回調(diào)函數(shù)作為參數(shù)死陆。第一個回調(diào)函數(shù)是Promise
對象的狀態(tài)變?yōu)?code>resolved時調(diào)用招拙,第二個回調(diào)函數(shù)是Promise
對象的狀態(tài)變?yōu)?code>rejected時調(diào)用。其中措译,第二個函數(shù)是可選的别凤,不一定要提供。這兩個函數(shù)都接受Promise
對象傳出的值作為參數(shù)领虹。-
看一個簡單的例子
function time(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms); }); } time(1000).then(value => { console.log(value); });
-
-
Promise 新建后就會立即執(zhí)行规哪。
let promise = new Promise(function (resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function () { console.log('resolved.'); }); console.log('Hi!'); //Promise //Hi //resolved
class
概述
ES6 提供了更接近傳統(tǒng)語言的寫法,引入了 Class(類)這個概念掠械,作為對象的模板由缆。通過class
關(guān)鍵字注祖,可以定義類【Γ基本上是晨,ES6 的class
可以看作只是一個語法糖,它的絕大部分功能舔箭,ES5 都可以做到罩缴,新的class
寫法只是讓對象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法而已层扶。上面的代碼用 ES6 的class
改寫箫章,就是下面這樣。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
基本介紹
-
constructor()
-
constructor
方法是類的默認方法镜会,通過new
命令生成對象實例時檬寂,自動調(diào)用該方法。一個類必須有constructor
方法戳表,如果沒有顯式定義桶至,一個空的constructor
方法會被默認添加。
-
-
類的實例
-
生成類的實例的寫法匾旭,與 ES5 完全一樣镣屹,也是使用
new
命令。前面說過价涝,如果忘記加上new
女蜈,像函數(shù)那樣調(diào)用Class
,將會報錯色瘩。class Point { // ... } // 報錯 var point = Point(2, 3); // 正確 var point = new Point(2, 3);
-
與 ES5 一樣伪窖,實例的屬性除非顯式定義在其本身(即定義在
this
對象上),否則都是定義在原型上(即定義在class
上)居兆。class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } var point = new Point(2, 3); point.toString(); // (2, 3) point.hasOwnProperty('x'); // true point.hasOwnProperty('y'); // true point.hasOwnProperty('toString'); // false point.__proto__.hasOwnProperty('toString'); // true
-
與 ES5 一樣惰许,類的所有實例共享一個原型對象
var p1 = new Point(2, 3); var p2 = new Point(3, 2); p1.__proto__ === p2.__proto__; //true
-
-
getter 和 setter
-
與 ES5 一樣,在“類”的內(nèi)部可以使用
get
和set
關(guān)鍵字史辙,對某個屬性設(shè)置存值函數(shù)和取值函數(shù),攔截該屬性的存取行為佩伤。class MyClass { constructor() { // ... } get prop() { return 'getter'; } set prop(value) { console.log('setter: ' + value); } } let inst = new MyClass(); inst.prop = 123; // setter: 123 inst.prop; // 'getter'
-
-
屬性表達式
-
類的屬性名聊倔,可以采用表達式
let methodName = 'getArea'; class Square { constructor(length) { // ... } [methodName]() { // ... } }
上面代碼中,
Square
類的方法名getArea
生巡,是從表達式得到的
-
static
-
類相當于實例的原型耙蔑,所有在類中定義的方法,都會被實例繼承孤荣。如果在一個方法前甸陌,加上
static
關(guān)鍵字须揣,就表示該方法不會被實例繼承,而是直接通過類來調(diào)用钱豁,這就稱為“靜態(tài)方法”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
關(guān)鍵字,表明該方法是一個靜態(tài)方法牲尺,可以直接在Foo
類上調(diào)用(Foo.classMethod()
)卵酪,而不是在Foo
類的實例上調(diào)用。如果在實例上調(diào)用靜態(tài)方法谤碳,會拋出一個錯誤溃卡,表示不存在該方法。 -
注意蜒简,如果靜態(tài)方法包含
this
關(guān)鍵字瘸羡,這個this
指的是類,而不是實例class Foo { static bar() { this.baz(); } static baz() { console.log('hello'); } baz() { console.log('world'); } } Foo.bar(); // hello
解說:上面代碼中搓茬,靜態(tài)方法
bar
調(diào)用了this.baz
犹赖,這里的this
指的是Foo
類,而不是Foo
的實例垮兑,等同于調(diào)用Foo.baz
冷尉。另外,從這個例子還可以看出系枪,靜態(tài)方法可以與非靜態(tài)方法重名雀哨。 -
父類的靜態(tài)方法,可以被子類繼承
class Foo { static classMethod() { return 'hello'; } } class Bar extends Foo {} Bar.classMethod(); // 'hello'
-
靜態(tài)屬性
靜態(tài)屬性指的是 Class 本身的屬性私爷,即
Class.propName
雾棺,而不是定義在實例對象(this
)上的屬性ES6 明確規(guī)定,Class 內(nèi)部只有靜態(tài)方法衬浑,沒有靜態(tài)屬性“坪疲現(xiàn)在有一個提案提供了類的靜態(tài)屬性,寫法是在實例屬性的前面工秩,加上
static
關(guān)鍵字尸饺。class MyClass { static myStaticProp = 42; constructor() { console.log(MyClass.myStaticProp); // 42 } }
繼承
-
簡介
-
Class 可以通過
extends
關(guān)鍵字實現(xiàn)繼承,這比 ES5 的通過修改原型鏈實現(xiàn)繼承助币,要清晰和方便很多浪听。class Point {} class ColorPoint extends Point {}
解說:上面代碼定義了一個
ColorPoint
類,該類通過extends
關(guān)鍵字眉菱,繼承了Point
類的所有屬性和方法迹栓。但是由于沒有部署任何代碼,所以這兩個類完全一樣俭缓,等于復(fù)制了一個Point
類 -
子類必須在
constructor
方法中調(diào)用super
方法克伊,否則新建實例時會報錯酥郭。這是因為子類自己的this
對象,必須先通過父類的構(gòu)造函數(shù)完成塑造愿吹,得到與父類同樣的實例屬性和方法不从,然后再對其進行加工,加上子類自己的實例屬性和方法洗搂。如果不調(diào)用super
方法消返,子類就得不到this
對象。class Point { /* ... */ } class ColorPoint extends Point { constructor() {} } let cp = new ColorPoint(); // ReferenceError
-
在子類的構(gòu)造函數(shù)中耘拇,只有調(diào)用
super
之后撵颊,才可以使用this
關(guān)鍵字,否則會報錯惫叛。這是因為子類實例的構(gòu)建倡勇,基于父類實例,只有super
方法才能調(diào)用父類實例嘉涌。class Point { constructor(x, y) { this.x = x; this.y = y; } } class ColorPoint extends Point { constructor(x, y, color) { this.color = color; // ReferenceError super(x, y); this.color = color; // 正確 } }
-
-
super
-
super
這個關(guān)鍵字妻熊,既可以當作函數(shù)使用,也可以當作對象使用仑最。在這兩種情況下扔役,它的用法完全不同-
第一種情況,
super
作為函數(shù)調(diào)用時警医,代表父類的構(gòu)造函數(shù)亿胸。ES6 要求,子類的構(gòu)造函數(shù)必須執(zhí)行一次super
函數(shù)预皇。class A {} class B extends A { constructor() { super(); } }
解說:上面代碼中侈玄,子類
B
的構(gòu)造函數(shù)之中的super()
,代表調(diào)用父類的構(gòu)造函數(shù)吟温。這是必須的序仙,否則 JavaScript 引擎會報錯。super
雖然代表了父類A
的構(gòu)造函數(shù)鲁豪,但是返回的是子類B
的實例潘悼,即super
內(nèi)部的this
指的是B的實例,因此
super()在這里相當于
A.prototype.constructor.call(this)` -
第二種情況爬橡,
super
作為對象時挥等,在普通方法中,指向父類的原型對象堤尾;在靜態(tài)方法中,指向父類class A { p() { return 2; } } class B extends A { constructor() { super(); console.log(super.p()); // 2 } } let b = new B();
-
由于
super
指向父類的原型對象(prototype)迁客,所以定義在父類實例上的方法或?qū)傩怨Γ菬o法通過super
調(diào)用的class A { constructor() { this.p = 2; } } class B extends A { get m() { return super.p; } } let b = new B(); b.m; // undefined
-
-
Set
基本用法
ES6 提供了新的數(shù)據(jù)結(jié)構(gòu) Set辞槐。它類似于數(shù)組,但是成員的值都是唯一的粘室,沒有重復(fù)的值榄檬。
Set
本身是一個構(gòu)造函數(shù),用來生成 Set 數(shù)據(jù)結(jié)構(gòu)衔统。-
Set
函數(shù)可以接受一個數(shù)組(或者具有 iterable 接口的其他數(shù)據(jù)結(jié)構(gòu))作為參數(shù)鹿榜,用來初始化。const set = new Set([1, 2, 3, 4, 4]); console.log(set); //[1,2,3,4]
-
數(shù)組去重
[...new Set([1, 2, 3, 2, 4, 5])]; //[1,2,3,4,5]
-
字符串去重
[...new Set('ababbc')].join(''); //"abc"
-
屬性和方法
-
Set 結(jié)構(gòu)的實例有以下屬性锦爵。
-
Set.prototype.constructor
:構(gòu)造函數(shù)舱殿,默認就是Set
函數(shù)。 -
Set.prototype.size
:返回Set
實例的成員總數(shù)险掀。
-
-
Set 實例的方法分為兩大類
-
操作方法(用于操作數(shù)據(jù))
-
Set.prototype.add(value)
:添加某個值沪袭,返回 Set 結(jié)構(gòu)本身const items = new Set([]); items.add(1).add(2).add(3); console.dir(items); //[1,2,3]
-
Set.prototype.delete(value)
:刪除某個值,返回一個布爾值樟氢,表示刪除是否成功const items = new Set([12, 23, 34]); var b = items.delete(12); console.log(b); //true console.log(items); //Set(2) {23, 34}
Set.prototype.has(value)
:返回一個布爾值冈绊,表示該值是否為Set
的成員。-
Set.prototype.clear()
:清除所有成員埠啃,沒有返回值const items = new Set([12, 23, 34]); var b = items.clear(12); console.log(b); //undefined console.log(items); //Set(0) {}
-
-
遍歷方法(用于遍歷成員)
Set.prototype.keys()
:返回鍵名的遍歷器Set.prototype.values()
:返回鍵值的遍歷器-
Set.prototype.entries()
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"]
-
Set.prototype.forEach()
let set = new Set([1, 4, 9]); set.forEach((value, key) => console.log(key + ' : ' + value)); // 1 : 1 // 4 : 4 // 9 : 9
-
Map
概述
JavaScript 的對象(Object)死宣,本質(zhì)上是鍵值對的集合(Hash 結(jié)構(gòu)),但是傳統(tǒng)上只能用字符串當作鍵碴开。這給它的使用帶來了很大的限制毅该。為了解決這個問題,ES6 提供了 Map 數(shù)據(jù)結(jié)構(gòu)叹螟。它類似于對象鹃骂,也是鍵值對的集合,但是“鍵”的范圍不限于字符串罢绽,各種類型的值(包括對象)都可以當作鍵畏线。也就是說,Object 結(jié)構(gòu)提供了“字符串—值”的對應(yīng)良价,Map 結(jié)構(gòu)提供了“值—值”的對應(yīng)寝殴,是一種更完善的 Hash 結(jié)構(gòu)實現(xiàn)。如果你需要“鍵值對”的數(shù)據(jù)結(jié)構(gòu)明垢,Map 比 Object 更合適蚣常。
基本用法
-
作為構(gòu)造函數(shù),Map 也可以接受一個數(shù)組作為參數(shù)痊银。該數(shù)組的成員是一個個表示鍵值對的數(shù)組
const map = new Map([ ['name', '張三'], ['title', 'Author'], ]); map.size; // 2 map.has('name'); // true map.get('name'); // "張三" map.has('title'); // true map.get('title'); // "Author"
屬性和方法
-
size 屬性抵蚊,
size
屬性返回 Map 結(jié)構(gòu)的成員總數(shù)。const map = new Map(); map.set('foo', true); map.set('bar', false); map.size; // 2
-
Map.prototype.set(key, value)
set
方法設(shè)置鍵名key
對應(yīng)的鍵值為value
,然后返回整個 Map 結(jié)構(gòu)贞绳。如果key
已經(jīng)有值谷醉,則鍵值會被更新,否則就新生成該鍵冈闭, set()方法返回的是 set 對象可以采用鏈式寫法const m = new Map(); m.set('edition', 6); // 鍵是字符串 m.set(262, 'standard'); // 鍵是數(shù)值 m.set(undefined, 'nah'); // 鍵是 undefined
-
Map.prototype.get(key)
get
方法讀取key
對應(yīng)的鍵值俱尼,如果找不到key
,返回undefined
萎攒。const m = new Map(); const hello = function () { console.log('hello'); }; m.set(hello, 'Hello ES6!'); // 鍵是函數(shù) m.get(hello); // Hello ES6!
-
Map.prototype.has(key)
has
方法返回一個布爾值遇八,表示某個鍵是否在當前 Map 對象之中const m = new Map(); m.set('edition', 6); m.set(262, 'standard'); m.set(undefined, 'nah'); m.has('edition'); // true m.has('years'); // false m.has(262); // true m.has(undefined); // true
-
Map.prototype.delete(key)
delete
方法刪除某個鍵,返回true
耍休。如果刪除失敗刃永,返回false
。const m = new Map(); m.set(undefined, 'nah'); m.has(undefined); // true m.delete(undefined); m.has(undefined); // false
-
Map.prototype.clear()
clear
方法清除所有成員羹应,沒有返回值let map = new Map(); map.set('foo', true); map.set('bar', false); map.size; // 2 map.clear(); map.size; // 0
遍歷
Map.prototype.keys()
:返回鍵名的遍歷器揽碘。Map.prototype.values()
:返回鍵值的遍歷器Map.prototype.entries()
:返回所有成員的遍歷器-
Map.prototype.forEach()
:遍歷 Map 的所有成員const map = new Map([ ['F', 'no'], ['T', 'yes'], ]); for (let key of map.keys()) { console.log(key); } // "F" // "T" for (let value of map.values()) { console.log(value); } // "no" // "yes" for (let item of map.entries()) { console.log(item[0], item[1]); } // "F" "no" // "T" "yes" // 或者 for (let [key, value] of map.entries()) { console.log(key, value); } // "F" "no" // "T" "yes" map.forEach(function (value, key, map) { console.log('Key: %s, Value: %s', key, value); });
forEach
方法還可以接受第二個參數(shù),用來綁定this
园匹。
Symbol
概述
ES5 的對象屬性名都是字符串雳刺,這容易造成屬性名的沖突。比如裸违,你使用了一個他人提供的對象掖桦,但又想為這個對象添加新的方法(mixin 模式),新方法的名字就有可能與現(xiàn)有方法產(chǎn)生沖突供汛。如果有一種機制枪汪,保證每個屬性的名字都是獨一無二的就好了,這樣就從根本上防止屬性名的沖突怔昨。這就是 ES6 引入Symbol
的原因雀久。ES6 引入了一種新的原始數(shù)據(jù)類型Symbol
,表示獨一無二的值趁舀。它是 JavaScript 語言的第七種數(shù)據(jù)類型赖捌,前六種是:undefined
、null
矮烹、布爾值(Boolean)越庇、字符串(String)、數(shù)值(Number)奉狈、對象(Object)卤唉。Symbol 值通過Symbol
函數(shù)生成。這就是說仁期,對象的屬性名現(xiàn)在可以有兩種類型桑驱,一種是原來就有的字符串竭恬,另一種就是新增的 Symbol 類型。凡是屬性名屬于 Symbol 類型熬的,就都是獨一無二的萍聊,可以保證不會與其他屬性名產(chǎn)生沖突。
var obj = {
say: 'a',
};
var say = Symbol(); //say 是symbol類型
obj[say] = 'web';
console.log(obj); //{say: "a", Symbol(): "web"}
語法
Symbol
函數(shù)前不能使用new
命令悦析,否則會報錯Symbol
函數(shù)可以接受一個字符串作為參數(shù),表示對 Symbol 實例的描述此衅,主要是為了在控制臺顯示强戴,或者轉(zhuǎn)為字符串時,比較容易區(qū)分-
每一個 Symbol 值都是不相等的挡鞍,這意味著 Symbol 值可以作為標識符骑歹,用于對象的屬性名,就能保證不會出現(xiàn)同名的屬性
var a = Symbol(); var b = Symbol(); console.log(a === b); //false var a = Symbol('a'); var b = Symbol('b'); console.log(a === b); //false
注意:
Symbol
函數(shù)的參數(shù)只是表示對當前 Symbol 值的描述墨微,因此相同參數(shù)的Symbol
函數(shù)的返回值是不相等的var a = Symbol('a'); var b = Symbol('a'); console.log(a === b); //false
-
Symbol 值不能與其他類型的值進行運算道媚,會報錯
let sym = Symbol('My symbol'); 'your symbol is ' + sym // TypeError: can't convert symbol to string `your symbol is ${sym}`; // TypeError: can't convert symbol to string
-
Symbol 值作為對象屬性名時,不能用點運算符
const mySymbol = Symbol(); const a = {}; a.mySymbol = 'Hello!'; console.log(a[mySymbol]); //undefined console.log(a['mySymbol']); //hello
-
Symbol 作為屬性名翘县,遍歷對象的時候最域,該屬性不會出現(xiàn)在
for...in
、for...of
循環(huán)中锈麸,也不會被Object.keys()
镀脂、Object.getOwnPropertyNames()
、JSON.stringify()
返回忘伞。但是薄翅,它也不是私有屬性,有一個Object.getOwnPropertySymbols()
方法氓奈,可以獲取指定對象的所有 Symbol 屬性名翘魄。該方法返回一個數(shù)組,成員是當前對象的所有用作屬性名的 Symbol 值舀奶。const obj = {}; let a = Symbol('a'); let b = Symbol('b'); obj[a] = 'Hello'; obj[b] = 'World'; const objectSymbols = Object.getOwnPropertySymbols(obj); console.log(objectSymbols); //[Symbol(a), Symbol(b)]
-
有時暑竟,我們希望重新使用同一個 Symbol 值,
Symbol.for()
方法可以做到這一點伪节。它接受一個字符串作為參數(shù)光羞,然后搜索有沒有以該參數(shù)作為名稱的 Symbol 值。如果有怀大,就返回這個 Symbol 值纱兑,否則就新建一個以該字符串為名稱的 Symbol 值,并將其注冊到全局化借。let s1 = Symbol.for('foo'); let s2 = Symbol.for('foo'); s1 === s2; // true
Symbol.for()
與Symbol()
這兩種寫法潜慎,都會生成新的 Symbol。它們的區(qū)別是,前者會被登記在全局環(huán)境中供搜索铐炫,后者不會垒手。Symbol.for()
不會每次調(diào)用就返回一個新的 Symbol 類型的值,而是會先檢查給定的key
是否已經(jīng)存在倒信,如果不存在才會新建一個值科贬。比如,如果你調(diào)用Symbol.for("cat")
30 次鳖悠,每次都會返回同一個 Symbol 值榜掌,但是調(diào)用Symbol("cat")
30 次,會返回 30 個不同的 Symbol 值Symbol.for('bar') === Symbol.for('bar'); // true Symbol('bar') === Symbol('bar');
由于
Symbol()
寫法沒有登記機制乘综,所以每次調(diào)用都會返回一個不同的值憎账。Symbol.for()
為 Symbol 值登記的名字,是全局環(huán)境的卡辰,不管有沒有在全局環(huán)境運行function foo() { return Symbol.for('bar'); } const x = foo(); const y = Symbol.for('bar'); console.log(x === y); // true
可迭代接口
Iterater 的概念
-
簡單介紹
JavaScript 原有的表示“集合”的數(shù)據(jù)結(jié)構(gòu)胞皱,主要是數(shù)組(
Array
)和對象(Object
),ES6 又添加了Map
和Set
九妈。這樣就有了四種數(shù)據(jù)集合反砌,用戶還可以組合使用它們,定義自己的數(shù)據(jù)結(jié)構(gòu)允蚣,比如數(shù)組的成員是Map
于颖,Map
的成員是對象。這樣就需要一種統(tǒng)一的接口機制嚷兔,來處理所有不同的數(shù)據(jù)結(jié)構(gòu)森渐。遍歷器(Iterator)就是這樣一種機制。它是一種接口冒晰,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機制同衣。任何數(shù)據(jù)結(jié)構(gòu)只要部署 Iterator 接口,就可以完成遍歷操作(即依次處理該數(shù)據(jù)結(jié)構(gòu)的所有成員)壶运。
Iterator 的作用有三個:一是為各種數(shù)據(jù)結(jié)構(gòu)耐齐,提供一個統(tǒng)一的、簡便的訪問接口蒋情;二是使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列埠况;三是 ES6 創(chuàng)造了一種新的遍歷命令
for...of
循環(huán),Iterator 接口主要供for...of
消費棵癣。 -
Iterator 的遍歷過程
創(chuàng)建一個指針對象辕翰,指向當前數(shù)據(jù)結(jié)構(gòu)的起始位置。也就是說狈谊,遍歷器對象本質(zhì)上喜命,就是一個指針對象沟沙。
第一次調(diào)用指針對象的
next
方法,可以將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個成員第二次調(diào)用指針對象的
next
方法壁榕,指針就指向數(shù)據(jù)結(jié)構(gòu)的第二個成員-
不斷調(diào)用指針對象的
next
方法矛紫,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置每一次調(diào)用
next
方法,都會返回數(shù)據(jù)結(jié)構(gòu)的當前成員的信息牌里。具體來說颊咬,就是返回一個包含value
和done
兩個屬性的對象。其中牡辽,value
屬性是當前成員的值贪染,done
屬性是一個布爾值,表示遍歷是否結(jié)束催享。簡單的Iterator遍歷器的實現(xiàn); var it = easyIterator(['a', 'b']); it.next(); // { value: "a", done: false } it.next(); // { value: "b", done: false } it.next(); // { value: undefined, done: true } function easyIterator(array) { var nextIndex = 0; return { next: function () { return nextIndex < array.length ? { value: array[nextIndex++], done: false } : { value: undefined, done: true }; }, }; }
Iterater 接口
-
字符串 數(shù)組 set map arguments 都有 iterater 接口,nodelist 集合哟绊,都可以用 for of 遍歷
var st = 'glh'; for (i of st) { console.log(i); // g l h } var arr = [1, 2]; for (v of arr) { console.log(v); //1 2 } function fn(a, b, c) { for (i of arguments) { console.log(i); //1 2 3 } } fn(1, 2, 3);
Modules
概述
- JavaScript 一直沒有模塊(module)體系因妙,無法將一個大程序拆分成互相依賴的小文件,再用簡單的方法拼裝起來票髓。其他語言都有這項功能攀涵,比如 Ruby 的
require
、Python 的import
洽沟,甚至就連 CSS 都有@import
以故,但是 JavaScript 任何這方面的支持都沒有,這對開發(fā)大型的裆操、復(fù)雜的項目形成了巨大障礙怒详。ES6 在語言標準的層面上,實現(xiàn)了模塊功能踪区,而且實現(xiàn)得相當簡單昆烁,完全可以取代 CommonJS 和 AMD 規(guī)范,成為瀏覽器和服務(wù)器通用的模塊解決方案缎岗。
語法
-
export
export
命令用于規(guī)定模塊的對外接口-
一個模塊就是一個獨立的文件静尼。該文件內(nèi)部的所有變量,外部無法獲取传泊。如果你希望外部能夠讀取模塊內(nèi)部的某個變量鼠渺,就必須使用
export
關(guān)鍵字輸出該變量。下面是一個 JS 文件眷细,里面使用export
命令輸出變量//demo.js export var firstName = 'Michael'; export var lastName = 'Jackson'; export var year = 1958; //或者 var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export { firstName, lastName, year };
-
import
import
命令用于輸入其他模塊提供的功能-
import
命令接受一對大括號拦盹,里面指定要從其他模塊導(dǎo)入的變量名。大括號里面的變量名薪鹦,必須與被導(dǎo)入模塊(profile.js
)對外接口的名稱相同// main.js import { firstName, lastName, year } from './profile.js'; function setName(element) { element.textContent = firstName + ' ' + lastName; }
-
export default
為了給用戶提供方便掌敬,就要用到
export default
命令惯豆,為模塊指定默認輸出本質(zhì)上,
export default
就是輸出一個叫做default
的變量或方法奔害,然后系統(tǒng)允許你為它取任意名字在
import
命令后面楷兽,不再使用大括號-
export default
命令用于指定模塊的默認輸出。顯然华临,一個模塊只能有一個默認輸出芯杀,因此export default
命令只能使用一次// export-default.js export default function () { console.log('foo'); }
// import-default.js import customName from './export-default'; customName(); // 'foo'
瀏覽器端加載實現(xiàn)
-
瀏覽器加載 ES6 模塊,也使用標簽雅潭,但是要加入
type="module"
屬性// 01.js export var a = 123;
//demo.html <script type="module">import {a} from "./01.js"; console.log(a)//123</script>
-
腳本異步加載
<script src="path/to/myModule.js" defer></script> <script src="path/to/myModule.js" async></script>
解說:上面代碼中揭厚,標簽打開
defer
或async
屬性,腳本就會異步加載扶供。渲染引擎遇到這一行命令筛圆,就會開始下載外部腳本,但不會等它下載和執(zhí)行椿浓,而是直接執(zhí)行后面的命令太援。defer 與 async 的區(qū)別是:defer 要等到整個頁面在內(nèi)存中正常渲染結(jié)束(DOM 結(jié)構(gòu)完全生成,以及其他腳本執(zhí)行完成)扳碍,才會執(zhí)行提岔;async 一旦下載完,渲染引擎就會中斷渲染笋敞,執(zhí)行這個腳本以后碱蒙,再繼續(xù)渲染。一句話夯巷,defer 是“渲染完再執(zhí)行”赛惩,async 是“下載完就執(zhí)行”。另外趁餐,如果有多個 defer 腳本坊秸,會按照它們在頁面出現(xiàn)的順序加載,而多個 async 腳本是不能保證加載順序的澎怒。