塊做用域javascript
ES6引入塊做用域java
考慮到環境致使的行爲差別太大,應該避免在塊級做用域內聲明函數。若是確實須要,也應該寫成函數表達式,而不是函數聲明語句。node
letjquery
constes6
const
實際上保證的,並非變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動,所以值類型變量不可變,而引用類型變量能夠更改實體數據內容。頂層對象編程
let、const、class命令聲明的全局變量,不屬於頂層對象的屬性數組
ES6 容許按照必定模式,從數組和對象中提取值,對變量進行賦值,這被稱爲解構實質是模式匹配promise
等號右邊的對象須要具有 Iterator 接口瀏覽器
// 基本模式匹配 let [a, b, , c] = [1, 2, 3]; // a = 1; b = 2; c = undefined; // 複雜模式匹配 [a, b, c] = [1, [2], 3]; // a = 1; b = [2]; c = 3; [a, [b], c] = [1, [2], 3]; // a = 1; b = 2; c = 3; [a, [b], c] = [1, [2, 3], 4]; // a = 1; b = 2; c = 4; [a, [b], c] = [1, 2, 3]; // Uncaught TypeError: undefined is not a function // 與rest參數結合使用 let [head, ...tail] = [1, 2, 3, 4]; // head = 1; tail = [2, 3, 4] // 設置默認值,ES6 內部使用嚴格相等運算符(===),判斷一個位置是否有值。因此,只有當一個數組成員嚴格等於undefined,默認值纔會生效。 let [foo = 1] = []; // foo = 1; let [foo = 1] = [null]; // foo = null; // 默認值是表達式的狀況,表達式惰性求值 function f() { console.log('aaa'); } let [x = f()] = [1]; // 默認值爲變量的狀況,變量必須已聲明 let [x = 1, y = x] = []; let [x = y, y = 1] = []; // ReferenceError: y is not defined // 解構字符串 const [a, b, c, d, e] = 'hello'; // a = 'h'; b = 'e'; c = 'l'; d = 'l'; e = 'o'
let { foo, bar } = { foo: "aaa", bar: "bbb" }; // foo = 'aaa'; bar = 'bbb'; // 匹配的模式: 變量 let { foo: f, bar: b } = { foo: "aaa", bar: "bbb" }; // foo = undefined; f = "aaa"; b = "bbb" // 默認值 let { baz = {} } = { foo: "aaa", bar: "bbb" }; // baz = {}; let {x: y = 3} = {x: 5}; // y = 5; // 混合解構 let { items: [{name}] } = { id: '1', items: [{name: 'joke'}, {name: 'tony'}] }; // name = 'joke' let [{name, habbits: [habbit]}] = [{id: '1', name: 'kelly', habbits: ['piano']}, {id: '2', name: 'tony'}]; // name = 'kelly'; habbit = 'piano' // 嵌套賦值 let obj = {}; let arr = []; ({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true }); obj // {prop:123} arr // [true] // 在直接使用賦值表達式時,須要使用圓括號包住賦值表達式,大括號寫在行首會被解釋爲代碼塊 let x; {x} = {x: 1}; // SyntaxError: syntax error ({x} = {x: 1}); // x = 1; const arr = [1,2,3,4] let {0 : first, [arr.length - 1] : last, length} = arr; // first = 1; last = 4; length = 4
// 函數參數的解構賦值 function move({x, y} = { x: 0, y: 0 }) { return [x, y]; } move({x: 3, y: 8}); // [3, 8] move({x: 3}); // [3, undefined] move({}); // [undefined, undefined] move(); // [0, 0]
${name}
緩存
擴展方法
startsWith
let s = 'Hello world!'; s.startsWith('world') // true s.startsWith('world', 6) // true, 第二個參數針對第n個位置直到結束
endsWith
let s = 'Hello world!'; s.endsWith('world') // true s.endsWith('Hello', 5) // true, 第二參數針對前n個字符
includes
let s = 'Hello world!'; s.includes('world') // true s.includes('Hello', 6) // false, 第二參數針對第n個位置直到結束
repeat
'hello'.repeat(2) // hellohello,repeat重複字符串操做
padStart, padEnd
// 補全字符串方法,接受兩個參數,第一個參數指定字符串長度,第二個參數指定補全內容 'x'.padStart(5, 'ab') // ababx 'x'.padEnd(3, 'a') // xaa
擴展方法
默認值
箭頭函數
擴展方法
flat
[1, 2, [3, [4, 5]]].flat() // [1, 2, 3, [4, 5]],單層拉平 [1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5],兩層拉平 [1, [2, [3]]].flat(Infinity) // [1, 2, 3]
屬性名錶達式
let userName = 'joe'; let firstName = 'curly'; let lastName = 'water';
[userName]: 23, [firstName + lastName]: 25
};
- 方法的name屬性 - 屬性遍歷 - for...in - 遍歷原型鏈,遍歷自身和繼承的可枚舉屬性(不含Symbol屬性) - Object.keys - 返回一個數組,包括自身的全部可枚舉屬性(不含 Symbol 屬性)的鍵名 - Object.getOwnPropertyNames - 返回一個數組,包含對象自身的全部屬性(不含 Symbol 屬性)的鍵名 - Object.getOwnPropertySymbols - 返回一個數組,包含對象自身的全部Symbol屬性的鍵名 - Reflect.ownKeys - 返回一個數組,包含對象自身的全部鍵名 - super關鍵字 - 指向當前對象的原型對象,只能用在簡寫的對象方法中 - 解構 - 擴展運算符 - 擴展方法 - Object.is() - 嚴格相等,解決全等運算符`NaN`不等於自身,以及`+0`等於`-0`等問題 - Object.assign() - 用於對象的合併,將源對象(source)的全部可枚舉屬性,複製到目標對象,淺拷貝 - Object.getOwnPropertyDescriptors() - 返回對象屬性的描述對象 - Object.setPrototypeOf() - 設置原型對象 - Object.getPrototypeOf() - 讀取原型對象 - Object.keys() - 返回一個數組,包括自身的全部可枚舉屬性(不含 Symbol 屬性)的鍵名 - Object.values() - 返回一個數組,包括自身的全部可枚舉屬性(不含 Symbol 屬性)的鍵值 - Object.entries() - 方法返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵值對數組 - Object.fromEntries() - Object.entries的逆操做 # Symbol ES6新加入的類型值,表示獨一無二的值
let s = Symbol();
- `Symbol`函數的參數只是表示對當前 Symbol 值的描述 - 不能與其餘類型的值進行運算 - 做爲屬性名,不能使用點運算符
let mySymbol = Symbol();
// 第一種寫法
let a = {};
a[mySymbol] = 'Hello!';
// 第二種寫法
let a = {
[mySymbol]: 'Hello!'
};
// 第三種寫法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上寫法都獲得一樣結果
a[mySymbol] // "Hello!"
- 能夠用於定義一組常量,保證這組常量的值都是不相等的
const log = {};
log.levels = {
DEBUG: Symbol('debug'), INFO: Symbol('info'), WARN: Symbol('warn')
};
console.log(log.levels.DEBUG, 'debug message');
console.log(log.levels.INFO, 'info message');
# Set 和 Map **Set** ES6 提供了新的數據結構 Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。 `Set`自己是一個構造函數,用來生成 Set 數據結構。
const s = new Set([1, 2, 3]);
s.add(4);
s.delete(4);
s.has(4);
s.clear();
s.size;
s.keys();
s.values();
s.entries();
s.forEach((value, key) => console.log(key + ":" + value));
**WeakSet** WeakSet 結構與 Set 相似,也是不重複的值的集合。 WeakSet 的成員只能是對象。 WeakSet 中的對象都是弱引用,即垃圾回收機制不考慮 WeakSet 對該對象的引用,也就是說,若是其餘對象都再也不引用該對象,那麼垃圾回收機制會自動回收該對象所佔用的內存,不考慮該對象還存在於 WeakSet 之中。 **Map** ES6 提供了 Map 數據結構。它相似於對象,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵。
const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.set([1], false);
map.size // 2
map.get('foo');
map.has('foo');
map.delete('foo');
map.clear();
for (let key of map.keys()) {
console.log(key);
}
for (let value of map.values()) {
console.log(value);
}
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// 等同於使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
**WeakMap** `WeakMap`結構與`Map`結構相似,也是用於生成鍵值對的集合。 `WeakMap`只接受對象做爲鍵名(`null`除外) `WeakMap`的鍵名所指向的對象,不計入垃圾回收機制。 # Proxy Proxy 是一個構造函數,能夠理解成,在目標對象以前架設一層「攔截」,外界對該對象的訪問,都必須先經過這層攔截,所以提供了一種機制,能夠對外界的訪問進行過濾和改寫。 # Reflect 保存Object對象的一些屬於語言內部的方法,好比說`defineProperty`/`get`/`apply` 好處在於:讓`Object`操做都變成函數行爲,在Proxy中能夠獲取對象的默認行爲 > - Reflect.apply(target, thisArg, args) > - Reflect.construct(target, args) > - Reflect.get(target, name, receiver) > - Reflect.set(target, name, value, receiver) > - Reflect.defineProperty(target, name, desc) > - Reflect.deleteProperty(target, name) > - Reflect.has(target, name) > - Reflect.ownKeys(target) > - Reflect.isExtensible(target) > - Reflect.preventExtensions(target) > - Reflect.getOwnPropertyDescriptor(target, name) > - Reflect.getPrototypeOf(target) > - Reflect.setPrototypeOf(target, prototype) # Promise 所謂`Promise`,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。 `Promise`對象是一個構造函數,用來生成`Promise`實例。 狀態:`pending`(進行中)、`fulfilled`(已成功)和`rejected`(已失敗) - `Promise` 新建後就會當即執行。 - 調用`resolve`或`reject`並不會終結 `Promise` 的參數函數的執行。 - `then`方法返回的是一個新的`Promise`實例 - `catch`方法會捕獲狀態肯定前的全部錯誤,包括在then回調函數中的錯誤 - `Promise` 會吃掉錯誤,不會對後續代碼執行產生影響 - `finally`方法,無論 `Promise` 對象最後狀態如何,都會執行的操做 - `Promise.all`方法用於將多個 `Promise` 實例,包裝成一個新的 `Promise` 實例。多個`Promise`實例都改變狀態,纔會調用新`Promise`實例的回調 - 若是做爲參數的 `Promise` 實例,本身定義了`catch`方法,那麼它一旦被`rejected`,並不會觸發`Promise.all()`的`catch`方法。 - `Promise.race`方法用於將多個 `Promise` 實例,包裝成一個新的 `Promise` 實例。第一個`Promise`實例都改變狀態,進入新`Promise`實例的回調 - `Promise.resolve(reason)`方法也會返回一個新的 `Promise` 實例,該實例的狀態爲`fulfilled`。 - `Promise.reject(reason)`方法也會返回一個新的 `Promise` 實例,該實例的狀態爲`rejected`。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/ 異步操做成功 /){
resolve(value);
} else {
reject(error);
}
});
promise
.then(function(value) { console.log(value) }, function (err) {console.log(err);})
.catch(function(error) { console.log(error) });
# Iterator Iterator(遍歷器)是一種接口,爲各類不一樣的數據結構提供統一的訪問機制。任何數據結構只要部署 Iterator 接口,就能夠完成遍歷操做。
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() { return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {value: undefined, done: true}; }
};
}
ES6 規定,默認的 Iterator 接口部署在數據結構的`Symbol.iterator`屬性,或者說,一個數據結構只要具備`Symbol.iterator`屬性,就能夠認爲是「可遍歷的」(iterable) 原生具有Iterator接口的數據結構 - Array - Map - Set - String - TypedArray - 函數的 arguments 對象 - NodeList 對象 `for...of`循環調用遍歷器接口,做爲遍歷全部數據結構的統一的方法。 `for...in`循環主要是爲遍歷對象而設計的。 `forEach`沒法跳出循環 `for...of`可跳出循環,嚴格按照順序遍歷 # Generator Generator 函數是一個狀態機,封裝了多個內部狀態。 執行 Generator 函數會返回一個遍歷器對象,也就是說,Generator 函數除了狀態機,仍是一個遍歷器對象生成函數。返回的遍歷器對象,能夠依次遍歷 Generator 函數內部的每個狀態。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
- `yield`表達式後面的表達式,只有當調用`next`方法、內部指針指向該語句時纔會執行 - 只有調用`next`方法時,Genarator函數纔會執行 - `yield`只能被Genarator函數包裹,普通函數不行 - `yield*`表達式,用來在一個 Generator 函數裏面執行另外一個 Generator 函數 # async Generator 函數的語法糖 `async`表示函數裏有異步操做,`await`表示緊跟在後面的表達式須要等待結果。 `await`命令後面能夠是`Promise`對象 `async`函數的返回值是 Promise 對象 # Class
class MyClass {
constructor() {
this.name = name;
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
- 類和模塊的內部,默認就是嚴格模式,因此不須要使用`use strict`指定運行模式 - 類不存在變量提高
new Foo(); // Error
class Foo {
print () { console.log('Hello'); }
}
- name 屬性 - 可使用Generator實現Symbol.iterator遍歷器 - 類的方法內部若是含有`this`,它默認指向類的實例。可是若是單獨提取方法出來用,容易報錯
class Logger {
printName(name = 'there') { this.print(`Hello ${name}`); } print(text) { console.log(text); }
}
const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
解決方法
class Logger {
constructor() { this.printName = this.printName.bind(this); } // ...
}
class Logger {
prinitName () { this.print(`Hello ${name}`) } // ...
}
- 若是在一個方法前,加上`static`關鍵字,就表示該方法不會被實例繼承,而是直接經過類來調用,這就稱爲「靜態方法」。
class Foo {
static classMethod() { return 'hello'; }
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
若是靜態方法包含`this`關鍵字,這個`this`指的是類,而不是實例。 父類的靜態方法,能夠被子類繼承。 - 實例屬性除了在`constructor()`方法裏面定義,也能夠直接寫在類的最頂層。
class IncreasingCounter {
_count = 0; get value() { console.log('Getting the current value!'); return this._count; } increment() { this._count++; }
}
- 靜態屬性
class Foo {
static prop = 1;
}
- 私有屬性 - 使用_約定 - 結合Symbol使用避免被覆蓋 - 使用#表明私有屬性
class IncreasingCounter { #count = 0; get value() { console.log('Getting the current value!'); return this.#count; } increment() { this.#count++; } } ```
new
命令做用於的那個構造函數。this
對象,必須先經過父類的構造函數完成塑造,獲得與父類一樣的實例屬性和方法,而後再對其進行加工,加上子類本身的實例屬性和方法。若是不調用super
方法,子類就得不到this
對象。修飾器是一個對類進行處理的函數
@testable class MyTestableClass { // ... } function testable(target) { target.isTestable = true; } MyTestableClass.isTestable // true
function testable(isTestable) { return function(target) { target.isTestable = isTestable; } } @testable(true) class MyTestableClass {} MyTestableClass.isTestable // true @testable(false) class MyClass {} MyClass.isTestable // false
修飾器對類的行爲的改變,是代碼編譯時發生的,而不是在運行時。這意味着,修飾器能在編譯階段運行代碼。也就是說,修飾器本質就是編譯時執行的函數。
@connect(mapStateToProps, mapDispatchToProps) export default class MyReactComponent extends React.Component {}
修飾對象屬性
class Person { @readonly name() { return `${this.first} ${this.last}` } } function readonly(target, name, descriptor){ // target要修改的對象 // name要修飾的屬性名 // descriptor要修飾的屬性的描述對象 descriptor.writable = false; return descriptor; }
修飾器只能用於類和類的方法,不能用於函數,由於存在函數提高。
CommonJS規範
接口:
// moduleA.js module.exports = function( value ){ return value * 2; } // moduleB.js var multiplyBy2 = require('./moduleA'); var result = multiplyBy2(4);
require
命令第一次加載該腳本,就會執行整個腳本,而後在內存生成一個對象緩存。AMD規範
接口:
define('myModule', ['jquery'], function($) { // $ 是 jquery 模塊的輸出 $('body').text('hello world'); }); require(['myModule'], function(myModule) {}); // 未使用模塊名,類CommonJS使用 define(function(require, exports, module) {})
ES6 模塊
export
命令顯式指定輸出的代碼(並不是輸出對象),再經過import
動態引用。接口:
export
命令用於規定模塊的對外接口import
命令用於輸入接口export default
規定模塊默認接口,本質是輸出一個叫default的變量,因此在模塊中只能惟一存在,而且不可更改,不能跟聲明語句export
其餘模塊,export和import的複合寫法,實際上並無導入當前模塊,只是轉發import
命令會被 JavaScript 引擎靜態分析,先於模塊內的其餘語句執行,import
和export
命令只能在模塊的頂層,不能在代碼塊之中import()
函數完成動態加載,返回一個Promise對象// Module1 var m = 1; export { m n as N }; // 輸出一個default變量,將變量m的值賦給變量default export default m; // Module2 import {m, N} from "Module1"; // 導入m和N接口 import { m as M } from "Module1"; // 導入m接口,重命名爲M import module1 from "Module1"; // 導入默認接口 import * as module1 from "Module1"; // 導入全部接口 export * from "Module1"; // 再輸出,export *命令會忽略Module1模塊的default方法。
加載規則
瀏覽器對於帶有type="module"
的<script>
,都是異步加載,不會形成堵塞瀏覽器,即等到整個頁面渲染完,再執行模塊腳本,等同於打開了<script>
標籤的defer
屬性。
外部ES6模塊腳本特性:
use strict
。import
命令加載其餘模塊(.js
後綴不可省略,須要提供絕對 URL 或相對 URL),也可使用export
命令輸出對外接口。this
關鍵字返回undefined
,而不是指向window
。也就是說,在模塊頂層使用this
關鍵字,是無心義的。在let
和const
之間,建議優先使用const
const
進行優化,因此多使用const
,有利於提升程序的運行效率靜態字符串一概使用單引號或反引號,不使用雙引號。動態字符串使用反引號。
const a = 'apple'; let b = "banana"; b = "batman";
優先使用解構賦值
若是函數返回多個值,優先使用對象的解構賦值,而不是數組的解構賦值。這樣便於之後添加返回值,以及更改返回值的順序。
對象
Object.assign
方法。數組
使用擴展運算符拷貝數組
使用Array.from將類數組對象轉爲數組
函數
全部配置項都應該集中在一個對象,放在最後一個參數
rest運算符代替arguments變量
使用默認值語法設置函數參數的默認值。
Map
注意區分 Object 和 Map,若是隻是須要key: value
的數據結構,使用 Map 結構。由於 Map 有內建的遍歷機制。
模塊
若是模塊只有一個輸出值,就使用export default
,若是模塊有多個輸出值,就不使用export default
,export default
與普通的export
不要同時使用。
若是模塊默認輸出一個函數,函數名的首字母應該小寫。
function makeStyleGuide() { } export default makeStyleGuide;
若是模塊默認輸出一個對象,對象名的首字母應該大寫。
const StyleGuide = { es6: { } }; export default StyleGuide;