十八、Class
示例:ES5
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);1234567812345678
示例:ES6的等價寫法
class Point{ constructor(x, y){ this.x = x; this.y = y; } toString(){ return '(' + this.x + ', ' + this.y + ')'; } } var p = new Point(1, 2);12345678910111234567891011
實際上,類的所有方法都定義在類的prototype屬性上面;在類的實例上面調用方法,其實就是調用原型上的方法。 p.constructor === Point.prototype.constructor; // true
注意:類中定義的方法,都是帶有作用域的普通函數,而不是箭頭函數。
當然屬性名支持表達式方式
let methodName = "toString"; class Point{ constructor(x, y){ this.x = x; this.y = y; } [methodName](){ return '(' + this.x + ', ' + this.y + ')'; } } var p = new Point(1, 2);12345678910111234567891011
constructor方法
constructor方法是類的默認方法,通過new命令生成對象實例時,自動調用該方法。一個類必須有constructor方法,如果沒有顯式定義,一個空的constructor方法會被默認添加。
class Ponit{} new Ponit();1212
類的實例對象
必須通過new調用,否則會報錯!
var point = Point(2, 3); // 報錯 var point = new Point(2, 3); // 正確1212
不存在變量提升
/* function */ new Foo(); function Foo(){} /* class */ new Bar(); // Unexpected identifier Class Bar{}; 12345671234567
class表達式
和函數一樣,class可以使用表達式的形式定義。通過表達式可以實現立即執行的class。
let person = new class{ constructor(name){ this.name = name; } sayName(){ console.log(this.name); } }('ligang'); person.sayName();123456789123456789
繼承
class Point2D{ constructor(x, y){ this.x = x; this.y = y; } } class Point3D extends Point2D{ constructor(x, y, z){ super(x, y); this.z = z; } toString() { return `$(this.x}, $(this.y}, $(this.z}`; } } console.log(new Point3D(1, 2, 3)); // Point3D { x: 1, y: 2, z: 3 }1234567891011121314151612345678910111213141516
注意:一個子類繼承了一個父類,那麼在子類的constructor中必須使用super調用父類構造函數後才能在子類的constructor中使用this。
class Point2D{} class Point3D extends Point2D{ constructor(x, y, z){ // super(); this.x = x; this.y = y; this.z = z; } toString() { return `$(this.x}, $(this.y}, $(this.z}`; } } console.log(new Point3D(1, 2, 3)); // 報錯:this is not defined1234567891011121312345678910111213
取值函數(getter)和存值函數(setter)
在Class內部可以使用get和set關鍵字,對某個屬性設置存值函數和取值函數,攔截該屬性的存取行爲。
class Person{ constructor(name){ this.name = name; } set username(newName){ this.name = newName; } get username(){ return this.name; } }; var p = new Person('x'); console.log(p.username); // 'x' p.username = "ligang"; console.log(p.username); // 'ligang'123456789101112131415123456789101112131415
私有方法
ES6中並沒有支持,但是可以通過一些約定方式去實現
方式一:通過命名區分,如函數名增加”_”
方式二:通過Symbol值的唯一性
const method = Symbol('sayName'); class Person{ constructor(name){ this.name = name; } [method](){ console.log(this.name); } say(){ this[method](); } }; var p = new Person('ligang'); p.say(); // 'ligang' p.sayName(); // p.sayName is not a function123456789101112131415123456789101112131415
靜態方法
方法前加上static關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,這就稱爲“靜態方法”。
class Person{ constructor(name){ this.username = name; } static sayHello(){ return 'hello'; } static sayName(){ return this.username; } }; console.log(Person.sayHello()); // 'hello' console.log(Person.sayName()); // undefined1234567891011121312345678910111213
注意:靜態方法中,this的指向問題!!
問題
(1)不支持私用屬性,只能通過一些約定實現
(2)不支持實例屬性,只能通過Getter/Setter實現
(3)不支持多重繼承
十九、Module
ES6 模塊的設計思想,是儘量的靜態化,使得編譯時就能確定模塊的依賴關係,以及輸入和輸出的變量。CommonJS和AMD模塊,都只能在運行時確定這些東西。注意,ES6的模塊自動採用嚴格模式,不管你有沒有在模塊頭部加上”use strict”;。模塊功能主要由兩個命令構成:export和import。export命令用於規定模塊的對外接口,import命令用於輸入其他模塊提供的功能。
導出模塊
一個模塊就是一個獨立的文件。該文件內部的所有變量,外部無法獲取。如果你希望外部能夠讀取模塊內部的某個變量,就必須使用export關鍵字輸出該變量。
(1)導出單一接口
Sybtax:export <statement>
// module.js export function method(){} export class Foo{} // app.js import {method, Foo} from 'module.js'123456123456
export語句需要具有聲明部分和賦值部分。
const foo = 'bar'; export foo; // Error1212
(2)導出模塊默認接口
Sybtax:export default <value>
// module.js export default function() {} // app.js import customName from 'module.js' customName();123456123456
(3)混合使用導出接口語句
// module.js export default class Client{} export const foo = 'bar' // app.js import Client, {foo} from 'module.js'123456123456
(4)導出一個模塊的所有接口
Sybtax:export * from 'module-name'
// module.js export function foo(){} // app.js export * from 'module.js'1234512345
(5)導出一個模塊的部分接口
Sybtax:export {member} from 'module-name'
export {foo, bar} from 'my_module'; // 等價於 import {foo, bar} from 'my_moudle'; export {foo, bar};1234512345
(6)導出一個模塊的默認接口
Sybtax:export {default} from 'module'
export {es6 ad default} from 'my_module'; // 等價於 import {es6} from 'my_module'; export default es6;1234512345
引入模塊
(1)引入默認模塊
import namespace from 'module-name' import http from 'http'1212
(2)引入模塊部分接口
import {member1, member2} from 'module-name' import {isEmpty} from 'lodash'1212
(3)引入全部接口到指定命名空間
import * as namespace from 'module-name' import * as lib from 'module'1212
(4)混入引入默認接口和命名接口
import {default as <default name>, method1} from 'module-name' import {default as Client, utils} from 'module' import <default name>, {<named modules>} from 'module-name' import Client, {utils} from 'module'12341234
(5)不引入接口,僅運行模塊代碼
import 'module-name'11
ES6中提供的模塊機制,可以“模塊內容選擇性引入”,其意味着可以通過Rollup和webpack2利用ES6模塊化機制只壓縮必要代碼,最大程度地精簡JavaScript引用的體積,避免了昂貴的網絡帶寬和較慢的頁面加載速度。
總結:
寫到這裏,ES6的所有語法基本已全部描述,有彩蛋、也有單純的語法糖。裏面大多數的語法也可用通過ES5去shim(除了Proxy)。在Node6+以上,幾乎所有的ES6語法被支持,前端可通過Babel6工具進行轉換。在使用ES6過程中,有幾個很爽的小特性,特整理如下:
設置對象變量鍵值的語法
ES6之前,不能在對象字面量裏設置變量鍵值,必須要在對象初始化後增加鍵/值:
var myKey = 'name'; var person = { 'age': 25 }; person[myKey] = 'ligang';1234512345
ES6中新增了[]方式,完美解決:
let myKey = 'name'; let person = { [myKey]: 'ligang', 'age': 25 };1234512345
模板字符串
ES6之前創建多行字符串必須使用\
作連接符。模板字符串的出現,讓字符串拼接變得簡單可維護。
let person = { name: 'ligang', age: 26 }; console.log(`My name is ${person.name}. My age is ${person.age}.`); /* 結果: My name is ligang. My age is 26. */12345671234567
### find/findIndex
JavaScript 提供了 Array.prototype.indexOf
方法,用來獲取一個元素在數組中的索引,但是 indexOf
只能用來查找確切的值,不可以指定查詢條件。find
和 findIndex
可以設置查詢條件,在數組中查找到第一個滿足條件的值。從而避免了循環處理!
let ages = [12, 19, 6, 4]; let firstAdult = ages.find(age => age >= 18); // 19 let firstAdultIndex = ages.findIndex(age => age >= 18); // 112341234
擴展運算符:…
擴展運算符表示一個數組或者一個可迭代對象可以在一次調用中將它們的內容分割爲獨立的參數。
// 傳參給需要多個獨立參數的函數 arguments // 很像 Function.prototype.apply() let numbers = [9, 4, 7, 1]; Math.min(...numbers); // 1 // 將節點列表轉換成數組 let divsArray = [...document.querySelectorAll('div')]; // 將參數轉換成數組 let argsArray = [...arguments];1234567891012345678910
它能把可迭代對象(NodeList
, arguments
等等)轉化爲真正的數組,不再需要使用 Array.from
或Array.prototype.slice.call()
方法。
默認參數值
function greet(name = 'ligang', callback = function(){}) { console.log(`Hello ${name}!`); // 不再需要條件判斷“callback && typeof callback === 'function'”啦 callback(); }1234512345