這裏簡要記錄一下對本身感觸比較深的幾個知識點,將核心的應用投放於實際的項目之中,提供代碼的可維護性。node
1、let和constreact
{ // let聲明的變量只在let命令所在的代碼塊內有效 let a = 1; var b = 2; } console.log(a); // 報錯: ReferenceError: a is not defined console.log(b);
// for循環的技術器就很適合let命令 for (let i = 0; i < 3; i++) { console.log(i); } console.log(i); // ReferenceError: i is not defined
// 這裏的i是var聲明的,在全局範圍內有效,素偶一每一次循環,新的i值都會覆蓋舊值,致使最後輸出的是最後一輪的i值 for (var i = 0; i < 10; i++) { a[i] = function() { console.log(i); } } a[6](); // 10 var b = []; // 使用let聲明的變量盡在塊級做用域內有效,因此每一次循環的j其實都是一個新的變量,因而最後輸出6 for (let j = 0; j < 10; j++) { a[j] = function() { console.log(j); } } b[6]();
// let不像var那樣會發生"變量"提高的現象 // 可是通過babel轉換器轉換以後,仍是存在變量提高的現象 // ES6明確規定,若是區塊中存在let和const命令,則這個區塊中對這些命令聲明的變量從一開始就造成封閉做用域.只要在聲明這些變量以前就使用這些變量,就會報錯 { console.log(foo); // ReferenceError let foo = 2; } // 塊級做用域 { // 塊級做用域的出現使得得到普遍應用的當即執行匿名函數(IIFE)再也不必要了 // IIFE寫法 (function() { var tmp = 'a'; // ... })(); // 塊級做用域寫法 { let tmp = 'a'; // ... } // 所以,咱們可使用塊級做用域來劃分業務模塊,以及避免全局變量 } { let a = 'secret'; function f() { return a; } } f(); // 報錯
// const聲明的常量不得改變值 // const一旦聲明常量,就必須當即初始化,不能留到之後賦值 // const的做用域與let命令相同:只在聲明所在的塊級做用域內有效 // const命令聲明的變量也不提高,只能聲明後使用 const foo = 'AAA'; foo = 'BBB'; // 編譯不經過
{ // 跨模塊常量 // constants.js //export const A = 1; //export const B = 2; //export const C = 3; // test1.js模塊 //import * as constants from 'constants'; }
// 全局對象的屬性 var a = 1; console.log(window.a); // 1 let b = 2; console.log(window.b); // undefined
2、字符串es6
{ // 使用for of循環字符串 for (let c of 'foo') { console.log(c); } let s = 'Hello world!'; // 使用字符串的startsWidth/endsWidth/includes方法 console.log(s.startsWith('Hello')); // true console.log(s.endsWith('!')); // true console.log(s.includes('e')); // true // 這三個方法都支持第二個參數,表示開始搜索的位置 s.startsWith('world', 6); // true let person = { name: 'king', age: 20 }; // 模板字符串 // 全部的空格和縮進都會被保留在輸出中 let str = (` The name is ${person.name}. The age is ${person.age}. `); console.log(str); }
3、函數ajax
// 函數參數的默認值 function log(x, y = 'world') { console.log(x, y); } log('hello'); // 能夠省略尾部參數的默認值 function f(x = 1, y) { return [x, y]; } f(); // [1, undefined] f(2); // [2, undefined] f(, 1); // 報錯, 編譯沒法經過 // rest參數 function add(...values) { let sum = 0; for (let val of values) { sum += val; } return sum; } console.log(add(2, 5, 3)); // 10 const sortNumbers = function() { let arr = Array.prototype.slice.call(arguments); return arr.sort(); }; const sortNumbers = function (...numbers) { return numbers.sort(); }; sortNumbers(3, 1, 2); // rest參數必須是參數列表中的最後一個 const push = function(array, ...items) { items.forEach(function(item) { array.push(item); }); }; let a = []; console.log(push(a, 3, 1, 2));
4、對象編程
// Object.assign方法用來將源對象的全部可枚舉屬性複製到目標對象 let target = { a: 1 }; // 後邊的屬性值,覆蓋前面的屬性值 Object.assign(target, { b: 2, c: 3 }, { a: 4 }); console.log(target); // 用處1 - 爲對象添加屬性 class Point { constructor(x, y) { Object.assign(this, {x, y}); } } //let p = new Point(1, 2); // //console.log(p); // Point {x: 1, y: 2} // 用處2 - 爲對象添加方法 Object.assign(Point.prototype, { getX() { return this.x; }, setX(x) { this.x = x; } }); let p = new Point(1, 2); console.log(p.getX()); // 1 // 用處3 - 克隆對象 function clone(origin) { return Object.assign({}, origin); }
5、Set和Mapjson
// Set裏面的成員的值都是惟一的,沒有重複的值,Set加入值時不會發生類型轉換,因此5和"5"是兩個不一樣的值. let s = new Set(); [2, 3, 5, 4, 5, 2, 2].map(function(x) { s.add(x); }); //for (let i of s) { // console.log(i); //} console.log([...s]); console.log(s.size); // 數組去重 function dedupe(array) { return Array.from(new Set(array)); } console.log(dedupe([1, 2, 2, 3])); // 1, 2, 3
{ // Map相似於對象,也是鍵值對的集合,可是"鍵"的範圍不限於字符串,各類類型的值(包括對象)均可以當作鍵. // 也就是說,Object結構提供了"字符串--值"的對應,Map的結構提供了"值——值"的對象,是一種更完善的Hash結構實現. var m = new Map(); var o = { p: 'Hello World' }; m.set(o, 'content'); m.get(o); // content m.has(o); // true m.delete(o); // true m.has(o); // false m.set(o, 'my content').set(true, 7).set('foo', 8); console.log(m); // Map/數組/對象 三者之間的相互轉換 console.log([...m]); }
6、Iterator和Generator數組
{ // 是一種接口,爲各類不一樣的數據結構提供統一的訪問機制.任何數據結構,只要不輸Iterator接口,就能夠完成遍歷操做. // 可供for...of循環消費 const arr = ['red', 'green', 'blue']; let iterator = arr[Symbol.iterator](); for (let v of arr) { console.log(v); // red green blue } for (let i of iterator) { console.log(i); } // for of 循環能夠代替數組對象的forEach方法, 一樣能夠替代對象的for in循環 }
{ function * foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for (let v of foo()) { console.log(v); } }
7、Promise和asyncpromise
{ let getJSON = function (path, param) { return new Promise(function(resolve, reject) { let async = typeof param.async == 'undefined' ? true : param.async; //let deferred = $.Deferred(); param = param || {}; param.data.auth_token = lib.getToken(); window.loading(); $.ajax({ url: path, data: param.data, type: 'POST', dataType: 'json', async: async, timeout: 15000, success: function (data) { window.unloading(); if (data.code == 0) { resolve.apply(this, [data]); } else { reject.apply(this, [data]); lib.alert(data.msg, '我知道了'); } }, error: function (xhr, type) { window.unloading(); reject.apply(this, ['網絡異常, 請稍候再試']); lib.alert('網絡異常, 請稍候再試'); } }); }); }; getJSON('/xxx.json').then(function(rep) { }).catch(function(rep) { }); }
{ function timeout(ms) { return new Promise(function(resolve) { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { let promise = await timeout(ms); console.log(value); } asyncPrint('Hello world !', 1000); }
8、class瀏覽器
{ class Point { static classMethod() { return 'classMethod...'; } // constructor方法是類的默認方法,經過new命令生成對象實例時自動調用該方法. // 一個類必須有constructor方法,若是沒有顯示定義,一個空的constructor方法會被默認添加 constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } get prop() { return 'getter'; } set prop(value) { console.log('setter:' + value); } } // 靜態屬性的處理,只能用下面這種方式 Point.foo = 1; console.log(Point.foo); // 1 // 繼承 class ColorPoint extends Point { constructor(x, y, color) { // super方法必須被調用, 不然編譯不經過 // 若是super在賦值屬性this.xx = xx,後邊調用,會報錯'this' is not allowed before super() super(x, y); this.color = color; } toString() { return 'The color is ' + this.color + ' and the point is ' + super.toString(); } } var p = new ColorPoint(1, 2, 'red'); console.log(p.toString()); p.prop = 1; p.prop; console.log(Point.classMethod()); // 父類的靜態方法能夠被子類繼承 console.log('ColorPoint.classMethod(): ' + ColorPoint.classMethod()); }
9、Module服務器
{ // module /** * 優點: * 1. ES6能夠在編譯時就完成模塊編譯,效率要比commonJs模塊的加載方式高 * 2. 再也不須要UMD模塊格式,未來服務器端和瀏覽器都會支持ES6模塊格式.目前,經過各類工具庫其實已經作到了這一點 * 3. 未來瀏覽器的新API能夠用模塊格式提供,再也不須要作成全局變量或者navigator對象的屬性 * 4. 再也不須要對象做爲命名空間(好比Math對象),將來這些功能能夠經過模塊提供 */ }
// profile.js export var firstName = 'Michael'; export var lastName = 'Jackson'; export var year = 1958; // profile.js var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export {firstName, lastName, year}; // export命令除了輸出變量,還能夠輸出函數或類(class)。 export function multiply (x, y) { return x * y; }; // export輸出的變量就是原本的名字,可是可使用as關鍵字重命名。 function v1() { //... } function v2() { //... } export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion }; // import // main.js import {firstName, lastName, year} from './profile'; // 重命名 import { lastName as surname } from './profile'; // import命令具備提高效果,會提高到整個模塊的頭部,首先執行。 foo(); import { foo } from 'my_module'; // 僅僅執行lodash模塊,可是不輸入任何值。 import 'lodash'; // export default // 爲了給用戶提供方便,讓他們不用閱讀文檔就能加載模塊,就要用到export default命令,爲模塊指定默認輸出。 // export-default.js export default function () { console.log('foo'); } // import-default.js // 須要注意,這時import命令後面,不使用大括號。 import customName from './export-default'; customName(); // 'foo' // export default命令用在非匿名函數前,也是能夠的。 // export-default.js export default function foo() { console.log('foo'); } // 或者寫成 function foo() { console.log('foo'); } export default foo;
10、編程風格
// 1. let取代var
// 2. 全局常量 // 在let和const之間,建議優先使用const,尤爲是在全局環境,不該該設置變量,只應設置常量。 // const聲明常量還有兩個好處,一是閱讀代碼的人馬上會意識到不該該修改這個值,二是防止了無心間修改變量值所致使的錯誤。 // 全部的函數都應該設置爲常量。 // bad var a = 1, b = 2, c = 3; // good const a = 1; const b = 2; const c = 3; // best const [a, b, c] = [1, 2, 3];
// 3. 字符串 // 靜態字符串一概使用單引號或反引號,不使用雙引號。動態字符串使用反引號。
// 4. 對象 //對象儘可能靜態化,一旦定義,就不得隨意添加新的屬性。若是添加屬性不可避免,要使用Object.assign方法。 // bad const a = {}; a.x = 3; // if reshape unavoidable const a = {}; Object.assign(a, { x: 3 }); // good const a = { x: null }; a.x = 3; // 對象的屬性和方法,儘可能採用簡潔表達法,這樣易於描述和書寫。 var ref = 'some value'; // bad const atom = { ref: ref, value: 1, addValue: function (value) { return atom.value + value; }, }; // good const atom = { ref, value: 1, addValue(value) { return atom.value + value; }, };
// 5. 數組 //使用擴展運算符(...)拷貝數組。 // bad const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i++) { itemsCopy[i] = items[i]; } // good const itemsCopy = [...items]; //使用Array.from方法,將相似數組的對象轉爲數組。 const foo = document.querySelectorAll('.foo'); const nodes = Array.from(foo);
// 6. 函數 //不要在函數體內使用arguments變量,使用rest運算符(...)代替。由於rest運算符顯式代表你想要獲取參數,並且arguments是一個相似數組的對象,而rest運算符能夠提供一個真正的數組。 // bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // good function concatenateAll(...args) { return args.join(''); } //使用默認值語法設置函數參數的默認值。 // bad function handleThings(opts) { opts = opts || {}; } // good function handleThings(opts = {}) { // ... }
// 7. 模塊 //首先,Module語法是JavaScript模塊的標準寫法,堅持使用這種寫法。使用import取代require。 // bad const moduleA = require('moduleA'); const func1 = moduleA.func1; const func2 = moduleA.func2; // good import { func1, func2 } from 'moduleA'; //使用export取代module.exports。 // commonJS的寫法 var React = require('react'); var Breadcrumbs = React.createClass({ render() { return <nav />; } }); module.exports = Breadcrumbs; // ES6的寫法 import React from 'react'; const Breadcrumbs = React.createClass({ render() { return <nav />; } }); export default Breadcrumbs //若是模塊只有一個輸出值,就使用export default,若是模塊有多個輸出值,就不使用export default,不要export default與普通的export同時使用。 //不要在模塊輸入中使用通配符。由於這樣能夠確保你的模塊之中,有一個默認輸出(export default)。 // bad import * as myObject './importModule'; // good import myObject from './importModule'; //若是模塊默認輸出一個函數,函數名的首字母應該小寫。 function makeStyleGuide() { } export default makeStyleGuide; //若是模塊默認輸出一個對象,對象名的首字母應該大寫。 const StyleGuide = { es6: { } }; export default StyleGuide;
但願我所記錄的,正是你所想要的。
最後,將這本書的封面放在這裏——