一、let所聲明的變量只在let命令所在的代碼塊有效javascript
二、不存在變量提高html
在代碼塊內,使用let命令聲明變量以前,該變量都是不可用的,稱爲暫時性死區。java
三、不容許重複聲明es6
let 不容許在相同做用域內重複聲明一個變量編程
四、塊級做用域數組
沒有塊級做用域的不合理的場景數據結構
ar temp = new Date(); function f() { console.log(temp); if(true)// true或false結果同樣 { var temp = "hello" } }
f() //undefined
由於變量提高,致使內層的tmp變量覆蓋了外層的tem變量app
提高的爲變量聲明 var tmp函數式編程
可是沒有初始化,在if判斷裏纔對聲明的變量初始化函數
塊級做用域的出現實際上使得普遍應用的當即執行函數再也不必要了
//IIFE寫法 (function() { var tmp = ...; }());
//塊級做用域寫法 { let tmp = ...; }
五、const命令
1)const用來聲明常量。一旦聲明,其值就不能改變
2)只在聲明塊級做用域有效
3)const命令聲明的常量也不提高
4)對於複合類型的變量,變量名不指向數據,而是指向數據所在的地址。const命令只是保證變量名指向的地址不變,並不保證該地址的數據不變,因此將一個對象聲明爲常量必須十分當心
六、ES6一共有6種聲明變量的方法:
var,function,let ,const,import,class
七、全局對象的屬性
let,class,const聲明的全局變量不屬於全局變量的屬性(僅僅不是他的屬性)
八、變量的解構賦值
解構賦值容許指定默認值
var [a,b,c] = [1,2,3]
九、對象的解構賦值
var {foo,bar} = {foo:"aaa",bar:"bbb"}
對象的解構和數組有個重要的不一樣,數組的元素是按次序排列的,變量由它的位置決定。
對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。
第七章 數組的擴展
一、Array.from
Array.from 用於將兩類對象轉爲真正的數組:相似數組的對象和可遍歷對象
下面是一個相似數組的對象:
let arraylike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; // ES5的寫法 var arr1 = [].slice.call(arraylike);//['a','b','c'] //es6的寫法 let arr2 = Array.from(arraylike);//['a','b','c']
實際應用中,常見的相似數組的對象是DOM操做返回的Nodelist集合,以及函數內部的arguments對象,Array.from 均可以將他們轉爲真正的數組
只要是部署了interater接口的數據結構,Array.from 都能將其轉爲數組
set
所謂相似數組的對象,本質特徵只有一點,必須有length屬性,所以任何有 length屬性的對象,均可以經過Array.from()轉爲數組
Array.from第二個參數相似map
Array.from(arraylike,x=>x*x)
Array.from(arraylike).map(x => x*x)
Array.of用於將一組值轉爲數組
copyWithin()
複製數組,會修改當前數組
find()
用於找到第一個符合條件的數組成員,他的參數爲一個回調函數,全部成員執行這個回調函數,找到第一個返回true的成員,返回該成員,不然返回undefined
findIndex()
返回第一個符合條件的數組成員的位置,全部都不符合返回-1
fill()
使用給定值填充數組
entries(),keys(),values()
遍歷數組
includes()
返回一個布爾值,表示某個數組是否包含給定的值
以前使用indexof,有兩個缺點一、不夠語義化 二、內部使用嚴格運算符(===)判斷,對NaN誤判
map和set解構有一個has注意區分,Map.prototype.has(key) 查找鍵名
Set.prototype.has(value)查找值
數組的空位
空位不是undefined一個位置等於undefined依然是有值的,空位是沒有任何值
0 in [undefined,undefined,undefined]//true 0 in [, , ,]//false
0號位置有值,沒值
Array.from()、擴展運算符會將空位轉爲undefined
數組推導
容許直接經過現有數組生成新數組
var a1 = [1,2,3,4]; var a2 = [for (i of a1) i*2]; a2 //[2,4,6,8]
第九章 函數的擴展
一、ES6以前,不能直接爲函數的參數指定默認值,只能採用變通的方法
es6容許爲函數的參數設置默認值,直接寫在參數定義的後面
function log(x,y = 'World'){ console.log(x,y) }
參數變量時默認聲明的,因此不能用let或者const再次聲明
二、函數的length屬性
指定了默認值之後,函數的length屬性將返回沒有指定默認值的參數個數
(function(a){}).length // 1 (function(a = 5){}).length // 0
三、做用域
若是參數默認值是一個變量,則該變量所處的做用域與其餘變量的做用域規則是同樣的,先是當前函數的做用域而後纔是全局做用域
let x = 1; function f(x,y = x){ console.log(y) } f(2);//2
以上,函數做用域的內部變量x已經生成,因此y等於參數x而不是全局變量x
let x = 1; function f(y = x){ let x = 2; console.log(y) } f();//1
函數調用時y的默認值變量x尚未在函數內部生成,因此x指向全局變量
若是函數A的參數默認值是函數B,那麼因爲函數的做用域是其聲明時所在的做用域,函數B的做用域就不是函數A而是全局做用域。
四、rest參數
...變量名
用於獲取函數的多餘參數,這樣就不須要使用arguments對象了
rest參數以後不能有其餘參數,函數length屬性不包括rest參數
五、擴展運算符
...
比如rest參數的逆運算,將一個數組轉爲用逗號分割的參數序列
console.log(...[1,2,3]) //1 2 3
六、替代數組的apply方法
因爲擴展運算符能夠展開數組,因此不須要apply方法將數組轉爲函數的參數了
//ES5 的寫法 function f(x,y,z){} var args = [0,1,2]; f.apply(null,args); //ES6的寫法 function f(x,y,z){} var args = [0,1,2]; f(...args);
七、擴展運算符的應用
1)合併數組
var arr1 = ['a','b'] var arr2 = ['c'] var arr3 = ['d','e'] //es5 arr1.concat(arr2,arr3) //es6 [...arr1,...arr2,...arr3]
2)字符串
擴展運算符能夠將字符串專爲真正的數組
八、name屬性
函數的name屬性返回該函數的函數名
function foo() {} foo.name // 'foo'
九、箭頭函數
var sum =(num1, num2) => num1 + num2; //等同於 var sum =function(num1, num2) { return num1 + num2; }
若是箭頭函數的代碼多於一條語句,就要使用大括號將其括起來,並使用return語句
箭頭函數能夠與變量解構結合使用
const full = ({first,last}) => first + '' + last; //等同於 function full(person) { return person.first + '' +person.name; }
箭頭函數使用注意點:
一、函數體內this 對象就是定義時所在的對象,而不是使用時所在的對象
二、不能夠看成構造函數,不可使用new命令
三、不可使用arguments對象,該對象在函數體內不存在,若是要用能夠用rest參數代替
this指向的固定化並非由於箭頭函數內部有綁定this的機制,實際緣由是箭頭函數根本沒有本身的this
因爲箭頭函數沒有本身的this,固然也就不能用call(),apply(),bind()這些方法去改變this的指向
十、尾調用優化
尾調用是函數式編程的一個重要概念
就是指某個函數的最後一步是調用另外一個函數
function f(x){ return g(x); }
函數調用會在內存中造成一個‘調用記錄’。稱爲調用幀,保存調用位置和內部變量的信息。
若是在函數A的內部調用函數B,那麼在A的調用幀的上方還會造成B的調用幀,等到b運行結束,將結果返回A,B的調用幀纔會消失
尾調用因爲是函數最後一步操做,因此不須要保留外層函數的調用幀,由於調用位置,內部變量等信息都不會再用到了,直接用內層函數的調用幀取代外層函數便可
function f() { let m = 1; let n = 2; return g(m+n); } f() //等同於 function f() { return g(3); } f() //等同於 g(3)
十一、尾遞歸
遞歸很是耗費內存,由於同時須要保存成百上千個調用幀,很容易發生棧溢出錯誤,但對於尾遞歸來講,因爲只存在一個調用幀,因此永遠不會發生棧溢出錯誤
非尾調用,
function factorial(n) { if(n === 1) return 1; return n * factorial(n-1); } factorial(5) // 120
尾調用
function factorial(n, total) { if(n === 1) return total; return factorial(n - 1,n*total); } factorial(5,1);
若是改寫爲一個尾遞歸,只保留一個調用記錄,複雜度爲O(1)
尾遞歸的實現每每須要改寫遞歸函數,確保最後一步只調用自身。作到這一點的方法就是把全部用到的內部變量改寫成函數的參數
缺點是不太直觀
方法一:在尾遞歸函數以外再提供一個正常形勢的函數
function tailfactorial(n, total) { if(n === 1) return total; return factorial(n - 1,n*total); } function factorial(n) { return tailfactorial(n,1); } factorial(5);
函數式編程有一個概念:柯里化 :將多參數的函數轉換成單參數的形式
柯里化是這樣的一個轉換過程,把接受多個參數的函數變換成接受一個單一參數(譯註:最初函數的第一個參數)的函數,若是其餘的參數是必要的,返回接受餘下的參數且返回結果的新函數。
function currying(fn,n) { return function(m) { return fn.call(this, m, n) } } function tailfactorial(n, total) { if(n === 1) return total; return factorial(n - 1,n*total); } const factorial = currying(tailfactorial, 1); factorial(5)
方法二:採用ES6的默認值
function tailfactorial(n, total = 1) { if(n === 1) return total; return factorial(n - 1,n*total); }
第9章 對象的擴展
一、ES6容許在對象中只寫屬性名不寫屬性值
function f(x,y){ return {x,y}; } //等同於 function f(x,y){ return {x:x,y:y}; }
二、屬性名錶達式
方法一:
obj.foo = true
方法二:
obj['a'+'bc'] = 123
三、方法的name屬性
函數的name屬性返回函數名,對象方法也是函數,所以也有name屬性
若是對象方法是一個symbol值,那麼name屬性返回的是這個symbol值的描述
每一個從Symbol()返回的
symbol值都是惟一的。一個symbol值能做爲對象屬性的標識符;這是該數據類型僅有的目的。
四、object.is()
object.is 用來比較兩個值是否嚴格相等,它與嚴格比較運算符(===)的行爲基本一致
Object.is('foo','foo') // true Object.is({},{}) // false
五、Object.assign() 這是一種淺複製
用來將源對象的全部可枚舉屬性複製到目標對象。它至少須要兩個對象做爲參數,第一個參數是目標對象,後面的參數都是源對象。
只要有一個參數不是對象,就會拋出錯誤
var target = {a: 1}; var source1 = {b:2}; var source2 = {c:3}; Object.assign(target,source1,source2); target //{a:1,b:2,c:3}
若是目標屬性和源對象有同名屬性,或者多個源對象有同名屬性,後面的屬性會覆蓋前面的屬性
var target = {a: 1,b:1}; var source1 = {b:2,c:2}; var source2 = {c:3}; Object.assign(target,source1,source2); target //{a:1,b:2,c:3}
Object.assign() 只複製自身屬性,不可枚舉屬性和繼承屬性不會被複制。
對於嵌套的對象,Object.assign() 處理方法是替換而不是添加
var target = {a:{b:'c',d:'e'}} var source = {a:{b:'hello'}} Object.assign(target,source);//{a:{b:'hello'}}
爲對象添加屬性
爲對象添加方法
Object.assign(someClass.prototype,{
someMethod(arg1, arg2) {
...
},
anotherMethod() {
}
});
克隆對象
不過這種方法克隆,只能克隆原始對象自身的值,不能克隆他繼承的值
function clone(origin) { return Object.assign({},origin); }
合併多個對象
將多個對象合併到某個對象,若是但願合併後返回一個新對象,能夠對一個空對象合併
const merge = (...sources) => Object.assign({},...sources);
爲屬性指定默認值
六、屬性的可枚舉性
for...in:只遍歷對象自身的和繼承的可枚舉屬性
object.keys():返回對象自身的全部可枚舉屬性
Object.assign()只複製對象自身的可枚舉屬性
七、屬性的遍歷
es6一共有6種方法能夠遍歷對象的屬性
1)for ...in
2)Object.keys(obj)
返回一個數組
3)Object.getOwnPropertyNames(obj)
4) Object.getOwnPropertySymbols(obj)
5)Reflect.ownKeys(obj)
6) Reflect.enumerate(obj)
八、__proto__屬性:用來讀取或設置當前對象的prototype對象。
object.setPrototypeOf方法做用和__proto__相同
例子:
let proto = {}; let obj = {x:10}; Object.setPrototypeOf(obj, proto); proto.y = 20; proto.z = 40; obj.x //10 obj.y // 20 obj.z //40
以上代碼將prop對象設置爲obj對象的原型,因此從obj對象能夠讀取proto對象的屬性 。
object.getPrototypeOf() 用於讀取一個對象的prototype屬性
function rectangle() { } var rec = new rectangle(); Object.getPrototypeOf(rec) === rectangle.prototype // true
九、對象的擴展運算符
rest參數
rest參數複製的是這個值的引用,而不是這個值的副本,即若是一個鍵的值是複合類型的值(數組,對象,函數),那麼rest參數複製的是這個值的引用,而不是這個值的副本
let obj = {a: {b:1}}; let {...x} = obj; obj.a.b = 2; x.a.b // 2
擴展運算符
擴展運算符用於取出參數對象的全部可遍歷屬性,複製到當前對象
let aClone = {...a}; //等同於 let aClone = Object.assign({},a);
第13章 set和map數據結構
一、set
它相似於數組。可是成員的值都是惟一的,沒有重複的值,Set自己是一個構造函數,用來生成Set數據結構
var s = new Set(); [2,3,5,4,5,2,2].map(x=>s.add(x)); for(i of s){ console.log(i)//2,3,5,4 }
結果代表,set結構不會添加劇復的值
Set函數能夠接受一個數組做爲參數,用於初始化
var set = new Set([1,2,3,4,4]); [...set] //[1,2,3,4]
二、Set實例的屬性和方法
屬性:
Set.prototype.constructor; // 構造函數 Set.prototype.size; // 返回Set實例的成員總數
方法:
add(value)
delete(value)
has(value)
clear()
數組去重:
//數組去重 function dedupe(array) { return Array.from(new Set(array)); }
// 數組去重 let arr = [3,5,2,2,5,5]; let unique = [...new Set(arr)]; //[3,5,2]
使用set能夠很容易的實現並集,交集,差集
// 數組求交 util.intersection = function (a, b) { let set1 = new Set(a); let set2 = new Set(b); let Intersect = new Set([...set1].filter(x => set2.has(x))); // console.log([...Intersect]); return [...Intersect]; }
// 數組求差 util.difference = function (a, b) { let set1 = new Set(a); let set2 = new Set(b); let difference = new Set([...set1].filter(x => !set2.has(x))); // console.log([...difference]); return [...difference]; }
// 並集 let a = new Set([1,2,3]); let b = new Set([4,3,2]); let union = new Set([...a,...b]);
三、Map
map結構的目的和基本用法
js的對象本質上是鍵值對的集合(hash結構)可是隻能用字符串做爲鍵
爲了解決這個問題,es6提供了Map數據結構,它相似於對象,也是鍵值對的集合,可是鍵的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵
object 結構提供了字符串-值的對應,map結構提供了值-值的對應,是一種更完善的hash結構實現
若是須要鍵值對的數據結構,map比object更合適
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
第14章 iterator 和 for...of 循環
一、iterator 的概念
js 的原有的表示「集合」的數據結構,主要是數組和對象,es6又添加了map和set
須要一種統一的接口機制來處理全部不一樣的數據結構
遍歷器是這樣一種機制,它是一種接口,爲各類不一樣的數據結構提供統一的訪問機制。
任何數據結構只要部署了iterator 接口,就能夠完成遍歷操做。
iterator 做用有3個:
1)爲各類數據結構提供一個統一的簡便的訪問接口
2)使得數據結構的成員可以按某種次序排列
3)es6創造了一種新的遍歷命令,for...of循環,iterator 接口主要供for...of消費
Iterator 的遍歷過程是這樣的。
(1)建立一個指針對象,指向當前數據結構的起始位置。也就是說,遍歷器對象本質上,就是一個指針對象。
(2)第一次調用指針對象的next
方法,能夠將指針指向數據結構的第一個成員。
(3)第二次調用指針對象的next
方法,指針就指向數據結構的第二個成員。
(4)不斷調用指針對象的next
方法,直到它指向數據結構的結束位置
二、for...of循環