var
聲明初始化變量, 聲明能夠提高,但初始化不能夠提高。html
let
const
JS引擎在掃描代碼發現變量聲明時,要麼將它們提高至做用域頂部(
var
聲明),要麼將聲明放到TDZ(臨時死區)中(let
和const
聲明)。訪問TDZ中的變量會觸發運行時錯誤。只有執行過變量聲明語句後,變量纔會從TDZ中移出,而後能夠正常訪問。node
第一種狀況:es6
if(condition) {
console.log(typeof value); //引用錯誤!
let value = "blue"; //TDZ
}
複製代碼
第二種狀況:web
console.log(typeof value); //'undefined'-->這裏不報錯,只有變量在TDZ中才會報錯
if(condition) {
let value = "blue";
}
複製代碼
當即調用(IIFE)正則表達式
let
和const
之因此能夠在運用在for-in和for-of循環中,是由於每次迭代會建立一個新的綁定(const在for循環中會報錯)。編程
var
可能會在無心中覆蓋一個已有的全局屬性,let
或const
會在全局做用域下建立一個新的綁定,但該綁定不會添加爲全局對象的屬性。換句話說,使用let
或const
不能覆蓋全局變量,而只能遮蔽它。若是不是爲全局對象建立屬性,使用let
和const
要安全得多。json
注:若是但願在全局對象下定義變量,仍然可使用var。這種狀況常見於在瀏覽器中跨frame或跨window訪問代碼。數組
名詞解釋:promise
在UTF-16中,前2^16個碼位均以16位的編碼單元表示,這個範圍被稱做基本多文種平面瀏覽器
這個方法儘管有效,可是當統計長字符串中的碼位數量時,運動效率很低。所以,你也可使用字符串迭代器解決效率低的問題,整體而言,只要有可能就嘗試着減少碼位計算的開銷。
檢測u修飾符支持:
function hasRegExpU() {
try {
var pattern = new RegExp('.', 'u');
return true;
} catch (ex) {
return false;
}
}
複製代碼
當執行操做時, y修飾符會把上次匹配後面一個字符的索引保存到
lastIndexOf
中;若是該操做匹配的結果爲空,則lastIndexOf
會被重置爲0。g修飾符的行爲相似。
1. 只有調用exec()和test()這些正則表達式對象的方法時纔會涉及lastIndex屬性;
2. 調用字符串的方法,例如match(),則不會觸發粘滯行爲。
複製代碼
var re1 = /ab/i;
re2 = new RegExp(re1); //沒有修飾符複製
re3 = new RegExp(re1, "g"); //有修飾符(ES6)
複製代碼
es5方法獲取正則表達式的修飾符:
function getFlags(re) {
var text = re.toString();
return text.substring(text.lastIndexOf('/' + 1, text.length);
}
複製代碼
多行字符串
基本的字符串格式化(字符串佔位符)
HTML轉義
function passthru(literals, ...substitutions) {
//返回一個字符串
let result = "";
//根據substitutions的數量來肯定循環的執行次數
for(let i=0; i<substitutions.length; i++){
result += literals;
result += substitutions[i]
console.log(literals[i])
console.log(substitutions[i])
}
//合併最後一個literal
result += literals[literals.length - 1];
return result;
}
let count = 10;
price = 0.25;
message = passthru`${count} items cost $${(count * price).toFixed(2)}`;
console.log(message)
複製代碼
String.raw`assda\\naadasd`
//代碼模擬(略)
複製代碼
ES5默認參數值
下面函數存在什麼問題 ???
function makeRequest(url, timeout, callback) {
timeout = timeout || 2000;
callback = callback || function() {};
}
複製代碼
假如timeout
傳入值0,這個值是合法的,可是也會被視爲一個假值,並最終將timeout
賦值爲2000。在這種狀況下,更安全的選擇是經過typeof
檢查參數類型,以下:
function makeRequest(url, timeout, callback) {
timeout = (typeof timeout !== 'undefined') ? timeout :2000;
callback = (typeof callback !== 'undefined') ? callback : function() {};
}
複製代碼
ES5默認參數值
function makeRequest(url, timeout = 2000, callback) {
//函數的其他部分
}
//特別注意:此時 null 是一個合法值,因此不會使用 timeout 默認值,即 timeout = null
makeRequest('/foo', null, function(body){
doSomething(body);
})
複製代碼
arguments
的影響**非嚴格模式:參數變化,arguments對象隨之改變;
嚴格模式:不管參數如何變化,arguments對象再也不隨之改變;
非嚴格模式/嚴格模式:不管參數如何變化,arguments對象再也不隨之改變;
注: 在引用參數默認值的時候,只容許引用前面參數的值,即先定義的參數不能訪問後定義的參數。這能夠用默認參數的臨時死區來解釋。以下:
function add(first = second, second) {
return first + second;
}
console.log(add(1, 1)); //2
console.log(add(undefined, 1)) //拋出錯誤
//解釋原理:
//add(1, 1)
let first = 1;
let second = 1;
//add(undefined, 1)
let first = second;
let second = 1; //處於臨時死區
複製代碼
setter
之中(由於對象字面量setter的參數有且只有一個,而在不定參數的定義中,參數的數量能夠無限多)不管是否使用不定參數,arguments對象老是包含全部傳入函數的參數。
let value = [25, 50, 75, 100];
//es5
console.log(Math.max.apply(Math, values); //100
//es6
console.log(Math.max(...values)); //100
複製代碼
兩個有關函數名稱的特例:
bind()
函數建立的函數,其名稱將帶有「bound」前綴;Function
構造函數建立的函數,其名稱將是「anonymous」.var doSomething = function() {
//空函數
}
console.log(doSomething.bind().name); //'bound doSomething'
console.log((new Function()).name); //'anonymous(匿名)'
複製代碼
切記: 函數name屬性的值不必定引用同名變量,它只是協助調試用的額外信息,因此不能使用name屬性的值來獲取對於函數的引用。
JS函數有兩個不一樣的內部方法:[[call]] 和 [[Construct]]。
this
綁定到實例上(具備 [[Construct]] 方法的函數被統稱爲構造函數,箭頭函數沒有 [[Construct]] 方法 );new
關鍵字調用函數,則執行 [[call]] 函數,從而直接執行代碼中的函數體;爲了解決判斷函數是否經過new關鍵字調用的問題,new.target
橫空出世 (instance of ---> new.target)
在函數外使用
new.target
是一個語法錯誤。
箭頭函數與傳統的JS函數不一樣之處主要有如下幾個方面:
this
、super
、arguments
和new.target
綁定;new
關鍵字調用;this
的綁定;arguments
對象建立一個空函數:
let doNothing = () => {};
複製代碼
返回一個對象字面量
let getTempItem = id => ({ id: id, name: "Temp"});
複製代碼
建立當即執行的函數
let person = ((name) => {
return {
getName: function() {
return name;
}
}
})("xszi")
console.log(person.getName()); //xszi
複製代碼
箭頭函數沒有this
綁定
let PageHandler = {
id: '123456',
init: function() {
document.addEventListener("click", function(event){
this.doSomething(event.type); //拋出錯誤
}, false)
},
doSomething: function(type) {
console.log("handling " + type + "for" + this.id)
}
}
複製代碼
使用bind()
方法將函數的this
綁定到PageHandler
,修正報錯:
let PageHandler = {
id: '123456',
init: function() {
document.addEventListener("click", (function(event){
this.doSomething(event.type); //不報錯
}).bind(this), false)
},
doSomething: function(type) {
console.log("handling " + type + "for" + this.id)
}
}
複製代碼
使用箭頭函數修正:
let PageHandler = {
id: '123456',
init: function() {
document.addEventListener("click",
event => this.doSomething(event.type), false);
},
doSomething: function(type) {
console.log("handling " + type + "for" + this.id)
}
}
複製代碼
- 箭頭函數沒有
prototype
屬性,它的設計初衷是 即用即棄, 不能用來定義新的類型。- 箭頭函數的中
this
取決於該函數外部非箭頭函數的this
值,不能經過call(), apply()
或bind()
方法來改變this
的值。
箭頭函數沒有arguments
綁定
始終訪問外圍函數的
arguments
對象
如何利用尾調用優化
function factorial(n) {
if ( n<=1 ) {
return 1;
}
}else{
//引擎沒法自動優化,必須在返回後執行乘法操做
return n * factorial(n-1);
//隨調用棧尺寸的增大,存在棧溢出的風險
}
複製代碼
function factorial(n, p = 1) {
if ( n<=1 ) {
return 1 * p;
}
}else{
let result = n * p;
//引擎可自動優化
return factorial(n-1, result);
//不建立新的棧幀,而是消除並重用當前棧幀
}
複製代碼
腳本開始執行時存在於JS執行環境中的對象,全部標準對象都是內建對象。
var person = {
name: 'xszi',
sayName: function() {
console.log(this.name);
}
}
var person = {
name: 'xszi',
sayName() {
console.log(this.name);
}
}
//二者惟一的區別是,簡寫方式可使用super關鍵字
複製代碼
在對象字面量中使用方括號表示的該屬性名稱是可計算的,它的內容將被求值並被最終轉化爲一個字符串。
以下:
var suffix = ' name';
var person = {
['first' + suffix]: 'xszi',
['last' + suffix]: 'wang'
};
console.log(person['first name']); //xszi
console.log(person['last name']) // wang
複製代碼
ECMAScript
其中一個設計目標是:再也不建立新的全局函數,也不在Object.prototype
上建立新的方法。
Object.is()
大多數狀況下,Object.is()
與'==='運行結果相同,惟一區別在於 +0 和 -0 識別爲不相等,而且NaN
與NaN
等價。
Object.assign()
mixin()
方法使用賦值操做符(assignment operator)= 來複制相關屬性,卻不能複製 訪問器屬性 到接受對象中,所以最終添加的方法棄用mixin
而改用assign
做爲方法名。
Object.assign()
方法能夠接受任意數量的源對象,並按指定的的順序將屬性賦值到接收對象中。因此若是多個源對象具備同名屬性,則排位靠後的源對象會覆蓋排位靠前的。
訪問器屬性: Object.assign()
方法不能將提供者的訪問器屬性賦值到接收對象中。因爲 Object.assign()
方法執行了賦值操做,所以提供者的訪問器屬性最終會轉變爲接受對象中的一個數據屬性。
eg:
var receiver = {};
supplier = {
get name() {
return 'file.js'
}
};
Object.assign(receiver, supplier);
var descriptor = Object.getOwnPropertyDescriptor(receiver, 'name');
console.log(descriptor.value); // 'file.js'
console.log(descriptor.get); // undefined
複製代碼
自有屬性枚舉順序的基本規則是:
symbol
按照他們被加入的順序排序。Object.setPrototypeOf(targetObject, protoObject);
複製代碼
Super
引用
Super
引用至關於指向對象原型的指針,實際上也就是Object.getPrototypeOf(this)
, **必需要在使用簡寫方法的對象中使用Super
**引用。
Super
引用不是動態變化的,它老是指向正確的對象。
ES6正式將方法定義爲一個函數,它會有一個內部的 [[HomeObject]] 屬性來容納這個方法從屬的對象。
Super
的因此引用都經過 [[HomeObject]] 屬性來肯定後續的運行過程:
Object.getPrototypeOf()
方法來檢索原型的引用;this
綁定而且調用相應的方法。let node = {
type: "Indetifier",
name: "foo"
}
let {type, name} = node;
console.log(type); // Indetifier
console.log(name); // foo
複製代碼
不要忘記初始化程序(也就是符號右邊的值)
var {type, name}; //報錯,使用let和const一樣報錯
// 除使用解構外,使用var, let不強制要求提供初始化程序, 可是const必定須要;
複製代碼
let node = {
type: "Indetifier",
name: "foo"
}
type = 'Literal', name = 5;
//使用解構語法爲多個變量賦值
({type, name} = node); //須要使用()包裹解構複製語句,{}是一個代碼塊,不能放在左邊
console.log(type); // Indetifier
console.log(name); // foo
複製代碼
let node = {
type: "Indetifier",
name: "foo"
}
let { type: localType, name: localName } = node;
console.log(localType); // Indetifier
console.log(localName); // foo
複製代碼
type: localType
語法的含義是讀取名爲type
的屬性並將其只存儲在變量localType
中。
數組解構也可用於賦值上下文,但不須要用小括號包裹表達式,這一點與對象解構的的約定不一樣。
let colors = ['red', 'green', 'blue'], firstColor = 'black', secondColor = 'purple';
[firstColor, secondColor] = colors;
console.log(firstColor); // 'red'
console.log(secondColor); // 'green'
複製代碼
交換值
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a); //2
console.log(b); //1
複製代碼
let colors = ['red', ['green', 'lightgreen'], 'blue'];
let [firstColor, [secondColor]] = colors;
console.log(firstColor); //red
console.log(secondColor); //green
複製代碼
function setCookie(name, value, options) {
options = options || {};
let secure = options.secure,
path = options.path,
domian= options.domain,
expires = options.expires;
//設置cookie代碼
}
// 第三個參數映射到options中
setCookie('type', 'js', {
secure: true,
expires: 60000
})
複製代碼
上面函數存在一個問題:僅查看函數的聲明部分,沒法辨識函數的預期參數,必須經過閱讀函數體才能夠肯定全部參數的狀況。可使用 解構參數 來優化:
function setCookie(name, value, {secure, path, domain, expires}}) {
//設置cookie代碼
}
setCookie('type', 'js',{
secure: true,
expires: 60000
})
複製代碼
Symbol
和Symbol
屬性在
Symbol
出現以前,人們一直經過屬性名來訪問全部屬性,不管屬性名由什麼元素構成,所有經過一個字符串類型的名稱來訪問;私有名稱原來是爲了讓開發者們建立非字符串名稱而設計的,可是通常的技術沒法檢測這些屬性的私有名稱。
經過Symbol
能夠爲屬性添加非字符串名稱,可是其隱私性就被打破了。
Symbol
、Symbol
共享體系let firstName = Symbol();
let person = {};
person[firstName] = 'xszi';
console.log(person[firstName]); //xszi
複製代碼
因爲Symbol
是原始值,所以調用new Symbol()
會致使程序拋出錯誤。
Symbol
函數接受一個可選參數,其可讓你添加一段文本描述即將建立的Symbol
,這段描述 不可用於屬性訪問。該描述被存儲在內部的 [[Description]] 屬性中,只有當調用Symbol
的toString()
方法時才能夠讀取這個屬性。
Symbol
共享體系有時咱們可能但願在不一樣代碼中共享同一個
Symbol
(在很大的代碼庫中或跨文件追蹤Symbol
很是困難),ES6提供了一個能夠全局訪問的全局Symbol
註冊表,即便用Symbol.for()
方法。
let uid = Symbol.for('uid');
let object = {};
object[uid] = '12345';
console.log(object[uid]); //'12345'
console.log(uid); // 'Symbol(uid)'
複製代碼
實現原理: Symbol.for()
方法首先在全局Symbol
註冊表中搜索鍵爲‘uid’的Symbol
是否存在,若是存在,直接返回已有的Symbol
;不然,建立一個新的Symbol
,並使用這個鍵在Symbol
全局註冊表中註冊,隨即返回新建立的Symbol
。
可使用Symbol.keyFor()
方法在Symbol
全局註冊表中檢索與Symbol
有關的鍵。
Symbol
全局註冊表是一個相似全局做用域的共享環境,也就是說你不能假設目前環境中存在哪些鍵。當使用第三方組件時,儘可能使用Symbol
鍵的命名空間減小命名衝突。如 jQuery.
console.log()
會調用Symbol
的String()
方法desc = String(uid);
desc = uid + ''; //報錯,不能轉爲字符串類型
desc = uid / 2; //報錯,不能轉爲數字類型
複製代碼
Object.keys()
返回可枚舉屬性Object.getOwnPropertyNames()
不考慮可枚舉性,一概返回Object.getOwnProperty-Symbols()
ES6用來檢索對象中的Symbol屬性全部對象一開始沒有本身獨有的屬性,可是對象能夠從原型鏈中繼承Symbol屬性。
well-know Symbol
暴露內部操做仍是不怎麼理解,找到一個使用Symbol
的實際場景才能更好理解!
Set 和 Map 主要的應用場景在於 數據重組 和 數據儲存
Set 是一種叫作集合的數據結構,Map 是一種叫作字典的數據結構
//set
var set = Object.create(null);
set.foo = true;
//檢查屬性是否存在
if(set.foo){
//要執行的代碼
}
複製代碼
//map
var map = Object.create(null);
map.foo = "bar";
//獲取已存值
var value = map.foo;
console.log(value);
複製代碼
通常來講,Set集合常被用於檢查對象中是否存在某個鍵名,而Map集合常被用來獲取已存的信息。
全部對象的屬性名必須是字符串類型,必須確保每一個鍵名都是字符串類型且在對象中是惟一的
- 有序
- 不重複
+0和-0在Set中被認爲是相等的。
Set構造函數能夠接受全部可迭代對象做爲參數
複製代碼
forEach遍歷Set,回調函數中value和key的值相等,個人理解: Set集合中的元素都是不重複的,因此能夠把值做爲鍵,即「以值爲鍵」。以下:
let set = new Set([1, 2]);
set.forEach(function(value, key, ownerSet)){
console.log(key + " " + value);
console.log(ownerSet === set);
});
複製代碼
在回調函數中使用this引用
let set = new Set([1, 2]);
let processor = {
output(value) {
console.log(value);
},
process(dataSet) {
dataSet.forEach(function(value){
this.output(value);
}, this);
}
};
processor.process(set);
複製代碼
箭頭函數this
let set = new Set([1, 2]);
let processor = {
output(value) {
console.log(value);
},
process(dataSet) {
dataSet.forEach(value => this.output(value));
}
};
processor.process(set);
複製代碼
let set = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
array = [...set];
console.log(array); //[1, 2, 3, 4, 5]
複製代碼
function eliminbateDuplicates(items){
return [...new Set(items)]
}
let numbers = [1, 2, 3, 3, 3, 4, 5];
noDuplicates = eliminateDuplicates(numbers);
console.log(noDuolicates); //[1, 2, 3, 4, 5]
複製代碼
解決Set集合的強引用致使的內存泄漏問題
Weak Set集合只存儲對象的弱引用,而且不能夠存儲原始值;集合中的弱引用若是是對象惟一的引用,則會被回收並釋放相應內存。
Weak Set集合的方法:add, has,delete
差別 | Set | Weak Set |
最大區別 | 保存對象值的強引用 | 保存對象值的弱引用 |
方法傳入非對象參數 | 正常 | 報錯 |
可迭代性 | 可迭代 | 不可迭代 |
支持forEach方法 | 支持 | 不支持 |
支持size屬性 | 支持 | 不支持 |
- 有序
- 鍵值對
複製代碼
在對象中,沒法用對象做爲對象屬性的鍵名;可是Map集合中,卻能夠這樣作。
let map = new Map(),
key1 = {},
key2 = {};
map.set(key1, 5);
map.set(key2, 42);
console.log(map.get(key1)); //5
console.log(map.get(key2)); //42
複製代碼
以上代碼分別用對象 key1 和 key2 做爲兩個鍵名在Map集合裏存儲了不一樣的值。這些鍵名不會強制轉換成其餘形式,因此這兩個對象在集合中是獨立存在的,也就是說,不須要修改對象自己就能夠爲其添加一些附加信息。
Map集合的方法:set,get,has(key),delete(key),clear,forEach,size(屬性)
Map集合初始化過程當中,能夠接受任意數據類型的鍵名,爲了確保它們在被存儲到Map集合中以前不會被強制轉換爲其餘數據類型,於是只能將它們放在數組中,由於這是惟一一種能夠準確地呈現鍵名類型的方式。
無序 鍵值對
- 弱引用Map集合,集合中鍵名必須是一個對象,若是使用非對象鍵名會報錯;
- 鍵名對於的值若是是一個對象,則保存的是對象的強引用,不會觸發垃圾回收機制。
複製代碼
let map = new WeakMap(),
element = document.querySelector('.element');
map.set(element, "Original");
let value = map.get(element);
console.log(value); //"Original"
//移除element元素
element.parentNode.removeChild(element);
element = null;
//此時 Weak Map集合爲空,數據被同步清除
複製代碼
set, get, has, delete
私有對象數據
存儲對象實例的私有數據是Weak Map的另一個應用:
var Person = (function(){
var privateData = {},
privateData = 0;
function Person(name){
Object.defineProperty(this, "_id", { value: privateId++ });
privateData[this._id] = {
name: name
};
}
Person.prototype.getName = function() {
return privateData[this._id].name;
}
return Person;
}());
複製代碼
上面這種方法沒法獲知對象實例什麼時候被銷燬,不主動管理的話,privateData中的數據就永遠不會消失,須要使用Weak Map來解決這個問題。
let Person = (function(){
let privateData = new WeakMap(),
privateData = 0;
function Person(name){
privateData.set(this, {name: name});
}
Person.prototype.getName = function() {
return privateData.get(this).name;
}
return Person;
}());
複製代碼
當你要在Weak Map集合與普通的Map集合之間作出選擇時,須要考慮的主要問題是,是否只用對象做爲集合的鍵名。
迭代器的出現旨在消除循環複雜性並減小循環中的錯誤。
迭代器是一種特殊對象,他具備一些專門爲迭代過程設計的專有端口,全部的迭代器對象都有一個next()
方法,每次調用都返回一個結果對象。
ES5語法 實現一個迭代器
function createIterator(items) {
var i = 0;
return {
next: function() {
var done = (i >= items.length);
var value = !done ? items[i++] : undefined;
return {
done: done,
value: value
};
}
}
}
var iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); //"{ value: 1, done: false}"
console.log(iterator.next()); //"{ value: 2, done: false}"
console.log(iterator.next()); //"{ value: 3, done: false}"
console.log(iterator.next()); //"{ value: undefined, done: true}"
複製代碼
生成器是一種返回迭代器的函數,經過function
關鍵字後的星號(*)來表示,函數中會用到新的關鍵字yield
。
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
let iterator = createIterator();
console.log(iterator.next().value); //1
console.log(iterator.next().value); //2
console.log(iterator.next().value); //3
複製代碼
yield
的使用限制
yield
關鍵字只可在生成器內部使用,在其餘地方使用會致使程序拋出語法錯誤,即便在生成器內部的函數裏使用也會報錯,與return
關鍵字同樣,不能穿透函數的邊界。
不能用箭頭函數來建立生成器
for-of
循環可迭代對象具備Symbol.iterator
屬性,是一種與迭代器密切相關的對象。Symbol.iterator
經過指定的函數能夠返回一個做用於附屬對象的迭代器。
function isIterable(object) {
return typeof object[Symbol.iterator] === 'function';
}
console.log(isIterable([1, 2, 3])); //true
複製代碼
默認狀況下,開發者定義的對象都是不可迭代對象,但若是給Symbol.iterator添加一個生成器,則能夠將其變爲可迭代對象。
let collection = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
}
}
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);
for (let x of collection) {
console.log(x);
}
1
2
3
複製代碼
集合對象迭代器
字符串迭代器
NodeList迭代器
function *createIterator() {
let first = yield 1;
let second = yield first + 2; //4 + 2
yield second + 3; //5 + 3
}
let iterator = createIterator();
console.log(iterator.next()); // '{ value: 1, done: false }'
console.log(iterator.next(4)); // '{ value: 6, done: false }'
console.log(iterator.next(5)); // '{ value: 8, done: false }'
console.log(iterator.next()); // '{ value: undefined, done: true }'
複製代碼
調用next()方法命令迭代器繼續執行(可能提供一個值),調用throw()方法也會命令迭代器繼續執行,但同時也拋出一個錯誤,在此以後的執行過程取決於生成器內部的代碼。
展開運算符與
for-of
循環語句會直接忽略經過return語句指定的任何返回值,只要done
一變爲true
就當即中止讀取其餘的值。
在生成器裏面再委託另外兩個生成器
生成器使人興奮的特性與異步編程有關。
function run(taskDef) {
//建立一個無使用限制的迭代器
let task = taskDef();
//開始執行任務
let result = task.next();
//循環調用next() 的函數
function step() {
//若是任務未完成,則繼續執行
if(!result.done){
result = task.next();
//result = task.next(result.value) 向任務執行器傳遞數據
step();
}
}
//開始迭代執行
step();
}
複製代碼
ES6中的類與其餘語言中的仍是不太同樣,其語法的設計實際上借鑑了Javascript的動態性。
ES5 中的近類結構,建立一個自定義類型:
function PersonType(name) {
this.name = name;
}
PersonType.prototype.sayName = function() {
console.log(this.name);
}
var person = new PersonType('waltz');
person.sayName(); //'waltz'
console.log(person instanceof PersonType); //true
console.log(person instance of Object); //true
複製代碼
class PersonClass {
//等價於PersonType的構造函數
constructor(name){
this.name = name;
}
//等價於PersonType.protoType.sayName
sayName() {
console.log(this.name);
}
}
let person = new PersonClass('waltz');
person.sayName(); //'waltz'
console.log(person instanceof PersonClass); //true
console.log(person instanceof Object); //true
console.log(typeof PersonClass); //'function'
console.log(typeof PersonClass.prototype.sayName) //'function'
複製代碼
自有屬性是實例中的屬性,不會出如今原型上,且只能在類的構造函數或方法中建立。建議你在構造函數中建立全部的自有屬性,從而只經過一處就能夠控制類中的全部自有屬性。
與函數不一樣的是,類屬性不可被賦予新值。
let
聲明相似,不能被提高;真正執行聲明語句以前,它們會一直存在於臨時死區(TDZ)中。Object.defineProperty()
方法手工指定某個方法爲不可枚舉;而在類中,全部的方法都是不可枚舉的。[[Construct]]
的內部方法,經過關鍵字new調用那些不含[[Construct]]
的方法會致使程序拋出錯誤。new
之外的方式調用類的構造函數會致使程序拋出錯誤。和函數的聲明形式和表達式相似。
在js引擎中,類表達式的實現與類聲明稍有不一樣。對於類聲明來講,經過let定義的外部綁定與經過const定義的內部綁定具備相同的名稱。而命名類表達式經過const定義名稱,從而只能在類的內部使用。
在程序中。一等公民是指一個能夠傳入函數,能夠從函數返回,而且能夠賦值給變量的值。(JS函數是一等公民)
function createIbject(classDef) {
return new classDef();
}
let Obj = createObject(class {
sayHi() {
console.log('Hi!')
}
});
obj.sayHi(); //'Hi!'
複製代碼
類表達式還有另外一種使用方式,經過當即調用類構造函數能夠建立單例。用new
調用類表達式,緊接着經過一對小括號調用這個表達式:
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('waltz');
person.sayName(); // 'waltz'
複製代碼
依照這種模式可使用類語法建立單例,而且不會再做用域中暴露類的引用。
class CustomHtmlElement() {
constructor(element){
this.element = element;
}
//建立getter
get html() {
return this.element.innerHTML;
}
//建立setter
set html(value) {
this.element.innnerHTML = value;
}
}
var descriptor = Object.getOwnPropertyDescriptor(CustomHtmlElement.prototype, "html");
console.log("get" in descriptor); //true
console.log("set" in descriptor); //true
console.log(descriptor.enumerable); //false
複製代碼
//類方法
let methodName = "sayName";
class PersonClass(name) {
constructor(name) {
this.name = name;
}
[methodName]() {
console.log(this.name);
}
};
let me = new PersonClass("waltz");
me.sayName(); // 'waltz'
複製代碼
//訪問器屬性
let propertyName = 'html';
class CustomHTMLElement)() {
constructor(element) {
this.element = element;
}
get [propertyName]() {
return this.element.innerHTML;
}
set [propertyName](value) {
this.element.innerHTML = value;
}
}
複製代碼
class MyClass {
*createIterator() {
yield 1;
yield 2;
yield 3;
}
}
let instance = new MyClass();
let iterator = instance.createIterator();
複製代碼
若是用對象來表示集合,又但願經過簡單的方法迭代集合中的值,那麼生成器方法就派上用場了。
儘管生成器方法很實用,但若是你的類是用來表示值的 集合 的,那麼爲它定義一個 默認迭代器 更有用。
直接將方法添加到構造函數中來模擬靜態成員是一種常見的模式。
function PersonType(name) {
this.name = name;
}
//靜態方法
PersonType.create = function(name) {
return new PersonType(name);
}
//實例方法
PersonType.protoType.sayName = function() {
console.log(this.name);
};
var person = PersonType.create('waltz');
複製代碼
類等價:
class PersonClass {
// 等價於PersonType構造函數
constructor(name) {
this.name = name;
}
//等價於PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
//等價於PersonType.create
static create(name) {
return new PersonClass(name);
}
}
let person = PersonClass.create('waltz');
複製代碼
類中的全部方法和訪問器屬性均可以用static關鍵字來定義,惟一的限制是不能將static用於定義構造函數方法。
不可在實例中訪問靜態成員,必需要直接在類中訪問靜態成員。
ES5實現
function Rectangle(length, width) {
this.length = length;
this.width = width;
}
Rectangle.prototype.getArea = function() {
return this.length * this.width;
};
function Square(length) {
Rectangle.call(this, length, length);
}
Square.prototype = Object.create(Rectangle.prototype, {
constuctor: {
value: Square,
enumerable: true,
writable: true,
configurable: true
}
});
var square = new Square(3);
console.log(square.getArea()); // 9
console.log(square instanceof Square); //true
console.log(square instanceof Rectangle); true
複製代碼
ES6類實現
class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
getArea() {
return this.length * this.width;
}
}
class Square extends Rectangle {
//派生類指定了構造函數則必需要調用 super()
constructor(length) {
//等價於Retangle.call(this, length, length)
super(length, length);
}
//若是不使用構造函數,則當建立新的類實例時會自動調用 super() 並傳入全部參數
}
var square = new Square(3);
console.log(square.getArea()); //9
console.log(square instanceof Square); //true
console.log(square instanceof Rectangle); //true
複製代碼
使用super()的小貼士:
- 只可在派生類的構造函數中使用
super()
,若是嘗試在非派生類(不是用extends
聲明的類)或函數中使用則會致使程序拋出錯誤。- 在構造函數中訪問
this
以前必定要調用super()
,它負責初始化this
,若是在調用super()
以前嘗試訪問this
會致使程序錯誤。- 若是不想調用
super()
,則惟一的方法是讓類的構造函數返回一個對象。
類方法遮蔽 --- 派生類中的方法總會覆蓋基類中的同名方法。
靜態成員繼承 --- 若是基類有靜態成員,那麼這些靜態成員在派生類中也可用。
派生自表達式的類 --- 只要表達式能夠解析爲一個函數而且具備[[Constructor]]屬性和原型,那麼就能夠用extends進行派生。 extends強大的功能使得類能夠繼承自任意類型的表達式,從而創造更多可能性。
因爲能夠動態肯定使用哪一個基類,於是能夠建立不一樣的繼承方法
let SerializationMixin = {
serialize() {
return JSON.stringify(this);
}
};
let AreaMixin = {
getArea() {
return this.length * this.width;
}
};
function mixin(...mixins) {
var base = function() {};
Object.assign(base.prototype, ...mixins);
return base;
}
class Square extends mixin(AreaMixin, SerializableMixin) {
constructor(length) {
super();
this.length = length;
this.width = length;
}
}
var x = new Square(3);
console.log(x.getArea()); //9
console.log(x.serialize()); // "{'length': 3, 'width': 3}"
//若是多個mixin對象具備相同屬性,那麼只有最後一個被添加的屬性被保留。
複製代碼
內建對象的繼承
class MyArray extends Array {
//空
}
var colors = new MyArray();
colors[0] = "red";
console.log(colors.length); //1
colors.length = 0;
console.log(colors[0]); //undefined
複製代碼
Symbol.species
屬性
內建對象繼承的一個實用之處,本來在內建對象中返回實例自身的方法將自動返回派生類的實例。
Symbol.species
是諸多內部Symbol中的一個,它被用於定義返回函數的靜態訪問器屬性。被返回的函數是一個構造函數,每當要在實例的方法中(不是在構造函數中)建立類的實例時必須使用這個構造函數。
通常來講,只要想在類方法中調用
this.constructor
,就應該使用Symbol.species
屬性,從而讓派生類重寫返回類型。並且若是你正從一個已定義Symbol.species
屬性的類建立派生類,那麼確保使用哪一個值而不是使用構造函數。
class MyArray extends Array {
static get [Symbol.species]() {
return Array;
}
}
let items = new MyArray(1, 2, 3, 4),
subitems = items.slice(1, 3);
console.log(items instanceof MyArray); //true
console.log(subitems instanceof Array); //true
console.log(subitems instanceof MyArray); //false
複製代碼
new.target
在簡單狀況下,new.target
等於類的構造函數。
由於類必須經過new
關鍵字才能調用,因此在列的構造函數中,new.target
屬性永遠不會是undefined
。
1.1 ES6以前建立數組的方法:
1.2 ES6:
Array.of();
做用:幫助開發者們規避經過Array構造函數建立數組是的怪異行爲,由於,Array構造函數表現的與傳入的的參數類型及數量有些不符;
function createArray(arrayCreator, value){
return arrayCreator(value);
}
let items = createArray(Array.of, value)
複製代碼
Array.from();
ES5方法將類數組轉化爲真正的數組:
function makeArray(arrayLike) {
var result = [];
for(var i=0, len = arrayLike.length; i<len; i++) {
result.push(arrayLike[i]);
}
return result;
}
function doSomething() {
var args = makeArray(arguments);
//使用args
}
複製代碼
改進方法:
function makeArray(arrayLike) {
return Array.prototype.slice.call(arrayLike);
}
function doSomething() {
var args = makeArray(arguments);
//使用args
}
複製代碼
ES6-Array.from():
function doSomething() {
var args = Array.from(arguments);
//使用args
}
複製代碼
1.3 映射轉換
若是想要進一步轉化數組,能夠提供一個映射函數做爲Array.from()
的第二個參數,這個函數用來將類數組對象中的每個值轉換成其餘形式,最後將這些結果儲存在結果數組的相應索引中。
function translate() {
return Array.from(arguments, (value) => value + 1);
}
let numbers = translate(1, 2, 3);
console.log(numbers); //2, 3, 4
複製代碼
也能夠傳入第三個參數來表示映射函數的this值
let helper = {
diff: 1,
add(value) {
return value + this.diff;
}
};
function translate() {
return Array.from(arguments, helper.add, helper);
}
let numbers = translate(1, 2, 3);
console.log(numbers); //2, 3, 4
複製代碼
用Array.from()
轉換可迭代對象
let numbers = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
};
let numbers = Array.from(numbers, (value) => value + 1);
console.log(numbers2); //2, 3, 4
複製代碼
若是一個對象既是類數組又是可迭代的,那麼
Array.from()
方法會根據迭代器來決定轉換那個值。
2.1 find()
方法和findIndex()
方法
一旦回調函數返回
true
,find()
方法和findIndex()
方法都會當即中止搜索數組剩餘的部分。
適用於根據某個條件查找匹配的元素,若是隻想查找與某個值匹配的元素,則indexOf()
方法和lastIndexOf()
方法是更好的選擇。
2.2 fill()
方法
若是開始索引或結束索引爲負值,那麼這些值會與數組的length屬性相加來做爲最終的位置。
2.3copyWith()
方法
傳入兩個參數,一個是開始填充值的索引位置,另外一個是開始複製值的索引位置。(若是索引存在負值,也會與數組的length屬性相加做爲最終值)
3.1 定義
定型數組能夠爲JavaScript帶來快速的換位運算。ES6採用定型數組做爲語言的正式格式來確保更好的跨JavaScript引擎兼容性以及與JavaScript數組的互操做性。
所謂定型數組,就是將任何數字轉換爲一個包含數字比特的數組。
定型數組支持存儲和操做如下8種不一樣的數值類型:
全部與定型數組有關的操做和對象都集中在這8個數據類型上,可是在使用它們以前,須要建立一個 數組緩衝區 存儲這些數據。
3.2 數組緩衝區
數組緩衝區是全部定型數組的根基,它是一段能夠包含特定數量字節的內存地址。(相似
c
語言malloc()
分配內存)
let buffer = new ArrayBuffer(10); //分配10字節
console.log(buffer.byteLength); // 10
複製代碼
數組緩衝區包含的實際字節數量在建立時就已肯定,能夠修改緩衝區內的數據,可是不能改變緩衝區的尺寸大小。
DataView
類型是一種通用的數組緩衝區視圖,其支持全部8種數值型數據類型。
let buffer = new ArrayBuffer(10),
view = new DataView(buffer);
複製代碼
能夠基於同一個數組緩衝區建立多個view
, 於是能夠爲應用申請一整塊獨立的內存地址,而不是當須要空間時再動態分配。
獲取讀取試圖信息
讀寫視圖信息
let buffer = new ArrayBuffer(2),
view = new DataView(buffer);
view.setInt(0, 5);
view.setInt(1, -1);
console.log(view.getInt16(0)); // 1535
console.log(view.getInt8(0)); //5
console.log(view.getInt8(1)); //-1
複製代碼
定型數組是視圖
3.3 定型數組與普通數組的類似之處
能夠修改
length
屬性來改變普通數組的大小,而定型數組的length
屬性是一個不可寫屬性,因此不能修改定型數組的大小。
3.4 定型數組與普通數組的差異
定型數組和普通數組最重要的差異是:定型數組不是普通數組。
定型數組一樣會檢查數據類型的合法性,0被用於代替因此非法值。
set(): 將其它數組複製到已有的定型數組。
subarray(): 提取已有定型數組的一部分做爲一個新的定型數組。
JavaScript既能夠像事件和回調函數同樣指定稍後執行的代碼,也能夠明確指示代碼是否成功執行。
JavaScript引擎一次只能執行一個代碼塊,因此須要跟蹤即將運行的代碼,那些代碼被放在一個任務隊列中,每當一段代碼準備執行時,都會被添加到任務隊列。每當JavaScript引擎中的一段代碼結束執行,事件循環(event loop) 會執行隊列中的下一個任務,它是JavaScript引擎中的一段程序,負責監督代碼執行並管理任務隊列。
事件模型--->回調模式--->Promise
Promise至關於異步操做結果的佔位符,它不會去訂閱一個事件,也不會傳遞一個回調函數給目標函數,而是讓函數返回一個Promise對象。like:
// readFile承諾將在將來的某個時刻完成
let promise = readFile("example.txt");
複製代碼
操做完成後,Promise會進入兩個狀態:
Fulfilled
Promise異步操做成功完成;Rejected
因爲程序錯誤或一些其餘緣由,Promise異步操做未能成功完成。內部屬性[[PromiseState]]
被用來表示Promise
的三種狀態:"pending"、"fulfilled"、"rejected"。這個屬性不暴露在Promise
對象上,因此不能以編程的方式檢測Promise
的狀態,只有當Promise
的狀態改變時,經過then()
方法採起特定的行動。
若是一個對象實現了上述的
then()
方法,那這個對象咱們稱之爲thenable
對象。全部的Promise都是thenable
對象,但並不是全部thenable
對象都是Promise
。
then方法
catch方法(至關於只給其傳入拒絕處理程序的then()方法)
// 拒絕
promise.catch(function(err)) {
console.error(err.message);
});
複製代碼
與下面調用相同
promise.then(null, function(err)){
// 拒絕
console.error(error.message);
});
複製代碼
Promise比事件和回調函數更好用
若是一個Promise處於已處理狀態,在這以後添加到任務隊列中的處理程序仍將進行。
Promise的執行器會當即執行,而後才執行後續流程中的代碼:
let promise = new Promise(function(resolve, reject){
console.log("Promise");
resolve();
})
console.log("Hi!");
//Promise
//Hi!
複製代碼
完成處理程序和拒絕處理程序老是在 執行器 完成後被添加到任務隊列的末尾。
使用Promise.resolve()
使用Promise.reject()
若是向
Promise.resolve()
方法或Promise.reject()
方法傳入一個Promise
, 那麼這個Promise
會被直接返回。
非Promise
的Thenable
對象
Promise.resolve()
方法和Promise.reject()
方法均可以接受非Promise
的Thenable
對象做爲參數。若是傳入一個非Promise
的Thenable
對象,則這些方法會建立一個新的Promise
,並在then()
函數中被調用。
非Promise
的Thenable
對象: 擁有then()
方法而且接受resolve
和reject
這兩個參數的普通對象。
若是不肯定某個對象是否是Promise
對象,那麼能夠根據預期的結果將其傳入Promise.resolve()
方法中或Promise.object()
方法中,若是它是Promise
對象,則不會有任何變化。
每一個執行器都隱含一個try-catch
塊,因此錯誤會被捕獲並傳入拒絕處理程序。
Promise
拒絕處理有關Promise
的其中一個 最具爭議 的問題是,若是在沒有拒絕處理程序的狀況下拒絕一個Promise
,那麼不會提示失敗信息。
6.1 Node.js環境的拒絕處理
unhandledRejection
在一個事件循環中,當Promise
被拒絕,而且沒有提供拒絕處理程序時,觸發該事件。
let rejected;
process.on("unhandledRejection", function(reason, promise){
console.log(reason.message); // "Explosion!"
console.log(rejected === promise); // true
});
rejected = Promise.reject(new Error("Explosion!"));
複製代碼
rejectionHandled
在一個事件循環以後,當Promise
被拒絕時,若拒絕處理程序被調用,觸發該事件。
let rejected;
process.on("rejectionHandled", function(promise){
console.log(rejected === promise); // true
});
rejected = Promise.reject(new Error("Explosion!"));
//等待添加拒絕處理程序
setTimeout(function(){
rejected.catch(function(value){
console.log(value.message); // "Explosion!"
});
}, 1000);
複製代碼
6.2 瀏覽器環境 的拒絕處理
unhandledRejection
(描述與Node.js相同)
rejectionHandled
瀏覽器中的實現與
Node.js
中的幾乎徹底相同,兩者都是用一樣的方法將Promise
及其拒絕值存儲在Map集合中,而後再進行檢索。惟一的區別是,在事件處理程序中檢索信息的位置不一樣。
Promise
每次調用
then()
方法或catch()
方法時實際上建立並返回了另外一個Promise
,只有當第一個Promise
完成或被拒絕後,第二個纔會被解決。
務必在
Promise
鏈的末尾留有一個拒絕處理程序以確保可以正確處理全部可能發生的錯誤。
拒絕處理程序中返回的值仍可用在下一個
Promise
的完成處理程序中,在必要時,即便其中一個Promise
失敗也能恢復整條鏈的執行。
Promise
中返回Promise
在完成或拒絕處理程序中返回Thenable
對象不會改變Promise
執行器的執行動機,先定義的Promise
的執行器先執行,後定義的後執行。
Promise
Promise.All()
方法Promise.race()
方法Promise
繼承Promise與其餘內建類型同樣,也能夠做爲基類派生其餘類,因此你能夠定義本身的Promise
變量來擴展內建Promise
的功能。
Promise
的異步任務執行let fs = require("fs");
function run(taskDef) {
//建立迭代器
let task = taskDef();
//開始執行任務
let result = task.next();
//遞歸函數遍歷
(function step() {
//若是有更多任務要作
if(!result.done) {
//用一個Promise來解決會簡化問題
let promise = Promise.resolve(result.value);
promise.then(function(value) {
result = task.next(value);
step();
}).catch(function(error){
result = task.throw(error);
step();
})
}
}());
}
//定義一個可用於任務執行器的函數
function readFile(filename) {
return new Promise(function(resolve, reject) {
fs.readFile(filename, function(err, contents){
if(err){
reject(err);
}else{
resolve(contents);
}
});
});
}
//執行一個任務
run(function*(){
let contents = yield readFile("config.json");
doSomethingWith(contents);
console.log("done");
})
複製代碼
ES2017 await
代理
(Proxy)
是一種能夠攔截並改變底層JavaScript引擎操做的包裝器,在新語言中經過它暴露內部運做的對象。
調用 new Proxy()
可建立代替其餘目標對象的代理,它虛擬化了目標,因此兩者看起來功能一致。
代理能夠攔截 JavaScript
引擎內部目標的底層對象操做,這些底層操做被攔截後會觸發響應特定操做的陷阱函數。
反射API
以Reflect
對象的形式出現,對象中方法的默認特性與相同的底層操做一致,而代理能夠覆寫這些操做,每一個代理陷阱對應一個命名和參數都相同的Reflect
方法。
set
陷阱驗證屬性 / 用get
陷阱驗證對象解構(Object Shape)
set
代理陷阱能夠攔截寫入屬性的操做,get
代理陷阱能夠攔截讀取屬性的操做。
let target = {
name: "target"
}
let proxy = new Proxy(target, {
set(trapTarget, key, value, receiver) {
//忽略不但願受到影響的已有屬性
if(!trapTarget.hasOwnProperty(key)) {
if(isNaN(value)) {
throw new TypeError("屬性必須是數字");
}
}
//添加屬性
return Reflect.set(trapTarget, key, value, receiver);
}
});
//添加一個新屬性
proxy.count = 1;
console.log(proxy.count); //1
console.log(target.count); //1
//因爲目標已有name屬性於是能夠給它賦值
proxy.name = "proxy";
console.log(proxy.name); //"proxy"
console.log(target.name); //"proxy"
複製代碼
let proxy = new Proxy({},{
get(trapTarget, key, receiver) {
if (!(key in receiver)) {
throw new TypeError("屬性" + key + "不存在");
}
return Reflect.get(trapTarget, key, receiver);
}
});
//添加一個屬性,程序仍正常運行
proxy.name = "proxy";
console.log(proxy.name); // "proxy"
//若是屬性不存在,則拋出錯誤
console.log(proxy.nme); // 拋出錯誤
複製代碼
has
陷阱隱藏已有屬性deleteProperty
陷阱防止刪除屬性原型代理陷阱有一些限制:
getPrototypeOf
陷阱必須返回對象或null
,只要返回值必將致使運行時錯誤,返回值檢查能夠確保Object.getPropertyOf()
返回的老是預期的值;setPropertyOf
陷阱中,若是操做失敗則返回的必定是false
,此時Object.setPrototypeOf()
會拋出錯誤,若是setPrototypeOf
返回了任何不是false
的值,那麼Object.setPrototypeOf()
便假設操做成功。Object.getPrototypeOf()
和Object.setPrototypeOf()
是高級操做,建立伊始便給開發者使用的;而Reflect.getPrototypeOf()
和Reflect.setPrototypeOf()
方法則是底層操做,其賦予開發者能夠訪問以前只在內部操做的[[GetPrototypeOf]]
和[[SetPrototypeOf]]
的權限。
Object.setPrototypeOf()
和Reflect.setPrototypeOf()
之間在返回值上有微妙的差別,前者返回傳入的對象,後者返回布爾值。
preventExtensions
(阻止擴展)isExtensible
(判斷是否可擴展)相比高級功能方法而言,底層的具備更嚴格的錯誤檢查。
defineProperty
(定義屬性)getOwnPropertyDescriptor
(獲取屬性)給Object.defineProperty()添加限制
若是讓陷阱返回
true
而且不調用Reflect.defineProperty()
方法,則可讓Object.definePropperty()
方法靜默失效,這既消除了錯誤又不會真正定義屬性。
描述符對象限制
當
defineProperty
陷阱被調用時,descriptor
對象有value
屬性卻沒有name
屬性,這是由於descriptor
不是實際傳入Object.defineProperty()
方法的第三個參數的引用,而是一個只包含那些被容許使用的屬性的新對象。Reflect.defineProperty()
方法一樣也忽略了描述符上的全部非標準屬性。
ownKeys
陷阱
ownKeys
陷阱經過Reflect.ownKeys()
方法實現默認的行爲,返回的數組中包含全部自有屬性的鍵名,字符串類型和Symbol
類型的都包含在內。
Object.getOwnPropertyNames()
方法和Object.keys()
方法返回的結果將Symbol
類型的屬性名排除在外。Object.getOwnPropertySymbols()
方法返回的結果將字符串類型的屬性名排除在外。Object.assign()
方法支持字符串和Symbol
兩種類型。apply
和construct
陷阱全部的代理陷阱中,只有
apply
和construct
的代理目標是一個函數。
new
調用構造函數能夠經過檢查new target
的值來肯定函數是不是經過new
來調用的。
假設Numbers()
函數定義在你沒法修改的代碼中,你知道代碼依賴new target
,但願函數避免檢查卻仍想調用函數。在這種狀況下,用new
調用時的行爲已被設定,因此你只能使用apply
陷阱。
length
的值length
的值來刪除元素想要建立使用代理的類,最簡單的方法是像往常同樣定義類,而後在構造函數中返回一個代理,那樣的話,當類實例化時返回的對象是代理而不是實例(構造函數中的this
是該實例)。
將代理用做原型
雖然從類構造函數返回代理很容易,但這也意味着每建立一個實例都要建立一個新代理。然而有一種方法可讓全部的實例共享一個代理:將代理用做原型。
模塊是自動運行在嚴格模式下而且沒有辦法退出運行的
Javascript
代碼。
注:在模塊的頂部,this
的值是undefined
;模塊不支持HTML風格的代碼註釋。
綁定的微妙怪異之處
export var name = "xszi";
export function setName(newName) {
name = newName;
}
//導入以後
import { name, setName } from "./example.js";
console.log(name); //xszi
setName("waltz");
console.log(name); //waltz
name = "hahha"; //拋出錯誤
複製代碼
導入和導出重命名
模塊的默認值
只能爲每一個模塊設置一個默認的導出值,導出時屢次使用default
關鍵字是一個語法錯誤。
用逗號將默認的本地名稱與大括號包裹的非默認值分隔開,請記住,在import
語句中,默認值必須排在非默認值以前。
從新導出一個綁定
無綁定導入
即便沒有任何導出或導入的操做,這也是一個有效的模塊。
無綁定導入最有可能被應用與建立Pilyfill
和Shim
Shim
: 是一個庫,它將一個新的API引入到一箇舊的環境中,並且僅靠舊環境中已有的手段實現。
Polyfill
: 一個用在瀏覽器API上的Shim
,咱們一般的作法是先檢查當前瀏覽器是否支持某個API,若是不支持的話就加載對用的polyfill
。
把舊的瀏覽器想一想成一面有裂縫的牆,這些
polyfill
會幫助咱們把這面牆的裂縫填平。
在web瀏覽器中使用模塊
//加載一個JavaScript模塊文件
<script type="module" src="module.js"></script>
複製代碼
//內聯引入模塊
<script type="module">
import { sum } from "./example.js";
let result = sum(1, 2)
</script>
複製代碼
模塊與腳本不一樣,它是獨一無二的,能夠經過import
關鍵字來指明其所依賴的其餘文件,而且這些文件必須被加載進該模塊才能正確執行。爲了支持該功能,<script type="module">
執行時自動應用defer
屬性。
每一個模塊均可以從一個或多個其餘的模塊導入,這會使問題複雜化。所以,首先解析模塊以識別全部導入語句;而後,每一個導入語句都觸發一次獲取過程(從網絡或從緩存),而且在全部導入資源都被加載和執行後纔會執行當前模塊。
//沒法保證這兩個哪一個先執行
<script type="module" async src="module1.js"></script>
<script type="module" async src="module2.js"></script>
複製代碼
將模塊做爲Worker加載
Worker
能夠在網頁上下文以外執行JavaScript
代碼。
//按照腳本的方式加載script.js
let worker = new Worker("script.js");
複製代碼
//按照模塊的方式加載module.js
let worker = new Worker("module.js", {type: "module"});
複製代碼
IEEE 754
只能準確的表示-2^53 ~ 2^53之間的整數:
var inside = Number.MAX_SAFE_INTEGER,
outside = inside + 1;
console.log(Number.isInteger(inside)); //true
console.log(Number.isSafeInteger(inside)); //true
console.log(Number.isInteger(outside)); //true
console.log(Number.isSafeInteger(outside)); //false
複製代碼
Math
方法提升一般的數學計算的速度
Unicode
標識符_ptoto_
屬性實際上,_proto_
是Object.getPrototypeOf()
方法和Object.setPrototypeOf()
方法的早期實現。
5 ** 2 == Math.pow(5, 2); //true
複製代碼
求冪運算符在JavaScript全部二進制運算符中具備最高的優先級(一元運算符的優先級高於**)
奇怪之處:
includes()
方法認爲+0和-0是相等的,Object.is()
方法會將+0和-0識別爲不一樣的值。
ECMAScript 2016
規定在參數被解構或有默認參數的函數中禁止使用"use strict"
指令。只有參數爲不包含解構或默認值的簡單參數列表才能夠在函數體中使用"use strict"
。