es5只有全局做用域和函數做用域,沒有塊級做用域。
下面看一個例子:es6
for(var i = 0;i < 10;i++){ //... } alert(i) //10;
對於有塊級做用域的語言來講,for語句初始化變量的表達式所定義的的變量,只會存在於循環的環境中。然而對於JavaScript來講,由for語句建立的變量i,即便在for語句循環結束後,也依舊存在於外部的執行環境中,這就是所說的變量提高。
es6新增了let和const命令,用來聲明變量和常量,let命令只在所在的代碼塊內有效。
例子:面試
{ let a = 10; var b = 1; } console.log(a) //ReferenceError:a is not defined. console.log(b) //1
在上面的代碼塊之中,分別用let和var聲明瞭兩個變量。在代碼塊以外調用這兩個變量,
發現let聲明的變量錯誤,由於let的做用域是在它所在的當前代碼塊,不會被提高到當前函數的最頂部。json
常見的面試題:數組
var a = []; for (var i = 0;i < 10;i++){ a[i] = function(){ console.log(i); } } a[6](); //10
上面的代碼中,變量i是var聲明的,在全局範圍內都是有效的,因此全局只有一個變量i,每一次循環,變量i的值都會發生變化,被賦給數組a的函數內部的console.log(i),裏面的i指向的就是全局的i。也就是說,全部數組a的成員裏面的i,指向的都是同一個i,致使運行時的輸出的時最後一輪的i的值,也就是10.
如何使a[6]()輸出的是6哪?數據結構
//es5閉包解決循環綁定問題 var a = []; for (var i = 0;i < 10;i++){ a[i] = (function(value){ return function(){ console.log(value) } })(i) } a[6](); //6 //es6 //使用let,聲明的變量僅在塊級做用域內有效 var a = []; for (let i = 0;i < 10;i++){ a[i] = function(){ console.log(i) } } a[6](); //6 上面代碼中,變量i是let聲明的,當前的i只在本輪循環有效,因此每一次循環的i其實都是一個新的變量,因此最後輸出的是6。 由於JavaScript引擎內部會記住上一輪循環的值,初始化本輪的變量i時,就在上一輪循環的基礎上運算。 另外,for循環的一個特別的地方,就是設置循環變量的部分是一個父做用域,而循環體內部是一個單獨的子做用域。 for(let i =0;i < 3;i++){ let i = 'abc'; console.log(i) } //abc //abc //abc 上面代碼輸出3次abc。這說明函數內部的變量i與循環變量i不在同一個做用域內,有各自單獨的做用域。
const聲明一個只讀的常量。一旦聲明常量的值就不能改變.
const一旦聲明變量,就必須當即初始化,不能留到之後賦值。閉包
const PI = 3.1415; PI; //3.1415 PI = 3; // TypeError: Assignment to constant variable const foo;//SyntaxError: Missing initializer in const declaration
什麼是解構?app
解構不是構造一個新的對象或者數組,而是逐個拆分現有的對象或數組、字符串,來提取須要的數據。 es6中被解構的數據項位於賦值運算符=的右側,能夠是數組、對象、字符串。
數組解構:使用一個數組做爲一個數據項,根據數組模式(只要等號兩邊的模式相同,左邊的變量就會賦予對應的值)從這個數組裏面提取提取一個或者多個變量賦值。
基本用法:函數
//把數組中全部的數值賦給一個個單獨的變量 let [a,b,c] = [1,2,3]; a //1 b //2 c //3 //第一個不提取,其他都提取 let [,b,c] = ['li','yong','good']; b //'yong' c //'good' //中間第二個不提取,其他都提取 let [a,,c] = ['li','yong','good']; a //'li' c //'good' //最後一個不提取,其他都提取(不徹底解構) let [a,b] = ['li','yong','good'] a //'li' b //'good' //第一個提取,其他的放到一個數組裏,要用到rest操做符 //rest操做符:用來獲取多餘的參數,將多餘的參數放入數組中。 let [head,...tail] = [1,2,3,4]; head //1 tail //[2,3,4]
默認值:
解構賦值容許指定默認值:ui
let [foo = true] = []; foo //true let [x,y = 'yong'] = ['li']; x //'li' y //'yong' let [x,y = 'yong'] = ['li',undefined]; x //'li' y //'yong' let [x = 'li'] = [null] x //null 若是一個數組成員是null,默認值不會生效。
以上代碼中,undefined 和 null的區別:
undefined使用默認值;
null是有值,但值是空。this
對象的解構與數組的解構不一樣,數組的元素是按次序排列的,變量的取值由它的位置決定;對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。
基本用法:
//變量名和屬性同名 let {a,b} = {a:'li',b:'yong'} a //'li' b //'yong' //變量名與屬性名不一致 let {a:xxx} = {a:'li',b:'yong'} xxx //'li' 對象的解構賦值的內部機制,是先找到同名屬性,而後再賦給對應的變量。
默認值:
let {x,y = 5} = {x:1}; x //1 y //5 let {x = 3} = {x: undefined}; x //3 let {x = 3} = {x:null}; x //null 若是將一個已經聲明的變量用於解構賦值,必須當心 //錯誤的寫法 let x; {x} = {x:1};// SyntaxError: Unexpected token 上面代碼的寫法會報錯,由於JavaScript引擎會將{x}理解成一個代碼塊,從而發生語法錯誤。 //正確的寫法 let x; ({x} = {x:1}); x //1
字符串能夠解構賦值。由於字符串被轉換成一個相似數組的對象。
const [a,b] = 'yes'; a //'y' b //'e' c //'s'
function move({x = 0,y = 0} = {}){ return [x, y]; } move({x: 3, y:8}); // [3, 8] move({x: 3}); // [3, 0] move({}) // [0, 0] move() // [0, 0] 上面代碼中,函數move的參數是一個對象,經過對這個對象進行解構,獲得變量x 和 y 的值。若是解構失敗, x 和 y 等於認值。 注意下面的寫法: function move({x, y} = {x: 0, y: 0}){ return [x, y]; } move({x: 3,y: 8}) //[3,8] move({x:3}) //[3, undefined] move({}) //[undefined,undefined] move() //[0, 0] 上面的代碼是爲函數move的參數指定默認值,而不是爲變量x 和 y 指定默認值,因此獲得與前一種寫法不一樣的結果。
寫了那麼多,來總結一下在工做中的經常使用的例子:
let x = 1; let y = 2; [x, y] = [y, x]; x //2 y //1 上面的代碼交換變量x 和 y 的值。
函數返回多個值,只能將它們放在數組或對象裏返回。 有了解構賦值,取出這些值就很是方便了。 //返回一個數組 function example(){ return [1,2,3]; } let [a, b, c] = example(); //返回一個對象 function example(0{ return { foo: 1, bar: 2 } } let { foo, bar } = example();
解構賦值能夠將一組參數與變量名對應起來 //參數是一組有次序的值 function f([x,y,z]){....} f([1,2,3]) //參數是一組無次序的值 function f({x, y, z}){...} f({z: 3, y: 2, x: 1})
let jsonData = { id : 42, status: 'ok', data : [77,909] }; let { id, status, data:number } = jsonData; id // 42 status // 'ok' number // [77,909]
加載模塊時,指定輸入哪些方法。 const { SourceMapConsumer, SourceNode } = require("source-map");
es6引入模板字符串(``),進行字符串的拼接,對於字符串中嵌入變量,使用${}。
var str = '這是字符串變量'; $('#body').append(`liyong,${str},good`); es5字符串查找: 只有indexOf()方法,能夠用來肯定一個字符串是否包含裏一個字符串。返回子字符串的位置(若是沒有找到該子字符串,則返回-1) var d = 'this is a line'; d.indexOf('is') //2 es6提供了一種查找子字符串的方法:includes() -includes(): 返回布爾值,表示是否找到了參數字符串。 var d = 'this is a line'; d.includes('is') //true
//將一個數組轉化爲用逗號分割的參數序列 console.log([1,...[2,3,4],5]) //[1,2,3,4,5]
數組是複合的數據類型,直接複製的話,只是複製的指向底層數據結構的指針,而不是克隆了一個新的數組。
es5中數組的複製; const a1 = [1,2]; const a2 = a1; a2[0] = 2; a1; //[2,2] 上面代碼,a2不是a1的克隆,而是指向同一個數據的另外一個指針。修改a2,會直接致使a1的變化。 另一種es5的方法: const a1 = [1,2]; const a2 = a1.concat(); a2[0] = 2; a1; //[1,2] 上面代碼中,a1會返回原數組的克隆,再修改a2就不會對a1產生影響。 es6擴展運算符的複製數組的寫法: const a1 = [1,2]; const a2 = [...a1]; a2[0] = 2; a1; //[1,2]
數組合並的寫法:
//es5 const b1 = [3,4,5] [1,2].concat(b1) //[1,2,3,4,5] //es6 [1,2, ...b1] //[1,2,3,4,5]
將一組相似數組的對象(一組Dom節點)轉爲真正的數組
let arrayLike = { '0':'li', '1':'yong', length:2 } //es5的寫法 let arr = [].slice.call(arrayLike); //['li','yong'] let arr = Array.prototype.slice.call(arrayLike);//['li','yong'] //es6的寫法 let arr = Array.from(arrayLike); //['li','yong']
用於將一組值,轉換爲數組。
Array.of(3,11,8) //[3,11,8]
數組實例的find方法,用於找出第一個符合條件的數組成員。它的參數是一個回調函數,全部的數組成員依次執行該回調函數,直到找出第一個返回值爲true的成員,而後返回該成員。若是沒有符合條件的成員,則返回undefined.
[1,4,-5,10].find((n) => n < 0) //-5 以上代碼找出數組中第一個小於0的成員
與字符串的includes方法相似,Array.prototype.includes方法返回一個布爾值。
[1,2,3].includes(2); //true [1,2,3].includes(4); //false
keys()是對鍵名的遍歷、values()是對鍵值的遍歷、entries()是對鍵值對的遍歷
let arr = ['li','yong']; for(let [index,value] of arr.entries()){ console.log(`索引 ${index} : 鍵值 ${value}`) } //索引 0 : 鍵值 li //索引 1 : 鍵值 yong
let arr = ['li','yong','good','']; arr.forEach((value,index)=>console.log(`索引${index} : 鍵值${value}`)) //索引0 : 鍵值li // 索引1 : 鍵值yong // 索引2 : 鍵值good
let arr = ['li','yong','good']; arr.filter(x=>{if(x == 'li'){return console.log(`篩選的是:${x}`)}})
let arr = ['li','yong','good']; console.log(arr.join('|')) //li|yong|good
let arr = ['li','yong','good']; console.log(arr.toString()) //li,yong,good
const name = 'li'; const age = 00; const o = {name, age,method(){ console.log(this.name) }} o.method() //'li'
es5 比較兩個值是否相等,相等運算符(==)和嚴格相等運算符(===)。前者的不足是,會自動轉換數據類型,後者的NaN不等於自身,以及+0等於-0。
es6 提出"Same-value-equality"(同值相等)。Object.is就是比較兩個值是否嚴格相等。
Object.is(true,1) //false Object.is(true,true) //true Object.is(+0,-0) //false Object.is(NaN,NaN) //true
Object.assign方法用於對象的 合併,將源對象(source)的全部的可枚舉屬性,複製到目標對象(target)。
const target = {a: 1}; const source1 = {b: 2}; const source2 = {c: 3}; Object.assign(target, source1, source2) target //{a:1, b:2, c:3}
爲對象添加方法
直接將兩個函數放在大括號中,再使用assign方法添加到目標對象中。
const bbbb = {name:'li'};
Object.assign(bbbb,{
getName(){ return this.name; }, getAge(){ return this.age; }
})
返回一個數組
Object.entries的用途是遍歷對象的屬性。
const obj = {name:'li',age:00 }; for( let [key,value] of Object.entries(obj) ){ console.log(`屬性${JSON.stringify(key)}:屬性值${value}`) } //屬性"name":屬性值li //屬性"age":屬性值00