《ECMAScript6 入門》阮一峰 讀書筆記
- let和const
- let
- 聲明的變量僅在
塊級作用域內有效
,var聲明的變量在全局范圍內有效 - let
不存在變量提升
,所以變量一定要先聲明后使用,換言之,var是有變量提升的(也就是可以先使用后聲明,聲明在編譯的時候會被提升到最開頭) - 暫時性死區,也就是使用let命令聲明變量之前,該變量都是不可用的,也就是第二條。暫時性死區的本質就是,只要一進入當前作用域,所要使用的變量就已經存在了,但是不可獲取,只有等到聲明變量的那一行代碼出現,才可以獲取和使用該變量。
-
不允許重復聲明
,也就是在同一個作用域內不能重復聲明同一個變量。
- 聲明的變量僅在
- let
- 塊級作用域
- 不合理場景一
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = "hello world";
}
}
f(); // undefined,因為變量提升內層的tmp變量覆蓋了外層的tmp變量????
??有疑惑,并不理解。。。
2. 不合理場景二,變量泄漏為全局變量(比如for循環里面的計數變量,如果用var來聲明,其實在全局也是可以訪問到的)
3. 塊級作用域與函數聲明
- es5規定函數只能在頂層作用域和函數作用域之中聲明,不能在塊級作用域中聲明,比如在if語句里面就不能聲明一個變量,但實際上,瀏覽器是支持的
- es6明確允許在塊級作用域之中聲明函數。比較特別的是,塊級作用域之中,函數聲明語句的行為類似于`let`,在`塊級作用域之外是不可以引用的`
```
eg:
function f(){console.log("I am outside");}
(function (){
if(false){
function f(){console.log("I am inside");}
}
f()
}())
*===================================================*
es5中實際上是如此運行的:
function f(){console.log("I am outside");}
(function (){
function f(){console.log("I am inside");}//變量提升
if(false){}
f()
}())
*===================================================*
es6中實際運行代碼如下:
function f(){console.log("I am outside");}
(function(){
f()//在塊級內聲明的那個inside的f函數對作用域之外沒有影響,所以相當于不存在,
調用的是外面的函數,因為作用域查找都是向外查找噠!
}())
```
- ??為了兼容的問題,es6允許瀏覽器可以不遵守上面的規定,也就是可以有自己的行為方式,具體表現為(也就是實際上定義函數的時候遵守的規則,只針對支持es6的瀏覽器有效):
- 允許在塊級作用域內聲明函數
- 函數聲明類似于var,即會提升到全局作用域或函數作用域的頭部
- 同時,函數聲明還會提升到所在的塊級作用域的頭部
- 在塊級作用域內聲明函數的時候,塊級作用域一定要加大括號,比如在if里面聲明一個函數,if后面一定要有大括號
const聲明一個只讀的常量,聲明之后,值不能改變。所以,
聲明的時候就要賦值
,其他的和let一樣。有一個注意的點是,比如const了一個數組,這個數組本身是不能給賦值的,但是這個數組是可以push一個變量的!
-
頂層對象的屬性
- 全局變量在es5中是頂層對象的屬性,es6規定,var和function命令聲明的全局變量依然是頂層對象的屬性,但是let、const、class命令聲明的全局變量不屬于頂層對象的屬性。
-
頂層對象
混亂的頂層對象
- 瀏覽器里面,頂層對象是window,但Node和Web Worker沒有
window
- 瀏覽器和Web Worker里面,
self
指向頂層對象,但是Node 沒有self
- Node里面,頂層對象是
global
,但其他環境都不支持
為了在不同的環境中都能去到頂層對象,一般使用
this
變量,但是有局限性
- 全局環境中,this
會返回頂層對象,但是,Node模塊和ES模塊中,this
返回的是當前模塊
- 函數里面的this
,如果函數不是作為對象的方法運行,而是單純作為函數運行,this
會指向頂層對象,但是,嚴格模式下,this
會返回undefined
。
- 不管是嚴格模式,還是普通模式,new Function('return this')()
,總是會返回全局對象,但是,如果瀏覽器用了CSP(Content Security Policy,內容安全政策),那么eval
、new Function
這些方法都可能無法使用。
綜上所述,兩種解決辦法: 方法一: (typeof window !== 'undefined' ? window: (typeof process === 'object' && typeof require === 'function' && typeof global === 'object')? global : this ) 方法二: var getGlobal = function(){ if(typeof self !== 'undefined'){return self;} if(typeof window !== 'undefined'){return window;} if(typeof global !== 'undefined'){return global;} throw new Error('unalble to locate global object'); }
- 瀏覽器里面,頂層對象是window,但Node和Web Worker沒有
- 變量的解構賦值
es6允許按照一定模式,從數組和對象中提取值,對變量進行復賦值,成為解構賦值。
- 數組的解構賦值
- “模式匹配”,也就是只要等號兩邊的模式相同,左邊的變量就會被賦予對應的值。
let [foo, , baz] = [1,2,[d]];//foo就是1,baz就是[d]這個數組 let [head, ...tail] = [1,2,3,4];//head就是1,tail就是[2,3,4] let [x,y,...z] = ['a'];//x就是“ a”,y是undefined(解構不成功的話,變量的值就等于undefined),z是[],為啥z是[](????為啥??),因為"..."是將右邊剩余的值用數組的形式賦給左邊的值,所以z就得到了一個空的數組 let [a,[b],d] = [1,[2,3],4];//a是1,b是2(完全對應過去,如果b不加外面的中括號,那b就是[2,3]),d是4
- 等號的右邊如果不是可遍歷的結構,會報錯的
- 默認值,允許指定默認值,數組成員嚴格等于
undefined
才會使默認值生效
var [foo = true] = [];//foo是true
- 對象的解構賦值
- 變量必須與對象的屬性同名,才能取到正確的值,如果沒有對應的同名屬性,取不到值的話,值為undefined
誰后面有“:”,誰就是模式,并不是變量var {bar, foo} = {foo:'aaa',bar:'bbb'}//foo的值是'aaa',bar的值是'bbb' //注意真正被賦值的是誰 var {foo:baz} = {foo:"aaa",bar:"bbb"}//baz的值是"aaa"而不是foo的值是“aaa”,foo是個“模式”
- 這種寫法,變量的聲明和賦值是一體的,像下面,如果用let或const是不行的,但是用var是可以的。
let foo; let {foo} = {foo:1}//這樣會報錯滴,因為foo在上面已經聲明過了,所以不能再次聲明 //如果非常想用let并且分開的話,可以用下面的方式 let foo; ({foo} = {foo:1})//這樣不會報錯,外面必須用()包起來,因為解析器會將起首的大括號理解成一個代碼塊而不是賦值語句。
- 默認值,默認值生效的條件是,對象的屬性值嚴格等于
undefined
,同樣,如果解構失敗(也就是對應右邊沒有相同變量名的屬性),那么變量的值等于undefined
- 實際應用場景:可以很方便的把一些對象的屬性值賦值給其他的變量,不用一個一個的寫
var { log,sin,cos } = Math
- 字符串的解構賦值
var [a,b,c] = 'hello'; //a==>h,b==>e,c==>l var {length:len} = 'hello'; //len的值是5,有點懵???(類似數組的對象都有一個length屬性,所以就5了???)
- 數值和布爾值的解構賦值
解構賦值時,如果等號右邊是數值或布爾值,則會先轉為對象,(undefined和null無法轉為對象,所以對他們解構賦值會出錯)
- 函數形式參數的解構賦值
- 變量的解構賦值的用途
- 交換變量的值
[x,y] = [y,x]//數組的解構是嚴格按照順序的
- 從函數返回多個值:函數一次只能返回一個值,如果要返回多個值,只能將她們放在數組或對象里返回,有了解構賦值,可以很方便的取出這些值
function example(){ return [1,2,3] } var [a,b,c] = example()//這樣就賦值了,多方便??
- 函數參數的定義
function f([x,y,z]){....} f([1,2,3])
- 提取JSON數據
var jsonData = { id:42, status:"ok", data:[22,3] } let { id, status, data:number } = jsonData;
- 遍歷Map結構
- 輸入模塊的指定方法
- 字符串的擴展(這節好深奧,先放下??
- 字符的Unicode表示法,
用\uxxxx形式來表示一個字符,其中xxxx表示字符的碼點
- 字符的Unicode表示法,
- 正則的擴展
- 數值的擴展
-
數組的拓展
-
Array.from()
,用于將兩類對象轉為真正的數組:類似數組的對象和可遍歷的對象。
let arrayLike = { '0':'a', '1':'b', '2':'c', length:3 };//類似數組的對象 let arr = Array.from(arrayLike);//['a','b','c']
- 實際應用中,常見的類似數組的對象是DOM操作返回的NodeList集合,以及函數內部的arguments對象。(所謂類似數組的對象本質特征只有一點,即必須有length屬性,任何有length屬性的對象,都可以通過Array.from方法轉為數組)
-
Array.of()
,用于將一組值轉換為數組,沒有參數的時候返回一個空數組
Array.of(1,2,4)//【1,2,4】
-
copyWithin()
,在當前數組內部,將指定位置的成員復制到其他位置(會覆蓋原有成員),然后返回當前數組,也就是會修改當前數組。
Array.prototype.copyWith(target,start,end)//接受三個參數,其中target是必需的,target代表從該位置開始替換數據,start表示從該位置開始讀取數據,默認為0,負數則表示倒數,end表示到該位置前停止讀取數據,默認等于數組長度,負數則表示倒數 [1,2,3,4].copyWithin(0,3)//[4,4,4,4],從0開始用3位置上(也就是4這個數字到末尾的0來替換0
- 數組實例的find()和findIndex()
find方法用于找出第一個符合條件的數組成員,參數是一個回調函數,所有數組成員依次執行該回調函數,直到找出第一個返回值為true的成員,然后返回該成員。如果沒有符合條件的成員,則返回undefined。回調函數可以接受三個參數,
依次為當前的值,當前的位置,原數組
。[1,2,-5].find(n => n < 0)//返回第一個小于0的成員
findIndex方法返回第一個符合條件的數組成員的位置,如果都不符合條件,則返回-1,回調函數參數和find方法一致。
find和findIndex方法都可以接受除回調函數以外的第二個參數,用來綁定回調函數的this對象。
- 數組實例的fill()
fill方法使用給定值,填充一個數組。如果數組中已有元素,則會全部被抹去。參數和copyWithin函數的參數是一致的。
['a','b','c'].fill(7)//返回一個[7,7,7]
- 數組實例的entries(),keys()和values()----遍歷數組的函數
------對鍵名的遍歷 for(let index of ['a','b'].keys()){ console.log(index); }//結果如下 //0 //1 ------對鍵值的遍歷 for(let elem of ['a','b'].values()){ console.log(elem); }//結果如下 //'a' //'b' ------對鍵值對的遍歷 for(let [index,elem] of ['a','b'].entries()){ console.log(index,elem); }//結果如下 //0 "a" //1 "b"
- 數組實例的includes()
Array.prototype.includes方法返回一個布爾值,表示某個數組是否包含給定的值,與字符串的includes方法類似。
[1,2,3].includes(2)//包含2,所以返回true
Map結構的has方法,是用來查找鍵名的 Set結構的has方法,是用來查找值的
- 數組的空位
數組的空位是指,數組的某一個位置沒有任何值,es6明確的將空位轉為undefined。以上數組的擴展方法都會把空位考慮進去,而不是忽略。
-
-
函數的擴展
- 函數參數的默認值
function log(x, y = 'world'){ ...}
- 與解構賦值默認值結合使用(沒看懂??
- 參數默認值的位置
非尾部的參數如果設置了默認值,那么這個參數是不能夠省略的!
- 函數的length屬性
指定了默認值以后,函數的length屬性,將返回沒有指定默認值的參數個數,也就是說,指定了默認值以后,length屬性將失真。
失真也就是不算它
。(function (a,b=2){}).length //1
-
作用域---大爺
- 如果參數默認值是一個變量,則該變量所處的作用域,與其他變量的作用域規則是一樣的,即先是當前函數的作用域,然后才是全局作用域。
情況一: var x = 1; function f(x, y = x){ 略 } //比如這里,y的默認值就是一個變量x,那么x的值要優先取傳進來的x的值,如果沒有, 才是全局變量中的x。
情況二: let x = 1; function f(y=x){ let x = 2; console.log(y); } f()//輸出的是1,因為給y賦值的時候,x尚未在函數內部生成,故取全局變量中的x給y賦值, 如果此時全局變量中的x不存在,就會報錯了。
情況三: let foo = "outer"; function bar(func = x=> foo){ let foo = "inner"; console.log(func()); } bar() //當函數的參數是一個函數的時候,該函數的作用域是其聲明時所在的作用域。 ????bar方法的參數func是一個匿名函數,具體的意義是傳進去x,返回值為foo, 也就是func()的值,匿名函數聲明的時候(也就是給func賦值的時候),bar函數的作 用域還沒有形成,所以匿名函數里面的foo指向外層作用域來的foo,輸出的是outer ????
2.rest參數
rest參數形式為"...變量名",用于獲取函數的多余參數,這樣就不需要使用arguments對象了,rest參數搭配的變量是一個數組,該變量將多余的參數放入數組中。??需要注意的是rest參數之后不能再有其他參數,也就是這個參數只能放在最后面。
function add(...values){ let sum = 0; for(var val of values){ sum+=val; } return sum; } add(2,4,5)//神似python里的參數??
3.擴展運算符
擴展運算符(spread)是三個點
...
,rest參數的逆運算,將一個數組轉為用逗號分隔的參數序列。注意,擴展運算符針對的是數組數組數組??也就是把一個數組相當于給拆開了。console.log(...[1,2,3]) //1 2 3
function push(array, ...items){ array.push(...items); } function add(x, y){ return x + y; } var numbers = [4,3]; add(...numbers)//7
替代數組的apply方法(??總結一下apply方法,因為得知道apply究竟是干嘛才知道怎么個替代法??10.26)
-
擴展運算符的應用:
- 合并數組
var arr1 = ['a','b']; var arr2 = ['c','d']; var arr = [...arr1,...arr2]
- 與解構賦值結合
這里和前面的解構賦值很容易混啊?? const [first,...rest]=[1,2,3,4]; first===》1 rest====》2,3,4 //有個易錯點,和前面很像,就是i 這個...變量只能放在最后??
- 函數的返回值,返回多個值的時候
- 字符串,可以將字符串轉換為真正的數組
[...'hello'] //['h','e','l','l','o']
- 實現了Iterator接口的對象,任何Iterator接口的對象,都可以用擴展運算符轉為真正的數組。
let arrayLike = { '0':'a', '1':'b', '2':'c', length:3 }; let arr = [...arrayLike]//TypeError //arrayLike是一個類似數組的對象,但是沒有部署Iterator接口,擴展運算符就會報錯, 這時,可以改為使用Array.from方法將arrayLike轉為真正的數組。
- Map結構和Set結構,Generator函數
擴展運算符內部調用的是數據結構的Iterator接口,因此只要具有Iterator接口的對象,都可以使用擴展運算符。
4.嚴格模式
use strict
es6規定,只要函數的形參使用了默認值、解構賦值或者擴展運算符,那么函數內部就不能顯示的設定為嚴格模式。
5.name屬性,函數的name屬性,返回該函數的函數名
6.箭頭函數
=>
- 如果箭頭函數不需要參數或者需要多個參數,就使用一個圓括號代表參數部分。
- 如果箭頭函數的代碼塊部分多于一條語句,就要使用大括號將他們括起來,并且使用return語句返回
- 如果要返回一個對象,外面要用小括號包起來,因為大括號被解釋為代碼塊,所以必須在對象外面加上括號
-
函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象
,在箭頭函數中,this是固定的。因為箭頭函數沒有自己的this,他們的this其實都是外層對象(也就是函數)的this。所以bind(),call(),apply()這些方法無法改變this的指向。 - 不可以當做構造函數,也就是不能用new操作符
- 不可以使用arguments對象,該對象在函數體內不存在,如果要用,可以用rest參數代替
- 不可以使用yield命令,因此箭頭函數不能用做generator函數
7.綁定this
::
es7的一個題案,如果用的話需要babel轉碼器雙冒號左邊是一個對象,右邊是一個函數,該運算符會自動將左邊的對象,作為上下文環境(即this對象)綁定到右邊的函數上面,如果雙冒號左邊為空,右邊是一個對象的方法,則等于將該方法綁定在該對象上面。
8.尾調用優化??看不懂??10.27
尾調用是函數式編程的一個重要概念,指的是某個函數的最后一步是調用另一個函數
-
對象的擴展
- 屬性的簡潔表示法
情況一: var foo = 'a'; var baz = {foo}; //以往寫對象的時候要屬性名、屬性值成對出現,es6允許在對象中,只寫屬性名, 不寫屬性值,這時,屬性值等于屬性名所代表的變量。 情況二: var o = { method(){ return "hello";//這不就是之前寫react的用法嘛?? } }; 等同于: var o = { method:function(){ return "hello"; } }; 綜合的例子: var birth = '2000/01/01'; var Person = { name: '張三', //等同于birth: birth birth,//react返回對象的時候就是這樣寫的!!!?? // 等同于hello: function ()... hello() { console.log('我的名字是', this.name); } };
- 屬性名表達式??
- Object.js()
比較兩個值是否相等,只有兩個運算符:相等運算符
==
和嚴格相等運算符===
,前者會自動轉換數據類型,后者的NaN不等于自身,以及+0
等于-0
。Object.is用來比較兩個值是否嚴格相等,彌補了===
的缺點。- Object.assign()
Object.assign()
用于對象的合并,將源對象的所有可枚舉屬性,復制到目標對象。Object.assign(target,source1,source2...) //第一個參數是目標對象,也就是最終的,后面都是源對象。如果只有一個參數那就直接返回 //????????如果目標對象與源對象有同名屬性,或者多個源對象有同名屬性,則后面的屬性會 覆蓋前面的屬性 ??undefined和null不能放在目標對象的位置(放,報錯,不放也就是放在別的位置會被跳過) ??字符串可以作為源對象,復制到目標對象里會是數組的形式,布爾值和數值都不能作為源對象, 雖然不報錯,但是會跳過
??注意點 1.Object.assign是淺拷貝,如果源對象某個屬性的值是對象,如果源對象某個屬性的值是對象, 那么目標對象拷貝得到的是這個對象的引用。 2.Object.assign也可以處理數組,但是也是將數組視為對象。
- Object.assign常見用途 - 為對象添加屬性 - 為對象添加方法,目標對象就是xxx.prototype,然后源對象就是用大括號包裹的函數 - 克隆對象 - 合并多個對象 - 為屬性指定默認值(可以將屬性的默認值為一個對象,用戶輸入的為另一個對象,兩個對象作為源對象復制給目標對象,因為用戶輸入的放在后面所以會覆蓋掉默認值對象的值,so~)
- 屬性的可枚舉性
對象的每個屬性都有一個描述對象(Descriptor),用來控制該屬性的行為。
Object.getOwnPropertyDescriptor
方法可以獲取該屬性的描述對象let obj = {foo:123}; Object.getOwnPropertyDescriptor(obj,'foo') //{ // value:123, // writable:true, // enumerable:true,是否可以枚舉 // configurable:true //}
- 屬性的遍歷
-
for...in
,遍歷對象自身的和繼承的可枚舉屬性(不包含Symbol屬性) -
Object.keys(obj)
,返回一個數組,包括對象自身的(不含繼承的)所有可枚舉屬性(不含Symbol屬性)。 -
Object.getOwnPropertyNames(obj)
,返回一個數組,包含對象自身的所有屬性(不含Symbol屬性,但是包括不可枚舉屬性)。 -
Object.getOwnPropertySymbols(obj)
,返回一個數組,包含對象自身的所有Symbol屬性 -
Reflect.ownKeys(obj)
,返回一個數組,包含對象自身的所有屬性,不管是屬性名是Symbol或字符串,也不管是否可以枚舉。
-