做者:Alexjavascript
譯者:前端小智html
來源:dev.to前端
阿里雲最近在作活動,低至2折,真心以爲很划算了,能夠點擊本條內容或者連接進行參與: promotion.aliyun.com/ntms/yunpar…java
ECMAScript 6(如下簡稱ES6)是 JS 語言的下一代標準,已經在2015
年6
月正式發佈了。它的目標,是使得 JS 語言能夠用來編寫複雜的大型應用程序,成爲企業級開發語言。接下來我們來看看 20 道棘手的面試題,經過作題,順帶提高一下我們的 JS 的技能。git
ES5
和ES6
的區別嗎?主題: JavaScript 難度: ⭐⭐⭐es6
ECMAScript 5 (ES5):ECMAScript 的第五版,於2009年標準化,該標準已在全部現代瀏覽器中徹底支持。github
ECMAScript 6 (ES6)/ ECMAScript 2015 (ES2015):ECMAscript 第 6 版,2015 年標準化。這個標準已經在大多數現代瀏覽器中部分實現。面試
如下是ES5和ES6之間的一些主要區別:數據庫
箭頭函數和字符串插值設計模式
const greetings = (name) => {
return `hello ${name}`;
}
複製代碼
也能夠這樣寫:
const greetings = name => `hello ${name}`;
複製代碼
const:const
表示沒法修改變量的原始值。須要注意的是,const
表示對值的常量引用,我們能夠改變被引用的對象的屬性值,但不能改變引用自己。
const NAMES = [];
NAMES.push("Jim");
console.log(NAMES.length === 1); // true
NAMES = ["Steve", "John"]; // error
複製代碼
塊做用域:ES6 中 let
, const
會建立塊級做用域,不會像 var
聲明變量同樣會被提高。
默認參數:默認參數使我們可使用默認值初始化函數。當參數省略或 undefined
時使用默認參數值。
function multiply (a, b = 2) {
return a * b;
}
multiply(5); // 10
複製代碼
類定義與繼承
ES6 引入了對類(class
關鍵字)、構造函數(constructor
關鍵字)和 extend
關鍵字(用於繼承)的語言支持。
for-of 運算符
for...of
語句建立一個遍歷可迭代對象的循環。
展開操做符
const obj1 = { a: 1, b: 2 }
const obj2 = { a: 2, c: 3, d: 4}
const obj3 = {...obj1, ...obj2}
複製代碼
Promises: Promises 提供了一種機制來處理異步操做的結果和錯誤。可使用回調來完成相同的事情,可是Promises
經過方法連接和簡潔的錯誤處理來提升可讀性。
const isGreater = (a, b) => {
return new Promise ((resolve, reject) => {
if(a > b) {
resolve(true)
} else {
reject(false)
}
})
}
isGreater(1, 2)
.then(result => {
console.log('greater')
})
.catch(result => {
console.log('smaller')
})
複製代碼
模塊導出
const myModule = { x: 1, y: () => { console.log('This is ES5') }}
export default myModule;
複製代碼
和導入
import myModule from './myModule';
複製代碼
主題: JavaScript 難度: ⭐⭐⭐
IIFE
是一個當即調用的函數表達式,它在建立後當即執行
(function IIFE(){
console.log( "Hello!" );
})();
// "Hello!"
複製代碼
經常使用此模式來避免污染全局命名空間,由於在IIFE
中使用的全部變量(與任何其餘普通函數同樣)在其做用域以外都是不可見的。
主題: JavaScript 難度: ⭐⭐⭐
如下是一些經驗分享:
在全局做用域內和Object.prototype
屬性中使用 function
。
爲對象構造函數使用 class
。
其它狀況使用箭頭函數。
爲啥大多數狀況都使用箭頭函數?
做用域安全性:當箭頭函數被一導致用時,全部東西都保證使用與根對象相同的thisObject
。若是一個標準函數回調與一堆箭頭函數混合在一塊兒,那麼做用域就有可能變得混亂。
緊湊性:箭頭函數更容易讀寫。
清晰度:使用箭頭函數可明確知道當前 this
指向。
主題: JavaScript 難度: ⭐⭐⭐
Symbol
是一種新的、特殊的對象,能夠用做對象中唯一的屬性名。使用 Symbol
替換string
能夠避免不一樣的模塊屬性的衝突。還能夠將Symbol
設置爲私有,以便尚無直接訪問Symbol
權限的任何人都不能訪問它們的屬性。
Symbol
是JS新的基本數據類型。與number
、string
和boolean
原始類型同樣,Symbol
也有一個用於建立它們的函數。與其餘原始類型不一樣,Symbol
沒有字面量語法。建立它們的惟一方法是使用如下方法中的Symbol
構造函數
let symbol = Symbol();
複製代碼
主題: JavaScript 難度: ⭐⭐⭐
ES6 的展開語法在以函數形式進行編碼時很是有用,由於我們能夠輕鬆地建立數組或對象的副本,而無需求助於Object.create,slice
或庫函數。Redux
和rx.js
項目中常用此特性。
function putDookieInAnyArray(arr) {
return [...arr, 'dookie'];
}
const result = putDookieInAnyArray(['I', 'really', "don't", 'like']);
// ["I", "really", "don't", "like", "dookie"]
const person = {
name: 'Todd',
age: 29,
};
const copyOfTodd = { ...person };
複製代碼
ES6 的 rest 語法提供了一種捷徑,其中包括要傳遞給函數的任意數量的參數。
就像展開語法的逆過程同樣,它將數據放入並填充到數組中而不是展開數組,而且它在函數變量以及數組和對象解構分中也常常用到。
function addFiveToABunchOfNumbers(...numbers) {
return numbers.map(x => x + 5);
}
const result = addFiveToABunchOfNumbers(4, 5, 6, 7, 8, 9, 10);
// [9, 10, 11, 12, 13, 14, 15]
const [a, b, ...rest] = [1, 2, 3, 4]; // a: 1, b: 2, rest: [3, 4]
const { e, f, ...others } = {
e: 1,
f: 2,
g: 3,
h: 4,
}; // e: 1, f: 2, others: { g: 3, h: 4 }
複製代碼
主題: JavaScript 難度: ⭐⭐⭐
// ES5 Function Constructor
function Person(name) {
this.name = name;
}
// ES6 Class
class Person {
constructor(name) {
this.name = name;
}
}
複製代碼
對於簡單的構造函數,它們看起來很是類似。
構造函數的主要區別在於使用繼承。若是我們建立一個繼承Person
類的Student
子類並添加一個studentId
字段,如下是兩種方式的使用:
// ES5 Function Constructor
function Student(name, studentID) {
// 調用你類的構造函數以初始化你類派生的成員。
Person.call(this. name)
// 初始化子類的成員。
this.studentId = studentId
}
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student
// ES6 Class
class Student extends Person {
constructor(name, studentId) {
super(name)
this.studentId = studentId
}
}
複製代碼
在 ES5 中使用繼承要複雜得多,並且 ES6 版本更容易理解和記住。
.call
和 .apply
區別是啥?主題: JavaScript 難度: ⭐⭐⭐
.call
和.apply
均用於調用函數,而且第一個參數將用做函數中this
的值。可是,.call
將逗號分隔的參數做爲下一個參數,而.apply
將參數數組做爲下一個參數。簡單記憶法:C用於call
和逗號分隔,A用於apply
和參數數組。
function add(a, b) {
return a + b;
}
console.log(add.call(null, 1, 2)); // 3
console.log(add.apply(null, [1, 2])); // 3
複製代碼
主題: JavaScript 難度: ⭐⭐⭐
選擇使用類的一些緣由:
語法更簡單,更不容易出錯。
使用新語法比使用舊語法更容易(並且更不易出錯)地設置繼承層次結構。
class
能夠避免構造函數中使用new的常見錯誤(若是構造函數不是有效的對象,則使構造函數拋出異常)。
用新語法調用父原型方法的版本比舊語法要簡單得多,用super.method()
代替ParentConstructor.prototype.method.call(this)
或Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)
考慮下面代碼:
使用 ES6 實現上述功能:
主題: JavaScript 難度: ⭐⭐⭐
能夠 Object.freeze
來實現枚舉
var DaysEnum = Object.freeze({
"monday": 1,
"tuesday": 2,
"wednesday": 3,
...
})
複製代碼
或者
var DaysEnum = {
"monday": 1,
"tuesday": 2,
"wednesday": 3,
...
}
Object.freeze(DaysEnum)
複製代碼
可是,這阻止我們把值分配給變量:
let day = DaysEnum.tuesday
day = 298832342 // 不會報錯
複製代碼
Object.freeze()
和 const
的區別主題: JavaScript 難度: ⭐⭐⭐
const
和Object.freeze
是兩個徹底不一樣的概念。
const
聲明一個只讀的變量,一旦聲明,常量的值就不可改變:
const person = {
name: "Leonardo"
};
let animal = {
species: "snake"
};
person = animal; // ERROR "person" is read-only
複製代碼
Object.freeze
適用於值,更具體地說,適用於對象值,它使對象不可變,即不能更改其屬性。
let person = {
name: "Leonardo"
};
let animal = {
species: "snake"
};
Object.freeze(person);
person.name = "Lima"; //TypeError: Cannot assign to read only property 'name' of object
console.log(person);
複製代碼
主題: JavaScript 難度: ⭐⭐⭐⭐
提高是指 JS 解釋器將全部變量和函數聲明移動到當前做用域頂部的操做,提高有兩種類型
只要一個var
(或函數聲明)出如今一個做用域內,這個聲明就被認爲屬於整個做用域,而且能夠在任何地方訪問。
var a = 2
foo() // 正常運行, foo 已被提高
function foo() {
a = 3
console.log(a) // 3
var a
}
console.log( a ) // 2
複製代碼
主題: JavaScript 難度: ⭐⭐⭐⭐
原型模式會建立新的對象,而不是建立未初始化的對象,它會返回使用從原型或樣本對象複製的值進行初始化的對象。原型模式也稱爲屬性模式。
原型模式有用的一個例子是使用與數據庫中的默認值匹配的值初始化業務對象。原型對象保留默認值,這些默認值將被複制到新建立的業務對象中。
傳統語言不多使用原型模式,可是JavaScript做爲一種原型語言,在構建新對象及其原型時使用這種模式。
主題: JavaScript 難度: ⭐⭐⭐⭐
在 ES6 中,let
和const
跟 var
、class
和function
同樣也會被提高,只是在進入做用域和被聲明之間有一段時間不能訪問它們,這段時間是臨時死區(TDZ)。
//console.log(aLet) // would throw ReferenceError
let aLet;
console.log(aLet); // undefined
aLet = 10;
console.log(aLet); // 10
複製代碼
主題: JavaScript 難度: ⭐⭐⭐⭐
不該該使用箭頭函數一些狀況:
當想要函數被提高時(箭頭函數是匿名的)
要在函數中使用this/arguments
時,因爲箭頭函數自己不具備this/arguments
,所以它們取決於外部上下文
使用命名函數(箭頭函數是匿名的)
使用函數做爲構造函數時(箭頭函數沒有構造函數)
當想在對象字面是以將函數做爲屬性添加並在其中使用對象時,由於我們沒法訪問 this
即對象自己。
主題: JavaScript 難度: ⭐⭐⭐⭐
WeakMaps 提供了一種從外部擴展對象而不影響垃圾收集的方法。當我們想要擴展一個對象,可是由於它是封閉的或者來自外部源而不能擴展時,能夠應用WeakMap
。
WeakMap只適用於 ES6 或以上版本。WeakMap是鍵和值對的集合,其中鍵必須是對象。
var map = new WeakMap();
var pavloHero = {
first: "Pavlo",
last: "Hero"
};
var gabrielFranco = {
first: "Gabriel",
last: "Franco"
};
map.set(pavloHero, "This is Hero");
map.set(gabrielFranco, "This is Franco");
console.log(map.get(pavloHero)); //This is Hero
複製代碼
WeakMaps的有趣之處在於,它包含了對map
內部鍵的弱引用。弱引用意味着若是對象被銷燬,垃圾收集器將從WeakMap
中刪除整個條目,從而釋放內存。
主題: JavaScript 難度: ⭐⭐⭐⭐
function foo(){ }();
複製代碼
IIFE
表明當即調用的函數表達式。JS解析器讀取函數foo(){}();
做爲函數foo(){}
和();
,前者是一個函數聲明,後者(一對括號)是嘗試調用一個函數,但沒有指定名稱,所以它拋出Uncaught SyntaxError: Unexpected token
異常。
我們可使用void
操做符:void function foo(){ }();
。不幸的是,這種方法有一個問題。給定表達式的求值老是undefined
的,因此若是IIFE 函數有返回值,則不能使用它,以下所示:
const foo = void
function bar() {
console.log('前端小智')
return 'foo';
}();
console.log(foo); // undefined
複製代碼
主題: JavaScript 難度: ⭐⭐⭐⭐
模塊模式一般用於命名空間,在該模式中,使用單個實例做爲存儲來對相關函數和對象進行分組。這是一個不一樣於原型設計的用例,它們並非相互排斥,我們能夠同時使用它們(例如,將一個構造函數放在一個模塊中,並使用new MyNamespace.MyModule.MyClass(arguments)
)。
構造函數和原型是實現類和實例的合理方法之一。它們與模型並不徹底對應,所以一般須要選擇一個特定的scheme
或輔助方法來實現原型中的類。
主題: JavaScript 難度: ⭐⭐⭐⭐⭐
當它們的鍵/值引用的對象被刪除時,它們的行爲都不一樣,如下面的代碼爲例:
var map = new Map()
var weakmap = new WeakMap()
(function() {
var a = {
x: 12
};
var b = {
y: 12
};
map.set(a, 1);
weakmap.set(b, 2);
})()
複製代碼
執行上面的 IIFE,就沒法再引用{x:12}
和{y:12}
。垃圾收集器繼續運行,並從 WeakMa
中刪除鍵b
指針,還從內存中刪除了{y:12}
。
但在使用 Map
的狀況下,垃圾收集器不會從Map
中刪除指針,也不會從內存中刪除{x:12}
。
WeakMap
容許垃圾收集器執行其回收任務,但Map
不容許。對於手動編寫的 Map
,數組將保留對鍵對象的引用,以防止被垃圾回收。但在WeakMap
中,對鍵對象的引用被「弱」保留,這意味着在沒有其餘對象引用的狀況下,它們不會阻止垃圾回收。
主題: JavaScript 難度: ⭐⭐⭐⭐⭐
柯里化是一種模式,其中一個具備多個參數的函數被分解成多個函數,當被串聯調用時,這些函數將一次累加一個所需的全部參數。這種技術有助於使用函數式編寫的代碼更容易閱讀和編寫。須要注意的是,要實現一個函數,它須要從一個函數開始,而後分解成一系列函數,每一個函數接受一個參數。
function curry(fn) {
if (fn.length === 0) {
return fn;
}
function _curried(depth, args) {
return function(newArgument) {
if (depth - 1 === 0) {
return fn(...args, newArgument);
}
return _curried(depth - 1, [...args, newArgument]);
};
}
return _curried(fn.length, []);
}
function add(a, b) {
return a + b;
}
var curriedAdd = curry(add);
var addFive = curriedAdd(5);
var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10]
複製代碼
主題: JavaScript 難度: ⭐⭐⭐⭐⭐
若是我們想要確保對象被深凍結,就必須建立一個遞歸函數來凍結對象類型的每一個屬性:
沒有深凍結
let person = {
name: "Leonardo",
profession: {
name: "developer"
}
};
Object.freeze(person);
person.profession.name = "doctor";
console.log(person); //output { name: 'Leonardo', profession: { name: 'doctor' } }
複製代碼
深凍結
function deepFreeze(object) {
let propNames = Object.getOwnPropertyNames(object);
for (let name of propNames) {
let value = object[name];
object[name] = value && typeof value === "object" ?
deepFreeze(value) : value;
}
return Object.freeze(object);
}
let person = {
name: "Leonardo",
profession: {
name: "developer"
}
};
deepFreeze(person);
person.profession.name = "doctor"; // TypeError: Cannot assign to read only property 'name' of object
複製代碼
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
阿里雲最近在作活動,低至2折,有興趣能夠看看:promotion.aliyun.com/ntms/yunpar…
乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。
由於篇幅的限制,今天的分享只到這裏。若是你們想了解更多的內容的話,能夠去掃一掃每篇文章最下面的二維碼,而後關注我們的微信公衆號,瞭解更多的資訊和有價值的內容。
每次整理文章,通常都到2點才睡覺,一週4次左右,挺苦的,還望支持,給點鼓勵