1、let和const面試
在JavaScript中我們之前主要用關鍵var來定義變量,ES6以後,新增了定義變量的兩個關鍵字,分別是let和const。 對於變量來講,在ES5中var定義的變量會提高到做用域中全部的函數與語句前面,而ES6中let定義的變量則不會,let聲明的變量會在其相應的代碼塊中創建一個暫時性死區,直至變量被聲明。 let和const都可以聲明塊級做用域,用法和var是相似的,let的特色是不會變量提高,而是被鎖在當前塊中。編程
一個很是簡單的例子:數組
function test(){
if(true){
console.log(a)//TDZ,俗稱臨時死區,用來描述變量不提高的現象
let a =1
}
}
test()// a is not defined
function test(){
if(true){
let a =1
}
console.log(a)
}
test()// a is not defined
惟一正確的使用方法:先聲明,再訪問。數據結構
function test(){
if(true){
let a =1
console.log(a)
}
}
test()// 1
const 聲明常量,一旦聲明,不可更改,並且常量必須初始化賦值。 const雖然是常量,不容許修改默認賦值,但若是定義的是對象Object,那麼能夠修改對象內部的屬性值。閉包
const type ={
a:1
}
type.a =2//沒有直接修改type的值,而是修改type.a的屬性值,這是容許的。
console.log(type)// {a: 2}
const和let的異同點相同點:const和let都是在當前塊內有效,執行到塊外會被銷燬,也不存在變量提高(TDZ),不能重複聲明。 不一樣點:const不能再賦值,let聲明的變量能夠重複賦值。 const實際上保證的,並非變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。對於簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,所以等同於常量。但對於複合類型的數據(主要是對象和數組),變量指向的內存地址,保存的只是一個指向實際數據的指針,const只能保證這個指針是固定的(即老是指向另外一個固定的地址),至於它指向的數據結構是否是可變的,就徹底不能控制了。所以,將一個對象聲明爲常量必須很是當心。app
塊級做用域的使用場景除了上面提到的經常使用聲明方式,咱們還能夠在循環中使用,最出名的一道面試題:循環中定時器閉包的考題 在for循環中使用var聲明的循環變量,會跳出循環體污染當前的函數。異步
for(var i =0; i <5; i++){
setTimeout(()=>{
console.log(i)//5, 5, 5, 5, 5
},0)
}
console.log(i)//5 i跳出循環體污染外部函數
//將var改爲let以後
for(let i =0; i <5; i++){
setTimeout(()=>{
console.log(i)// 0,1,2,3,4
},0)
}
console.log(i)//i is not defined i沒法污染外部函數
在實際開發中,咱們選擇使用var、let仍是const,取決於咱們的變量是否是須要更新,一般咱們但願變量保證不被惡意修改,而使用大量的const。使用const聲明,聲明一個對象的時候,也推薦使用const,當你須要修改聲明的變量值時,使用let,var能用的場景均可以使用let替代。異步編程
symbolES6 之前,咱們知道5種基本數據類型分別是Undefined,Null,Boolean,Number以及String,而後加上一種引用類型Object構成了JavaScript中全部的數據類型,可是ES6出來以後,新增了一種數據類型,名叫symbol,像它的名字表露的同樣,意味着獨一無二,意思是每一個 Symbol類型都是獨一無二的,不與其它 Symbol 重複。 能夠經過調用 Symbol() 方法將建立一個新的 Symbol 類型的值,這個值獨一無二,不與任何值相等。函數
var mySymbol=Symbol();
console.log(typeof mySymbol)//"symbol"
2、字符串性能
ES6字符串新增的方法
UTF-16碼位:ES6強制使用UTF-16字符串編碼。關於UTF-16的解釋請自行百度瞭解。
codePointAt():該方法支持UTF-16,接受編碼單元的位置而非字符串位置做爲參數,返回與字符串中給定位置對應的碼位,即一個整數值。
String.fromCodePoiont():做用與codePointAt相反,檢索字符串中某個字符的碼位,也能夠根據指定的碼位生成一個字符。
normalize():提供Unicode的標準形式,接受一個可選的字符串參數,指明應用某種Unicode標準形式。
在ES6中,新增了3個新方法。每一個方法都接收2個參數,須要檢測的子字符串,以及開始匹配的索引位置。
模板字符串字符串是JavaScript中基本類型之一,應該算是除了對象以外是使用最爲頻繁的類型吧,字符串中包含了例如substr,replace,indexOf,slice等等諸多方法,ES6引入了模板字符串的特性,用反引號來表示,能夠表示多行字符串以及作到文本插值(利用模板佔位符)。
// 之前的多行字符串咱們這麼寫:
console.log("hello world 1n\
hello cala");
// "hello world
// hello cala"
//有了模板字符串以後
console.log(`hello world
string text line 2`);
// "hello world
// hello cala"
能夠用${}來表示模板佔位符,能夠將你已經定義好的變量傳進括弧中,例如:
var name="cala";
var age=22;
console.log(hello,I'am ${name},my age is ${age}
)
//hello,I'am cala,my age is 22
includes(str, index):若是在字符串中檢測到指定文本,返回true,不然false。
let t ='abcdefg'
if(t.includes('cde')){
console.log(2)
}
//true
startsWith(str, index):若是在字符串起始部分檢測到指定文本,返回true,不然返回false。
let t ='abcdefg'
if(t.startsWith('ab')){
console.log(2)
}
//true
endsWith(str, index):若是在字符串的結束部分檢測到指定文本,返回true,不然返回false。
let t ='abcdefg'
if(t.endsWith('fg')){
console.log(2)
}
//true
若是你只是須要匹配字符串中是否包含某子字符串,那麼推薦使用新增的方法,若是須要找到匹配字符串的位置,使用indexOf()。
3、函數
函數的默認參數 在ES5中,咱們給函數傳參數,而後在函數體內設置默認值,以下面這種方式。
function a(num, callback){
num = num ||6
callback = callback ||function(data){console.log('ES5: ', data)}
callback(num * num)
}
a()//ES5: 36,不傳參輸出默認值
//你還能夠這樣使用callback
a(10,function(data){
console.log(data *10)// 1000, 傳參輸出新數值
})
在ES6中,咱們使用新的默認值寫法
function a(num =6, callback =function(data){console.log('ES6: ', data)}){
callback(num * num)
}
a()//ES6: 36, 不傳參輸出默認值
a(10,function(data){
console.log(data *10)// 1000,傳參輸出新數值
})
4、箭頭函數(=>)
(箭頭函數比較重要,如今簡單提一下,遲一點有空專門寫一篇箭頭函數的文章。)
const arr =[5,10]
const s = arr.reduce((sum, item)=> sum + item)
console.log(s)// 15
箭頭函數中this的使用跟普通函數也不同,在JavaScript的普通函數中,都會有一個本身的this值,主要分爲: 普通函數: 一、函數做爲全局函數被調用時,this指向全局對象 二、函數做爲對象中的方法被調用時,this指向該對象 三、函數做爲構造函數的時候,this指向構造函數new出來的新對象 四、還能夠經過call,apply,bind改變this的指向 箭頭函數:一、箭頭函數沒有this,函數內部的this來自於父級最近的非箭頭函數,而且不能改變this的指向。 二、箭頭函數沒有super 三、箭頭函數沒有arguments 四、箭頭函數沒有new.target綁定。 五、不能使用new 六、沒有原型 七、不支持重複的命名參數。
箭頭函數的簡單理解
一、箭頭函數的左邊表示輸入的參數,右邊表示輸出的結果。
const s = a => a
console.log(s(2))// 2
二、在箭頭函數中,this屬於詞法做用域,直接由上下文肯定,對於普通函數中指向不定的this,箭頭函數中處理this無疑更加簡單,以下:
//ES5普通函數
functionMan(){
this.age=22;
returnfunction(){
this.age+1;
}
}
var cala=newMan();
console.log(cala())//undefined
//ES6箭頭函數
functionMan(){
this.age=22;
return()=>this.age+1;
}
var cala=newMan();
console.log(cala())//23
三、箭頭函數中沒有arguments(咱們能夠用rest參數替代),也沒有原型,也不能使用new 關鍵字,例如:
//沒有arguments
var foo=(a,b)=>{return arguments[0]*arguments[1]}
console.log(foo(3,5))
//arguments is not defined
//沒有原型
varObj=()=>{};
console.log(Obj.prototype);
// undefined
//不能使用new 關鍵字
varObj=()=>{"hello world"};
var o =newObj();
// TypeError: Obj is not a constructor
四、箭頭函數給數組排序
const arr =[10,50,30,40,20]
const s = arr.sort((a, b)=> a - b)
console.log(s)// [10,20,30,40,50]
尾調用優化 尾調用是指在函數return的時候調用一個新的函數,因爲尾調用的實現須要存儲到內存中,在一個循環體中,若是存在函數的尾調用,你的內存可能爆滿或溢出。
ES6中,引擎會幫你作好尾調用的優化工做,你不須要本身優化,但須要知足下面3個要求: 一、函數不是閉包 二、尾調用是函數最後一條語句 三、尾調用結果做爲函數返回
尾調用實際用途——遞歸函數優化在ES5時代,咱們不推薦使用遞歸,由於遞歸會影響性能。 可是有了尾調用優化以後,遞歸函數的性能有了提高。
//新型尾優化寫法
"use strict";
function a(n, p =1){
if(n <=1){
return1* p
}
let s = n * p
return a(n -1, s)
}
//求 1 x 2 x 3的階乘
let sum = a(3)
console.log(sum)// 6
5、ES6對象新增方法
Object.assign()Object.assign()方法用於將全部可枚舉屬性的值從一個或多個源對象複製到目標對象。它將返回目標對象。 Object.assign 方法只會拷貝源對象自身的而且可枚舉的屬性到目標對象。該方法使用源對象的[[Get]]和目標對象的[[Set]],因此它會調用相關 getter 和 setter。所以,它分配屬性,而不只僅是複製或定義新的屬性。若是合併源包含getter,這可能使其不適合將新屬性合併到原型中。爲了將屬性定義(包括其可枚舉性)複製到原型,應使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。 String類型和 Symbol 類型的屬性都會被拷貝。 合併對象
var o1 ={ a:1};
var o2 ={ b:2};
var o3 ={ c:3};
var obj =Object.assign(o1, o2, o3);
console.log(obj);// { a: 1, b: 2, c: 3 }
console.log(o1);// { a: 1, b: 2, c: 3 }, 注意目標對象自身也會改變。
合併具備相同屬性的對象
var o1 ={ a:1, b:1, c:1};
var o2 ={ b:2, c:2};
var o3 ={ c:3};
var obj =Object.assign({}, o1, o2, o3);
console.log(obj);// { a: 1, b: 2, c: 3 }
6、Map和Set
Map和Set都叫作集合,可是他們也有所不一樣。Set常被用來檢查對象中是否存在某個鍵名,Map集合常被用來獲取已存的信息。 Set是有序列表,含有相互獨立的非重複值。Array和Set對比都是一個存儲多值的容器,二者能夠互相轉換,可是在使用場景上有區別。以下: Array的indexOf方法比Set的has方法效率低下 Set不含有重複值(能夠利用這個特性實現對一個數組的去重) Set經過delete方法刪除某個值,而Array只能經過splice。二者的使用方便程度前者更優 Array的不少新方法map、filter、some、every等是Set沒有的(可是經過二者能夠互相轉換來使用) Object和Map對比Object是字符串-值,Map是值-值 Object鍵爲string類型,Map的鍵是任意類型 手動計算Object尺寸,Map.size能夠獲取尺寸 Map的排序是插入順序 Object有原型,因此映射中有一些缺省的鍵。能夠理解爲Map=Object.create(null)
Set操做集合
letset=newSet()
// Set轉化爲數組
let arr =Array.from(set)
let arr =[...set]
// 實例屬性(繼承自Set)
set.constructor ===Set
set.size
// 操做方法
set.add(1)// 添加一個值
set.delete(1)//刪除一個值
set.has(1)//判斷是否有這個值(Array中的indexOf)
set.clear()//清除全部值
// 獲取用於遍歷的成員方法(Set的遍歷順序就是插入順序)
set.keys()// 返回鍵名的遍歷器
set.values()// 返回鍵值得遍歷器
set.entries()// 返回鍵值對的遍歷器
set.forEach()// 循環遍歷每一個值(和Array的方法一致)
for(let key of set.keys()){}
for(let val of set.values()){}
for(let entry of set.entries()){}
// 使用數組方法來處理set值
set=newSet(arr)
set=newSet([...set].map((x)=> x = x *2))
set=newSet([...set].filter((x)=> x >2))
Map的方法集合
let map =newMap()
// 實例屬性(繼承自Map)
map.constructor ===Map
map.size
// 操做方法
map.set(1,2)
map.get(1)
map.delete(1)
map.has(1)
map.clear()
// 遍歷方法
map.keys()
map.values()
map.entries()
map.forEach()
// Map和數組的轉換
map =newMap([['key','val'],[2,1]])// 要求雙成員數組
let arr =[...map]
// 值得注意的是Map的鍵是跟內存綁定的
map.set([1],'s')
map.get([1])
let arr =[1]
let arr1 =[1]
map.set(arr,'s')
map.get(arr)
map.set(arr1,'s')
map.get(arr1)
7、迭代器(Iterator)
一、entries() 返回迭代器:返回鍵值對
//數組
const arr =['a','b','c'];
for(let v of arr.entries()){
console.log(v)
}
// [0, 'a'] [1, 'b'] [2, 'c']
//Set
const arr =newSet(['a','b','c']);
for(let v of arr.entries()){
console.log(v)
}
// ['a', 'a'] ['b', 'b'] ['c', 'c']
//Map
const arr =newMap();
arr.set('a','a');
arr.set('b','b');
for(let v of arr.entries()){
console.log(v)
}
// ['a', 'a'] ['b', 'b']
二、values() 返回迭代器:返回鍵值對的value
//數組
const arr =['a','b','c'];
for(let v of arr.values()){
console.log(v)
}
//'a' 'b' 'c'
//Set
const arr =newSet(['a','b','c']);
for(let v of arr.values()){
console.log(v)
}
// 'a' 'b' 'c'
//Map
const arr =newMap();
arr.set('a','a');
arr.set('b','b');
for(let v of arr.values()){
console.log(v)
}
// 'a' 'b'
三、keys() 返回迭代器:返回鍵值對的key
//數組
const arr =['a','b','c'];
for(let v of arr.keys()){
console.log(v)
}
// 0 1 2
//Set
const arr =newSet(['a','b','c']);
for(let v of arr.keys()){
console.log(v)
}
// 'a' 'b' 'c'
//Map
const arr =newMap();
arr.set('a','a');
arr.set('b','b');
for(let v of arr.keys()){
console.log(v)
}
// 'a' 'b'
雖然上面列舉了3種內建的迭代器方法,可是不一樣集合的類型還有本身默認的迭代器,在for of中,數組和Set的默認迭代器是values(),Map的默認迭代器是entries()。
for of循環解構
對象自己不支持迭代,可是咱們能夠本身添加一個生成器,返回一個key,value的迭代器,而後使用for of循環解構key和value。
const obj ={
a:1,
b:2,
*[Symbol.iterator](){
for(let i in obj){
yield[i, obj[i]]
}
}
}
for(let[key, value] of obj){
console.log(key, value)
}
// 'a' 1, 'b' 2
字符串迭代器
const str ='abc';
for(let v of str){
console.log(v)
}
// 'a' 'b' 'c'
ES6給數組添加了幾個新方法:find()、findIndex()、fill()、copyWithin()
一、find():傳入一個回調函數,找到數組中符合當前搜索規則的第一個元素,返回它,而且終止搜索。
const arr =[1,"2",3,3,"2"]
console.log(arr.find(n =>typeof n ==="number"))// 1
二、findIndex():傳入一個回調函數,找到數組中符合當前搜索規則的第一個元素,返回它的下標,終止搜索。
const arr =[1,"2",3,3,"2"]
console.log(arr.findIndex(n =>typeof n ==="number"))// 0
三、fill():用新元素替換掉數組內的元素,能夠指定替換下標範圍。
arr.fill(value, start,end)
四、copyWithin():選擇數組的某個下標,從該位置開始複製數組元素,默認從0開始複製。也能夠指定要複製的元素範圍。
arr.copyWithin(target, start,end)const arr =[1,2,3,4,5]console.log(arr.copyWithin(3))// [1,2,3,1,2] 從下標爲3的元素開始,複製數組,因此4, 5被替換成1, 2const arr1 =[1,2,3,4,5]console.log(arr1.copyWithin(3,1))// [1,2,3,2,3] 從下標爲3的元素開始,複製數組,指定複製的第一個元素下標爲1,因此4, 5被替換成2, 3const arr2 =[1,2,3,4,5]console.log(arr2.copyWithin(3,1,2))// [1,2,3,2,5] 從下標爲3的元素開始,複製數組,指定複製的第一個元素下標爲1,結束位置爲2,因此4被替換成2ES6中類class、Promise與異步編程、代理(Proxy)和反射(Reflection)API,這幾塊內容比較複雜,之後有機會再詳細寫。