如今網上不少es6,es7的資料整理以及深刻分析,而這篇文章算是一個比較簡單的概括整理,比較簡單,主要涵蓋一些面試當中最多問道的一些問題,不過若是後期有時間會整理出來一篇更加詳細的,例如數組去重的方法概括大全之類的.歡迎各位大神批評指正.html
let
命令的用法和 var
類似,可是 let
只在所在代碼塊內有效。
基礎用法:vue
let a = 1;
let b = 2;
複製代碼
而且 let
有如下特色:node
var
聲明一個變量一個函數,都會伴隨着變量提高的問題,致使實際開發過程常常出現一些邏輯上的疑惑,按照通常思惟習慣,變量都是須要先聲明後使用。// var
console.log(v1); // undefined
var v1 = 2;
// 因爲變量提高 代碼實際以下
var v1;
console.log(v1)
v1 = 2;
// let
console.log(v2); // ReferenceError
let v2 = 2;
複製代碼
let
和 const
在相同做用域下,都不能重複聲明同一變量,而且不能在函數內從新聲明參數。// 1. 不能重複聲明同一變量
// 報錯
function f1 (){
let a = 1;
var a = 2;
}
// 報錯
function f2 (){
let a = 1;
let a = 2;
}
// 2. 不能在函數內從新聲明參數
// 報錯
function f3 (a1){
let a1;
}
// 不報錯
function f4 (a2){
{
let a2
}
}
複製代碼
const
聲明一個只讀的常量。
基礎用法:git
const PI = 3.1415926;
PI = 3;
// TypeError: Assignment to constant variable.
複製代碼
const
聲明時,必須賦值;const a ;
// SyntaxError: Missing initializer in const declaration.
複製代碼
const
聲明的常量,let
不能重複聲明;const PI = 3.1415926;
let PI = 0;
// Uncaught SyntaxError: Identifier 'PI' has already been declared
複製代碼
先講一個普通的例子:es6
var people = {
name: 'lux',
age: 20
}
//es5
var name = people.name
var age = people.age
//es6
const {name, age} = people
//數組一樣適用
var arr = [1,2,3];
const {one, , three} = arr;
複製代碼
與數組解構不一樣的是,對象解構不須要嚴格按照順序取值,而只要按照變量名去取對應屬性名的值,若取不到對應屬性名的值,則爲undefined
。面試
注意點:算法
let {a:b} = {a:1, c:2};
// error: a is not defined
// b => 1
複製代碼
對象的解構賦值的內部機制,是先找到同名屬性,而後再賦給對應的變量。真正被賦值的是後者,而不是前者。
上面代碼中,a
是匹配的模式,b
纔是變量。真正被賦值的是變量b
,而不是模式a
。express
let obj = {
a:[ 1, { b: 2}]
};
let {a, a: [c, {b}]} = obj;
// a=>[1, {b: 2}], b => 2, c => 1
複製代碼
指定解構的默認值:json
let {a=1} = {}; // a => 1
let {a, b=1} = {a:2}; // a => 2, b => 1
let {a:b=3} = {}; // b => 3
let {a:b=3} = {a:4}; // b = >4
// a是模式,b是變量 牢記
let {a=1} = {a:undefined}; // a => 1
let {a=1} = {a:null}; // a => null
// 由於null與undefined不嚴格相等,因此賦值有效
// 致使默認值1不會生效。
複製代碼
字符串的解構賦值中,字符串被轉換成了一個相似數組的對象。 基礎用法:數組
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
let {length:len} = 'hello';// len => 5
複製代碼
解構賦值的規則是,只要等號右邊的值不是對象或數組,就先將其轉爲對象。因爲undefined
和null
沒法轉爲對象,因此對它們進行解構賦值,都會報錯。
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
複製代碼
基礎用法:
function fun ([a, b]){
return a + b;
}
fun ([1, 2]); // 3
複製代碼
指定默認值的解構:
function fun ({a=0, b=0} = {}){
return [a, b];
}
fun ({a:1, b:2}); // [1, 2]
fun ({a:1}); // [1, 0]
fun ({}); // [0, 0]
fun (); // [0, 0]
function fun ({a, b} = {a:0, b:0}){
return [a, b];
}
fun ({a:1, b:2}); // [1, 2]
fun ({a:1}); // [1, undefined]
fun ({}); // [undefined, undefined]
fun (); // [0, 0]
複製代碼
let a = 1,b = 2;
[a, b] = [b, a]; // a =>2 , b => 1
複製代碼
// 返回一個數組
function f (){
return [1, 2, 3];
}
let [a, b, c] = f(); // a=>1, b=>2, c=>3
// 返回一個對象
function f (){
return {a:1, b:2};
}
let {a, b} = f(); // a=>1, b=>2
複製代碼
function f([a, b, c]) {...}
f([1, 2, 3]);
function f({a, b, c}) {...}
f({b:2, c:3, a:1});
複製代碼
let json = {
name : 'leo',
age: 18
}
let {name, age} = json;
console.log(name,age); // leo, 18
複製代碼
const m = new Map();
m.set('a',1);
m.set('b',2);
for (let [k, v] of m){
console.log(k + ' : ' + v);
}
// 獲取鍵名
for (let [k] of m){...}
// 獲取鍵值
for (let [,k] of m){...}
複製代碼
const {log, sin, cos} = require('math');
複製代碼
const object1 = {
a: 1,
b: 2,
c: 3
};
const object2 = Object.assign({c: 4, d: 5}, object1);
console.log(object2);
Object { c: 3, d: 5, a: 1, b: 2 }
// 這是淺拷貝,返回的不是一個新對象,而是把一個或多個源對象添加到目標對象
複製代碼
let object1 = {
a: 1,
b: 2,
c: 3
};
let object2 = {...object1};
object1.a=11;
console.log(object2);
Object { a: 11, b: 2, c: 3 }
// 這也是淺拷貝
複製代碼
let object1 = {
a: 1,
b: 2,
c: 3
};
let object2 =JSON.parse( JSON.stringify(object1));
object2.a=11;
console.log(object1,object2);
Object { a: 1, b: 2, c: 3 } Object { a: 11, b: 2, c: 3 }
複製代碼
介紹:
Set
數據結構相似數組,但全部成員的值惟一。
Set
自己爲一個構造函數,用來生成Set
數據結構,使用add
方法來添加新成員。
let a = new Set();
[1,2,2,1,3,4,5,4,5].forEach(x=>a.add(x));
for(let k of a){
console.log(k)
};
// 1 2 3 4 5
複製代碼
基礎使用:
let a = new Set([1,2,3,3,4]);
[...a]; // [1,2,3,4]
a.size; // 4
// 數組去重
[...new Set([1,2,3,4,4,4])];// [1,2,3,4]
複製代碼
注意:
Set
中添加值的時候,不會類型轉換,即5
和'5'
是不一樣的。[...new Set([5,'5'])]; // [5, "5"]
複製代碼
屬性和方法:
屬性:
Set.prototype.constructor
:構造函數,默認就是Set
函數。Set.prototype.size
:返回Set
實例的成員總數。操做方法:
add(value)
:添加某個值,返回 Set 結構自己。delete(value)
:刪除某個值,返回一個布爾值,表示刪除是否成功。has(value)
:返回一個布爾值,表示該值是否爲Set的成員。clear()
:清除全部成員,沒有返回值。let a = new Set();
a.add(1).add(2); // a => Set(2) {1, 2}
a.has(2); // true
a.has(3); // false
a.delete(2); // true a => Set(1) {1}
a.clear(); // a => Set(0) {}
複製代碼
數組去重:
let a = new Set([1,2,3,3,3,3]);
複製代碼
因爲傳統的JavaScript
對象只能用字符串當作鍵,給開發帶來很大限制,ES6增長Map
數據結構,使得各類類型的值(包括對象)均可以做爲鍵。
Map
結構提供了「值—值」的對應,是一種更完善的 Hash 結構實現。 基礎使用:
let a = new Map();
let b = {name: 'leo' };
a.set(b,'my name'); // 添加值
a.get(b); // 獲取值
a.size; // 獲取總數
a.has(b); // 查詢是否存在
a.delete(b); // 刪除一個值
a.clear(); // 清空全部成員 無返回
複製代碼
注意:
let a = new Map([
['name','leo'],
['age',18]
])
複製代碼
let a = new Map();
a.set(1,'aaa').set(1,'bbb');
a.get(1); // 'bbb'
複製代碼
undefined
。new Map().get('abcdef'); // undefined
複製代碼
let a = new Map();
let a1 = ['aaa'];
let a2 = ['aaa'];
a.set(a1,111).set(a2,222);
a.get(a1); // 111
a.get(a2); // 222
複製代碼
遍歷方法: Map 的遍歷順序就是插入順序。
keys()
:返回鍵名的遍歷器。values()
:返回鍵值的遍歷器。entries()
:返回全部成員的遍歷器。forEach()
:遍歷 Map 的全部成員。let a = new Map([
['name','leo'],
['age',18]
])
let a1 = [...a.keys()]; // a1 => ["name", "age"]
let a2 = [...a.values()]; // a2 => ["leo", 18]
let a3 = [...a.entries()];// a3 => [['name','leo'], ['age',18]]
複製代碼
經過上面的講述應該明白了Set的用法,那麼話很少說直接上代碼
let a=[1,2,3,4,5,6,1,2,3,2,1,2]
let b=new Set()
let c=[];
a.forEach(function (value,key,arr){
b.add(value)
})
b.forEach(function (value,key,set) {
c.push(value)
})
console.log(c)
// [1,2,3,4,5,6]
複製代碼
Promise 是一種規範,在es6之前,瀏覽器沒有內置的promise,不一樣框架有各自的promise實現,本文中的Promise以es6中的Promise爲例。
一個Promise封裝了一個操做(異步或同步)的結果和其狀態,按個人理解來看,它的出現有如下幾點緣由,一方面是爲了解決回調函數的嵌套問題,若是沒有promise 咱們能夠想象一下,應該如何控制流程,假設有如下場景,執行操做A,若是失敗了,執行 操做B,執行操做C,同時B 和 C 失敗和成功了也會有對應的處理,這裏爲了簡化,統一使用操做D來處理最終結果,那麼最後可能會寫出以下的代碼
function stepA(arg, onSuccess, onFailure) {
var output = dosomethingA(arg);
var res = output.res;
if (output.success) {
onSuccess(res);
} else {
onFaliure(res);
}
}
function stepB(arg, onSuccess, onFailure) {
var output = dosomethingB(arg);
var res = output.res;
if (output.success) {
onSuccess(res);
} else {
onFaliure(res);
}
}
function stepC(arg, onSuccess, onFailure) {
var output = dosomethingC(arg);
var res = output.res;
if (output.success) {
onSuccess(res);
} else {
onFaliure(res);
}
}
function stepD(arg) {
dosomethingD(arg);
}
stepA('test', function(res) {
stepB(res, stepD, stepD);
}, function(res) {
stepC(res, stepD, ste
複製代碼
這裏若是流程鏈更長或者將其中的幾個step換成是匿名函數的話,就會出現更多的回調嵌套,同時代碼會很是的長,根本沒法閱讀。 還有一個問題是,人們習慣於用線性的思想去思考爲題,promise這種鏈式調用的模式能夠將異步代碼看起來像同步代碼同樣執行。
仍是剛剛的那個例子,若是用Promise該怎麼改寫
function executeStep(arg, dosomething) {
return new Promise(function(resolve, reject) {
var result = dosomething(arg);
var res = result.res;
if (result.success) {
resolve(res);
} else {
reject(res);
}
});
}
function dosomethingA(arg) {
// ...
return result;
}
function dosomethingB(arg) {
// ...
return result;
}
function dosomethingC(arg) {
// ...
return result;
}
function dosomethingD(arg) {
// ...
console.log(arg);
}
function wrap(dosomething, result) {
return executeStep(result, dosomething);
}
wrap(dosomethingA, 'test').then(function(result) {
return wrap(dosomethingB, result);
}, function(reason) {
return wrap(dosomethingC, reason);
}).then(dosomethingD, dosomethingD);
複製代碼
根據PromiseA+規範,Promise有3種狀態,分別是pending,rejected和fullfilled,其中rejected 和fullfilled又稱爲settled,promise可由pending轉到settled,處於settled狀態的promise狀態不可再變化。
Promise.prototype.constructor
var promise = new Promise(function(resolve, reject) {
setTimeout(function() {
var res = Math.random();
if (res < 0.5) {
resolve(res);
} else {
reject(res);
}
});
});
複製代碼
其內部實現大致以下
function MyPromise(resolver){
if(typeof resolver !== 'function'){
throw new TypeError(resolver + ' is not a function');
}
if(!(this instanceof MyPromise) return new MyPromise(resolver);
var self = this;
self.status_ = 'PENDING';
self.data_ = undefined;
self.resolveCallBacks = [];
self.rejectCallBacks = [];
function resolve(value){
setTimeout(function(){
if(self.status_ === 'PENDING'){
self.status_ = 'RESOLVED';
self.data_ = value;
for(var i=0; i<self.rejectCallBacks.length; i++){
self.rejectCallBacks[i](value);
}
}
});
}
function reject(reason){
setTimeout(function(){
if(self.status_ === 'PENDING'){
self.status_ = 'REJECTED';
self.data_ = reason;
for(var i=0; i<self.resolveCallBacks.length; i++){
self.resolveCallBacks[i](value);
}
}
});
}
try{
resolver(resolve, reject);
}catch(reason){
reject(reason);
}
}
複製代碼
當經過構造函數建立一個Promise的時候,首先會將其狀態設置爲PENDING
,同時初始化resolve和reject的回調數組,在這裏,resolve
和reject
都是異步調用的,只有噹噹前Promise狀態爲PENDING
時,reject
和resolve
內部的代碼纔會執行, 更改Promise的狀態,將結果設置爲Promise的值,同時遍歷對應的回調數組並執行。
Promise.prototype.then(onFulfilled, onRejected)
這個是Promise中使用頻率最高的一個方法了,它會更具Promise的狀態選擇執行回調或將回掉添加到Promise的回調數組上,大體實現以下
MyPromise.prototype.then = function(onResolved, onRejected) {
onResolved = typeof onResolved === 'function' ? onResolved : function(value){return value};
onRejected = typeof onRejected === 'function' ? onRejected : function(reason){throw reason};
var self = this;
var newPromise;
if(self.status_ === 'RESOLVED'){
newPromise = new MyPromise(function(resolve, reject){
setTimeout(function(){
try{
var x = onResolved(self.data_);
resolvePromise(newPromise, x, resolve, reject);
}catch(reason){
reject(reason);
}
});
});
}else if(self.status_ === 'REJECTED'){
newPromise = new MyPromise(function(resolve, reject){
setTimeout(function(){
try{
var x = onRejected(self.data_);
resolvePromise(newPromise, x, resolve, reject);
}catch(reason){
reject(reason);
}
});
});
}else{
newPromise = new MyPromise(function(resolve, reject){
self.resolveCallBacks.push(function(){
try{
var x = onResolved(self.data_);
resolvePromise(newPromise, x, resolve, reject);
}catch(reason){
rejcet(reason);
}
});
self.rejectCallBacks.push(function(){
try{
var x = onRejected(self.data_);
resolvePromise(newPromise, x, resolve, reject);
}catch(reason){
reject(reason);
}
});
});
}
return newPromise;
};
複製代碼
首先會對傳入的回調函數進行檢測,若是不是function的話就會使用默認的function(這裏的resolvePromise將會在下文講到),以後判斷當前Promise的狀態,若是Promise已經決議,則將當前Promise的值傳給對應的回調,同時執行resolvePromise
,這些操做會在新建立的Promise中經過異步的方式進行執行,若是當前Promise處於PENDING
狀態,則會將回調添加到當前Promise的回調數組中。下面看一下resolvePromise
function resolvePromise(mPromise, x, resolve, reject){
var then;
var thenCalledOrThrow = false;
if(mPromise === x){
return reject(new TypeError('Circle is not expected to exist'));
} // Promises/A+ 2.3.1
if(x instanceof MyPromise){ // 2.3.2
if(x.status_ === 'PENDING'){
x.then(function(value){
resolvePromise(mPromise, value, resolve, reject)
}, reject);
}else{
x.then(resolve, reject);
}
return;
}
if((x != null) && ((typeof x === 'object') || (typeof x === 'function'))){ // 2.3.3
try{
then = x.then; // Maybe then is a getter 2.3.3.1
if(typeof then === 'function'){ // 2.3.3.3
then.call(x, function rs(y){
if(thenCalledOrThrow) return;
thenCalledOrThrow = true;
return resolvePromise(mPromise, y, resolve, reject); // 2.3.3.3.1
}, function rj(r){
if(thenCalledOrThrow) return;
thenCalledOrThrow = true;
return reject(r); // 2.3.3.3.2
});
}else{
return resolve(x); // 2.3.3.4
}
}catch(reason){ // 2.3.3.2
if(thenCalledOrThrow) return;
thenCalledOrThrow = true;
return reject(reason);
}
}else{
return resolve(x); // 2.3.4
}
}
複製代碼
這裏註釋中的標記表明上文提到的PromiseA+規範中的條目
Promise.prototype.catch(onRejected)
至關於then(null, onRejected)
, 注意的是在Promise裏,發生的異常不會輸出到控制檯,而會被存爲Promise的值
這部份內容原本是要放在es7下講的可是Promise和async/await結合起來會解決回調地獄的問題因此es7那塊就放上文章連接,這邊稍微講下async/await就好.
在async/await以前,咱們有三種方式寫異步代碼
嵌套回調
以Promise爲主的鏈式回調
使用Generators
可是,這三種寫起來都不夠優雅,ES7作了優化改進,async/await應運而生,async/await相比較Promise 對象then 函數的嵌套,與Generator 執行的繁瑣(須要藉助co才能自動執行,不然得手動調用next() ), Async/Await 可讓你輕鬆寫出同步風格的代碼同時又擁有異步機制,更加簡潔,邏輯更加清晰。
async/await更加語義化,async 是「異步」的簡寫,async function 用於申明一個function 是異步的; await,能夠認爲是async wait的簡寫, 用於等待一個異步方法執行完成;
async/await是一個用同步思惟解決異步問題的方案(等結果出來以後,代碼纔會繼續往下執行)
能夠經過多層async function 的同步寫法代替傳統的callback嵌套
自動將常規函數轉換成Promise,返回值也是一個Promise對象
只有async函數內部的異步操做執行完,纔會執行then方法指定的回調函數
異步函數內部可使用await
async function name([param[, param[, ... param]]]) { statements }
name: 函數名稱。
param: 要傳遞給函數的參數的名稱
statements: 函數體語句。
返回值: 返回的Promise對象會以async function的返回值進行解析,或者以該函數拋出的異常進行回絕。
複製代碼
await 放置在Promise調用以前,await 強制後面點代碼等待,直到Promise對象resolve,獲得resolve的值做爲await表達式的運算結果
await只能在async函數內部使用,用在普通函數裏就會報錯
[return_value] = await expression;
expression: 一個 Promise 對象或者任何要等待的值。
返回值:返回 Promise 對象的處理結果。若是等待的不是 Promise 對象,則返回該值自己。
複製代碼
在async函數裏,不管是Promise reject的數據仍是邏輯報錯,都會被默默吞掉,因此最好把await放入try{}catch{}中,catch可以捕捉到Promise對象rejected的數據或者拋出的異常
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {reject('error')}, ms); //reject模擬出錯,返回error
});
}
async function asyncPrint(ms) {
try {
console.log('start');
await timeout(ms); //這裏返回了錯誤
console.log('end'); //因此這句代碼不會被執行了
} catch(err) {
console.log(err); //這裏捕捉到錯誤error
}
}
asyncPrint(1000);
複製代碼
若是不用try/catch的話,也能夠像下面這樣處理錯誤(由於async函數執行後返回一個promise)
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {reject('error')}, ms); //reject模擬出錯,返回error
});
}
async function asyncPrint(ms) {
console.log('start');
await timeout(ms)
console.log('end'); //這句代碼不會被執行了
}
asyncPrint(1000).catch(err => {
console.log(err); // 從這裏捕捉到錯誤
});
複製代碼
若是你不想讓錯誤中斷後面代碼的執行,能夠提早截留住錯誤,像下面
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('error')
}, ms); //reject模擬出錯,返回error
});
}
async function asyncPrint(ms) {
console.log('start');
await timeout(ms).catch(err => { // 注意要用catch
console.log(err)
})
console.log('end'); //這句代碼會被執行
}
asyncPrint(1000);
複製代碼
多個await命令的異步操做,若是不存在依賴關係(後面的await不依賴前一個await返回的結果),用Promise.all()讓它們同時觸發
function test1 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
}
function test2 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 2000)
})
}
async function exc1 () {
console.log('exc1 start:',Date.now())
let res1 = await test1();
let res2 = await test2(); // 不依賴 res1 的值
console.log('exc1 end:', Date.now())
}
async function exc2 () {
console.log('exc2 start:',Date.now())
let [res1, res2] = await Promise.all([test1(), test2()])
console.log('exc2 end:', Date.now())
}
exc1();
exc2();
複製代碼
exc1 的兩個並列await的寫法,比較耗時,只有test1執行完了纔會執行test2
你能夠在瀏覽器的Console裏嘗試一下,會發現exc2的用Promise.all執行更快一些
let func = (num) => num;
let func = () => num;
let sum = (num1,num2) => num1 + num2;
[1,2,3].map(x => x * x);
複製代碼
(1). 箭頭函數this爲父做用域的this,不是調用時的this
箭頭函數的this永遠指向其父做用域,任何方法都改變不了,包括call,apply,bind。
普通函數的this指向調用它的那個對象。
let person = {
name:'jike',
init:function(){
//爲body添加一個點擊事件,看看這個點擊後的this屬性有什麼不一樣
document.body.onclick = ()=>{
alert(this.name);//?? this在瀏覽器默認是調用時的對象,可變的?
}
}
}
person.init();
複製代碼
上例中,init是function,以person.init調用,其內部this就是person自己,而onclick回調是箭頭函數,
其內部的this,就是父做用域的this,就是person,能獲得name。
let person = {
name:'jike',
init:()=>{
//爲body添加一個點擊事件,看看這個點擊後的this屬性有什麼不一樣
document.body.onclick = ()=>{
alert(this.name);//?? this在瀏覽器默認是調用時的對象,可變的?
}
}
}
person.init();
複製代碼
上例中,init爲箭頭函數,其內部的this爲全局window,onclick的this也就是init函數的this,也是window,
獲得的this.name就爲undefined。
(2). 箭頭函數不能做爲構造函數,不能使用new
//構造函數以下:
function Person(p){
this.name = p.name;
}
//若是用箭頭函數做爲構造函數,則以下
var Person = (p) => {
this.name = p.name;
}
複製代碼
因爲this必須是對象實例,而箭頭函數是沒有實例的,此處的this指向別處,不能產生person實例,自相矛盾。
(3). 箭頭函數沒有arguments,caller,callee
箭頭函數自己沒有arguments,若是箭頭函數在一個function內部,它會將外部函數的arguments拿過來使用。
箭頭函數中要想接收不定參數,應該使用rest參數...解決。
let B = (b)=>{
console.log(arguments);
}
B(2,92,32,32); // Uncaught ReferenceError: arguments is not defined
let C = (...c) => {
console.log(c);
}
C(3,82,32,11323); // [3, 82, 32, 11323]
複製代碼
(4). 箭頭函數經過call和apply調用,不會改變this指向,只會傳入參數
let obj2 = {
a: 10,
b: function(n) {
let f = (n) => n + this.a;
return f(n);
},
c: function(n) {
let f = (n) => n + this.a;
let m = {
a: 20
};
return f.call(m,n);
}
};
console.log(obj2.b(1)); // 11
console.log(obj2.c(1)); // 11
複製代碼
(5). 箭頭函數沒有原型屬性
var a = ()=>{
return 1;
}
function b(){
return 2;
}
console.log(a.prototype); // undefined
console.log(b.prototype); // {constructor: ƒ}
複製代碼
(6). 箭頭函數不能做爲Generator函數,不能使用yield關鍵字
(7). 箭頭函數返回對象時,要加一個小括號
var func = () => ({ foo: 1 }); //正確
var func = () => { foo: 1 }; //錯誤
複製代碼
(8). 箭頭函數在ES6 class中聲明的方法爲實例方法,不是原型方法
//deom1
class Super{
sayName(){
//do some thing here
}
}
//經過Super.prototype能夠訪問到sayName方法,這種形式定義的方法,都是定義在prototype上
var a = new Super()
var b = new Super()
a.sayName === b.sayName //true
//全部實例化以後的對象共享prototypy上的sayName方法
//demo2
class Super{
sayName =()=>{
//do some thing here
}
}
//經過Super.prototype訪問不到sayName方法,該方法沒有定義在prototype上
var a = new Super()
var b = new Super()
a.sayName === b.sayName //false
//實例化以後的對象各自擁有本身的sayName方法,比demo1須要更多的內存空間
複製代碼
所以,在class中儘可能少用箭頭函數聲明方法。
(9). 多重箭頭函數就是一個高階函數,至關於內嵌函數
const add = x => y => y + x;
//至關於
function add(x){
return function(y){
return y + x;
};
}
複製代碼
(10). 箭頭函數常見錯誤
let a = {
foo: 1,
bar: () => console.log(this.foo)
}
a.bar() //undefined
複製代碼
bar函數中的this指向父做用域,而a對象沒有做用域,所以this不是a,打印結果爲undefined
function A() {
this.foo = 1
}
A.prototype.bar = () => console.log(this.foo)
let a = new A()
a.bar() //undefined
複製代碼
原型上使用箭頭函數,this指向是其父做用域,並非對象a,所以得不到預期結果
在談for...of前;咱們來比較for和for...in的弊端;
1:其中for 循環的最大缺點是須要跟蹤計數器和退出條件。老生常談就很少說了。
2:for...in 它消除了跟蹤技術器和退出條件,可是依然須要使用 index 來訪問數組的值,讓人感受有點不人性化,此外,當你須要向數組中添加額外的方法(或另外一個對象)時,for...in 循環會帶來很大的麻煩。由於 for...in 循環循環訪問全部可枚舉的屬性,意味着若是向數組的原型中添加任何其餘屬性,這些屬性也會出如今循環中.
Array.prototype.decimalfy = function() {
for (let i = 0; i < this.length; i++) {
this[i] = this[i].toFixed(2);
}
};
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const index in digits) {
console.log(digits[index]);
}
複製代碼
Prints:
0
1
2
3
4
5
6
7
8
9
function() {
for (let i = 0; i < this.length; i++) {
this[i] = this[i].toFixed(2);
}
}
複製代碼
for...of 循環用於循環訪問任何可迭代的數據類型。
for...of 循環的編寫方式和 for...in 循環的基本同樣,只是將 in 替換爲 of,能夠忽略索引。簡潔方便;
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const digit of digits) {
console.log(digit);
}
複製代碼
ES6以前已經出現了js模塊加載的方案,最主要的是CommonJS和AMD規範。commonjs主要應用於服務器,實現同步加載,如nodejs。AMD規範應用於瀏覽器,如requirejs,爲異步加載。同時還有CMD規範,爲同步加載方案如seaJS。
ES6在語言規格的層面上,實現了模塊功能,並且實現得至關簡單,徹底能夠取代現有的CommonJS和AMD規範,成爲瀏覽器和服務器通用的模塊解決方案。
ES6模塊主要有兩個功能:export和import
export用於對外輸出本模塊(一個文件能夠理解爲一個模塊)變量的接口
import用於在一個模塊中加載另外一個含有export接口的模塊。
也就是說使用export命令定義了模塊的對外接口之後,其餘JS文件就能夠經過import命令加載這個模塊(文件)。以下圖(假設a和b文件在同一目錄下)
// a.js
var sex="boy";
var echo=function(value){
  console.log(value)
}
export {sex,echo}
//經過向大括號中添加sex,echo變量而且export輸出,就能夠將對應變量值以sex、echo變量標識符形式暴露給其餘文件而被讀取到
//不能寫成export sex這樣的方式,若是這樣就至關於export "boy",外部文件就獲取不到該文件的內部變量sex的值,由於沒有對外輸出變量接口,只是輸出的字符串。
複製代碼
// b.js
經過import獲取a.js文件的內部變量,{}括號內的變量來自於a.js文件export出的變量標識符。
import {sex,echo} from "./a.js"
console.log(sex) // boy
echo(sex) // boy
複製代碼
a.js文件也能夠按以下export語法寫,但不如上邊直觀,不太推薦。
// a.js
export var sex="boy";
export var echo=function(value){
  console.log(value)
}
//由於function echo(){}等價於 var echo=function(){}因此也能夠寫成
export function echo(value){
   console.log(value)
}
複製代碼
以上是export與module的基本用法,再進行拓展學習
前面的例子能夠看出,b.js使用import命令的時候,用戶須要知道a.js所暴露出的變量標識符,不然沒法加載。可使用export default命令,爲模塊指定默認輸出,這樣就不須要知道所要加載模塊的變量名。
//a.js
var sex="boy";
export default sex(sex不能加大括號)
//本來直接export sex外部是沒法識別的,加上default就能夠了.可是一個文件內最多隻能有一個export default。
其實此處至關於爲sex變量值"boy"起了一個系統默認的變量名default,天然default只能有一個值,因此一個文件內不能有多個export default。
複製代碼
// b.js
本質上,a.js文件的export default輸出一個叫作default的變量,而後系統容許你爲它取任意名字。因此能夠爲import的模塊起任何變量名,且不須要用大括號包含
import any from "./a.js"
import any12 from "./a.js"
console.log(any,any12) // boy,boy
複製代碼
Math.trunc() - 取整,去掉一個數的小數部分
console.log(Math.trunc(3.5)); // 3
console.log(Math.trunc(-3.5)); // -3
// 對於非數值,Math.trunc() 內部使用Number()方法先將其轉化爲數值
console.log(Math.trunc('123.456')); //123
console.log(Math.trunc(NaN)); // NaN
console.log(Math.trunc('abc')); // NaN
console.log(Math.trunc()); // NaN
複製代碼
Math.sign()
// Math.sign() // 用來判斷一個數是正數仍是負數 0,對於非數值,先轉化,後判斷
/*
* 參數爲正數 返回+1
* 參數爲負數,返回-1
* 參數爲0,返回0
* 參數爲-0,返回-0
* 參數爲NaN,返回NaN
* */
複製代碼
Math.cbrt()
// 計算立方根
console.log(Math.cbrt(-8)); // -2
複製代碼
Math.hypot()
// 返回全部參數的平方和的平凡根
console.log(Math.hypot(3,4,5)); // 7.0710678118654755
複製代碼
Array.prototype.includes方法返回一個布爾值,表示某個數組是否包含給定的值,與字符串的includes方法相似。
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // true
[1, 2, NaN].includes(NaN) // true
複製代碼
該方法的第二個參數表示搜索的起始位置,默認爲0。若是第二個參數爲負數,則表示倒數的位置,若是這時它大於數組長度(好比第二個參數爲-4, 但數組長度爲3),則會重置爲0開始。
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
複製代碼
沒有該方法以前,咱們一般使用數組的indexOf方法,檢查是否包含某個值。
if (arr.indexOf(el) !== -1) {
// ...
}
複製代碼
indexOf方法有兩個缺點,一是不夠語義化,它的含義是找到參數值的第一個出現位置,因此要去比較是否不等於-1,表達起來不夠直觀。二是,它內部使用嚴格相等運算符進行判斷,這會致使對NaN的誤判。
[NaN].indexOf(NaN) // -1
複製代碼
includes使用的是不同的判斷算法,就沒有這個問題。
NaN].includes(NaN) // true
複製代碼
下面代碼用來檢查當前環境是否支持該方法,若是不支持,部署一個簡易的替代版本。
const contains = (() => Array.prototype.includes
? (arr, value) => arr.includes(value)
:(arr, value) => arr.some(el => el === value)
)()
複製代碼
另外,Map和Set數據結構有一個has方法須要注意與includes區分。
-Map結構的has方法,是用來查找鍵名的,好比Map.prototype.has(key), WeakMap.prototype.has(key), Reflect.has(target, propertyKey)
-Set結構的has方法,是用來查找值的,好比Set.prototype.has(value), WeakSet.prototype.has(value)
這裏貼一下解決地獄回調方法的連接.
Promise,async/await解決回調地獄: www.cnblogs.com/fanghl/p/94…
基本用法:
let a = 3 ** 2 ; // 9
// 等效於
Math.pow(3, 2); // 9
複製代碼
**
是一個運算符,也能夠知足相似假髮的操做,以下:
let a = 3;
a **= 2; // 9
複製代碼
【複習資料】ES6/ES7/ES8/ES9資料整理(我的整理): juejin.im/post/5c02b1…
ES六、ES7特性回顧(爲本身以前的周馬觀花買單,面試有問哦): blog.csdn.net/zww19847743…
ES六、ES7新特性: my.oschina.net/kimyeongnam…
es6 對象深拷貝和淺拷貝: blog.csdn.net/Dong8508/ar…
ES6 Set進行數組去重: blog.csdn.net/qq_33350717…
Promise 簡介和使用: www.jianshu.com/p/113d426e8…
淺談async/await: www.jianshu.com/p/1e75bd387…
ES6箭頭函數總結: www.cnblogs.com/mengff/p/96…
ES6:循環 for ...of..: blog.csdn.net/daikunfa/ar…
ES6模塊的import和export用法總結: blog.csdn.net/qq_20069429…
ES6 之 Math對象的擴展: www.cnblogs.com/houfee/p/10…
Promise,async/await解決回調地獄: www.cnblogs.com/fanghl/p/94…
雖然這篇文章,大部分都是借鑑別人的文章內容,可是大部分都是我在看過以後以爲不錯的才放到上面講的,雖然乾貨比較少,本身總結的東西比較少吧,可是你們仍是能夠看看其推薦的文章,這樣應該能理解更深入,在這裏仍是請各位大大在評論區批評指正,最後大概下次會寫與vue相關的面試題之類的,盡請期待.