ES6 部分 Typescript 部分 前端工程面經(節流防抖、https、前端攻擊、性能優化...) https://juejin.cn/post/6844903734464495623 ES6面試(點擊可展開) 為什麼選擇 ES6 ? ES6是新一代的 JS 語言標準,規範了JS的使用標準(v ...
-
ES6 部分
-
Typescript 部分
-
前端工程面經(節流防抖、https、前端攻擊、性能優化...)
ES6面試(點擊可展開)
為什麼選擇 ES6 ?
- ES6是新一代的 JS 語言標準,規範了JS的使用標準(var標量提升)、新增了 JS 原生方法更加優雅
ES5、ES6、ES2015區別
- ES2015特指在2015年發佈的新一代JS語言標準,ES6泛指下一代JS語言標準,包含ES2015、ES2016、ES2017、ES2018等。現階段在絕大部分場景下,ES2015預設等同ES6。ES5泛指上一代語言標準。ES2015可以理解為ES5和ES6的時間分界線。
babel is what?
- babel是一個 ES6 轉碼器,可以將 ES6 代碼轉為 ES5 代碼,以便相容那些還沒支持ES6的平臺。
String字元串類型的升級優化
優化:
- 新增模板字元串,不需要再使用 + 拼接字元串,使用(
${}
)取代以往字元串形式,可以保存空格、換行符等
升級:
- 新增 includes 方法(以往只有indexOf、lastIndexOf查找字元位置)
- 新增 startsWith()、endsWith()、padStart()、padEnd()、repeat()
Array數組類型的升級優化
優化:
- 數組解構賦值
let [a,b,c] = [1,2,3]
形式進行賦值 - 擴展運算符 實現數組和鬆散序列的轉化
let args = [...arguments]
,輕鬆實現數組結構複製
升級:
-
新增
find
,且修複[NaN].indexOf(NaN) === -1
的bug- 這裡的修複指傳 callback
arr.find(item => isNaN(item))
- 這裡的修複指傳 callback
-
copyWithin(target , start 【, end 預設length】)
,將數組 (s,end] 的內容複製到 target開始的地方,end為負數則就取start處的數據 -
includes()
:可以用來改寫條件1 && 條件2 && 條件3
-
fill()
:new Array(10).fill(1)
-
flat()
:[1,2,[2,3]].flat()
,只能展開一層
Number數字類型的升級優化
優化:
- 更加嚴謹安全
Number.isNaN
:更為嚴格安全,isNaN()
會將字元串先往Number類型轉換,再判斷Number.isFinite()
: Infinity、字元串、對象返回false
,全局isFinite()
同上Number.isInteger(1.0) // true
,判斷是否是整數
Math
:Math.cbrt(8) === 2
,計算立方根Math.hypot(3:,4) === 25
,計算所有參數的平方的和的平方根Math.sign()
:正數返回1,負數返回-1,非數字返回NaNMath.trunc()
:先轉換為Number類型,正數、負數取整 / 非數字返回NaN
Object類型的升級優化
優化:
- 對象屬性變數式聲明:
// 屬性名與對應value的變數名相同時可縮寫
let [apple, orange] = ['red appe', 'yellow orange'];
let myFruits = {apple, orange};
// let myFruits = {apple: 'red appe', orange: 'yellow orange'};
對象解構賦值
對象的擴展運算符(...)
:let {...obj} = {'a' : 1 , 'b' : 2}
super
關鍵字:同this總是指向當前函數所在的對象不同- super總是指向
當前函數所在對象的原型對象
- super總是指向
升級,ES6在Object原型上新增的方法
Object.is
:做兩個目標對象的相等比較,修複NaN === NaN //false
,Object.is(NaN,NaN) // true
Object.assign(onlyTarget , source1 ,...)
:合併覆蓋到onlyTarget
中- source中的
自身屬性
會被合併 - source中的
繼承屬性
、不可枚舉的屬性
不會被合併- 且無法正確複製get和set屬性(會直接執行get/set函數,取return的值)
- source中的
getOwnPropertyDescriptors(obj)
:獲取對象所有的自身屬性(配合defineProperties
使用),ES5中getOwnPropertyDescriptor(obj,屬性名)
只能單個獲取(配合defineProperty
使用)defineProperties( obj , {屬性1 : {配置文件} , 屬性2 :{配置文件} ... } )
defineProperty( obj , 屬性名 , {配置文件} )
getPropertypeOf()
與setProperTypeOf()
:獲取或設置當前對象的prototype對象__proto__
屬性只是各大瀏覽器私加過於廣泛而默許的屬性- 在非瀏覽器環境不一定可以使用
keys() / values() / entries()
:分別獲取對象鍵 / 值 / 鍵值對
的數組
Function函數類型
優化:
- 箭頭函數:箭頭函數內的
this 指向函數定義時所在的對象
,而不是函數執行時所在的對象
- 箭頭函數如果外層仍是箭頭函數,將繼續向更外層尋找
- 箭頭函數不能用作
構造函數
,因為沒有自己的this,在new重定向this指向時,沒有屬於自己的this的可供綁定
- 箭頭函數沒有this,也不存在
arguments
對象,不過對象擴展符獲取var fn = (...arguments)=>{console.log(arguments)} ; fn(1,2,3) // [1,2,3]
- 函數
預設賦值
,ES6之前無法通過形參給出預設賦值
升級部分
- ES6新增了
雙冒號運算符
,用來取代bind、call和apply(瀏覽器暫不支持,Babel已經支持轉碼)
foo::bar;
// 等同於
bar.bind(foo);
foo::bar(...arguments);
// 等同於
bar.apply(foo, arguments);
Symbol是什麼
- ES6引入的第7種基本數據類型,
Symbol()生成的值都是獨一無二的
- 解決對象屬性名太多導致的屬性名衝突問題
Symbol作對象屬性名
無法被for in
、for of
、Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
遍歷到- Symbol() 不是對象的私有屬性,
其它對象也可以使用它作屬性
- Symbol() 不是對象的私有屬性,
let a = Symbol('a');
let obj = {} ;
obj[a] = 'xxx';
// obj = {a : 'xxx'} 的屬性名不是Symbol('a'),而是預設使用字元'a' 作屬性名
Set 是什麼
- 成員唯一,可以用來去重,
Array.from(new Set([1,2,3,3,3,4]))
- 多個
NaN
被Set認為是相同的,+0
與-0
也是相同的,undefined
也會被存儲
- 多個
.size()
、.add()
、.clear()
、.delete()
、.has()
- 繼承自Object,
.keys()
、.values()
、.entries()
、.forEach()
- 繼承自Object,
Map 是什麼
new Map() | new Map([['key1' , 'v1'] , ['key2', 'v2']])
- Map可以看作是Object的超集,打破傳統鍵值的限制
- 出現原因:
Object的key局限於字元串
,可以是函數、對象、任意基本類型(甚至是NaN)
.size()
、.clear()
、delete()
、entries()
、get(key)
、has(key)
、keys()
、set(key,value)
、values()
entries()
返回迭代對象(遍歷:for...of... / next())
- 遍歷Map:
for (let [key, value] of myMap)
、myMap.forEach(function(value, key){}
- 出現原因:
Proxy 是什麼
- Proxy可以攔截對象的get/set方法自由去處理
const handler = {
get: function(obj, prop) {
return prop in obj ? obj[prop] : 37;
}
};
const p = new Proxy({}, handler);
Reflect 是什麼
- 是將原生的一些零散分佈在Object、Function或者全局函數里的方法(如apply、delete、get、set等等),
統一整合到Reflect上
Reflect.apply(target, thisArgument, argumentsList)
整合 apply方法Reflect.defineProperty(target, propertyKey, attributes)
整合defineProperty方法
Promise 是什麼
- 解決 JS 非同步機制中回調機制產生的
回調地獄
問題- 可以鏈式調用
Iterator 是什麼
-
是一種標準、解決方案,
Set、Map都不能用for迴圈遍歷
- 要麼單獨為它倆設計,要麼統一規劃新的API(被採取)
-
就好像JavaScript是ECMAScript標準的一種具體實現一樣,
Iterator標準的具體實現是Iterator遍歷器
Iterator標準規定
- 所有部署了key值為[Symbol.iterator],且[Symbol.iterator]的value是標準的Iterator介面函數
- 該函數必須返回一個對象
- 對象中包含next方法
- 執行next()能返回包含value/done屬性的Iterator對象)的對象,都稱之為可遍歷對象
- next()後返回的Iterator對象也就是Iterator遍歷器
//obj.[Symbol.iterator]() 就是Iterator遍歷器
let obj = {
data: [ 'hello', 'world' ],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if (index < self.data.length) {
return {
value: self.data[index++],
done: false
};
} else {
return { value: undefined, done: true };
}
}
};
}
};
ES6規定,所有部署了Iterator介面的對象(可遍歷對象)都可以用for...of遍歷
- 擴展運算符本質上也是由for...of的一種實現
//Array
let array = ['red', 'green', 'blue'];
array[Symbol.iterator]() //Iterator遍歷器
array[Symbol.iterator]().next() //{value: "red", done: false}
//String
let string = '1122334455';
string[Symbol.iterator]() //Iterator遍歷器
string[Symbol.iterator]().next() //{value: "1", done: false}
//set
let set = new Set(['red', 'green', 'blue']);
set[Symbol.iterator]() //Iterator遍歷器
set[Symbol.iterator]().next() //{value: "red", done: false}
//Map
let map = new Map();
let obj= {map: 'map'};
map.set(obj, 'mapValue');
map[Symbol.iterator]().next() {value: Array(2), done: false}
for...of 來由
- 由上可知出現了統一的遍歷方法
ES6規定,所有部署了Iterator介面的對象(可遍歷對象)都可以用for...of遍歷
- 擴展運算符本質上也是由for...of的一種實現
Generator 函數
Generator函數可以說是Iterator介面的具體實現方式
- yeild 相當於 next() 方法
// 註意 * 貼著function
function* generator(){
yeild 1;
yeild 2;
}
async 函數
Generator函數可以說是Iterator介面的具體實現方式
- async函數可以理解為
內置自動執行器
的Generator函數語法糖,它配合ES6的Promise近乎完美的實現了非同步編程解決方案
Class、extends 是什麼
作為ES5生成實例對象的語法糖
,使對象實例化的過程更像面向對象編程
- Class內部定義的所有方法都是不可枚舉的
// ES5 玩法
function MyClass(data , props){
this.data = data
this.props = props
return this
}
MyClass.prototype.toString = function(){
return this.data + ',' + this.props
}
var mc = new MyClass('a','b')
// ES6 玩法
class MyClass{
constructor(data,props){
this.data = data
this.props = props
}
toString(){
return this.data + ',' + this.props
}
}
Object.keys(MyClass.prototype) // []
- ES6的構造函數必須使用
new
,ES5不使用new也可以執行 Class不存在變數提升
,必須先聲明Class才能使用,ES5中可以先實例化、再聲明- ES5的繼承實質:先創造子類的實例化對象this,再將父對象的方法掛載到this上
- ES6則是先將父類實例對象的方法、屬性掛載到this(super),再用子類構造函數進一步實例化this
// ES5方法
function Parent(){ }
function Child(){ }
// 方法1
var child1 = new Child()
child1.prototype = new Parent() // 這樣 child1 就可以訪問 Parent 上的方法了
// 方法2
function Child(){
Parent.call(this , arguments)
}
// ES6方法
class Child extends Parent{
constructor(props){
super(props) // 可以在父類中列印數據
this.props = props
}
}
module、export、import 是什麼
- module、export、import是ES6用來統一前端模塊化方案的設計思路和實現方案
- import引入的模塊是
靜態載入(編譯階段載入)
而不是動態載入(運行時載入)
- 靜態載入優點:
引入巨集(macro)
和類型檢驗(type system)
- 將來瀏覽器的新 API 就能用模塊格式提供,不再必要做成全局變數或者navigator對象的屬性
- 不再需要對象作為命名空間(比如Math對象),未來這些功能可以通過模塊提供
- 靜態載入優點:
- import引入export導出的介面值是動態綁定關係,即通過該介面,可以取到模塊內部實時的值
- import引入的模塊是
編程中哪些適合用ES6優化/規範
- 箭頭函數取代
var self = this
let
取代var
- 常用數組/對象的
解構賦值
來命名變數let { name , age } = { ...{name:'lhx' , age : 12 } }
- 長字元串拼接可以使用模板字元串
- Class取代傳統構造函數,實例化對象
- 保持模塊化開發思維,常用 import、 export方法
Typescript 函數
參數部分
- 基礎傳參
function fn(str1 : string , num1 : number)
- 預設傳參
function fn(str1 : string = 'default' , num1 : number = 12)
- 剩餘傳參
function fn(s1 : string . s2 : string , arr : string[] )
調用=>fn('a','b','c','d','e')
- 可選傳參
function fn(s1 : string , num1 ?: number )
函數重載
- 先給出
函數名相同、接收不同參數
的函數聲明,再給出函數的定義不同參數
定義:- 數量不同
- 順序不同
- 類型不同
function disp(s1:string):void;
function disp(n1:number,s1:string):void;
function disp(s1:string,n1:number):void;
// 參數類型不同,應設置為any
function disp(x: string | number,y?:string | number):void {
console.log(x);
console.log(y);
}
disp("abc")
disp(1,"xyz");
disp('a' , 100)