ES6相關(guān)

1鱼填、es5和es6的區(qū)別,說(shuō)一下你所知道的es6

ECMAScript5联逻,即ES5,是ECMAScript的第五次修訂检痰,于2009年完成標(biāo)準(zhǔn)化

  ECMAScript6包归,即ES6,是ECMAScript的第六次修訂铅歼,于2015年完成公壤,也稱ES2015

  ES6是繼ES5之后的一次改進(jìn),相對(duì)于ES5更加簡(jiǎn)潔椎椰,提高了開發(fā)效率

  ES6新增的一些特性:

1)let聲明變量和const聲明常量厦幅,兩個(gè)都有塊級(jí)作用域

ES5中是沒有塊級(jí)作用域的,并且var有變量提升慨飘,在let中确憨,使用的變量一定要進(jìn)行聲明

2)箭頭函數(shù)

ES6中的函數(shù)定義不再使用關(guān)鍵字function(),而是利用了()=>來(lái)進(jìn)行定義

3)模板字符串

模板字符串是增強(qiáng)版的字符串瓤的,用反引號(hào)(`)標(biāo)識(shí)休弃,可以當(dāng)作普通字符串使用,也可以用來(lái)定義多行字符串

4)解構(gòu)賦值

ES6 允許按照一定模式圈膏,從數(shù)組和對(duì)象中提取值塔猾,對(duì)變量進(jìn)行賦值

5)for of循環(huán)

for...of循環(huán)可以遍歷數(shù)組、Set和Map結(jié)構(gòu)稽坤、某些類似數(shù)組的對(duì)象丈甸、對(duì)象,以及字符串

6)import尿褪、export導(dǎo)入導(dǎo)出

ES6標(biāo)準(zhǔn)中睦擂,Js原生支持模塊(module)。將JS代碼分割成不同功能的小塊進(jìn)行模塊化茫多,將不同功能的代碼分別寫在不同文件中祈匙,各模塊只需導(dǎo)出公共接口部分,然后通過(guò)模塊的導(dǎo)入的方式可以在其他地方使用

7)set數(shù)據(jù)結(jié)構(gòu)

Set數(shù)據(jù)結(jié)構(gòu),類似數(shù)組夺欲。所有的數(shù)據(jù)都是唯一的跪帝,沒有重復(fù)的值。它本身是一個(gè)構(gòu)造函數(shù)

8)... 展開運(yùn)算符

可以將數(shù)組或?qū)ο罄锩娴闹嫡归_些阅;還可以將多個(gè)值收集為一個(gè)變量

9)修飾器 @

decorator是一個(gè)函數(shù)伞剑,用來(lái)修改類甚至于是方法的行為。修飾器本質(zhì)就是編譯時(shí)執(zhí)行的函數(shù)

10)class 類的繼承

ES6中不再像ES5一樣使用原型鏈實(shí)現(xiàn)繼承市埋,而是引入Class這個(gè)概念

11)async黎泣、await

使用 async/await, 搭配promise,可以通過(guò)編寫形似同步的代碼來(lái)處理異步流程, 提高代碼的簡(jiǎn)潔性和可讀性

async 用于申明一個(gè) function 是異步的,而 await 用于等待一個(gè)異步方法執(zhí)行完成

12)promise

Promise是異步編程的一種解決方案缤谎,比傳統(tǒng)的解決方案(回調(diào)函數(shù)和事件)更合理抒倚、強(qiáng)大

13)Symbol

Symbol是一種基本類型。Symbol 通過(guò)調(diào)用symbol函數(shù)產(chǎn)生坷澡,它接收一個(gè)可選的名字參數(shù)托呕,該函數(shù)返回的symbol是唯一的

14)Proxy代理

使用代理(Proxy)監(jiān)聽對(duì)象的操作,然后可以做一些相應(yīng)事情


2频敛、var项郊、let、const之間的區(qū)別

?????? var聲明變量可以重復(fù)聲明斟赚,而let不可以重復(fù)聲明

?????? let 和 const 定義的變量不會(huì)出現(xiàn)變量提升着降,而 var 定義的變量會(huì)提升。

  var是不受限于塊級(jí)的拗军,而let是受限于塊級(jí)

  var會(huì)與window相映射(會(huì)掛一個(gè)屬性)任洞,而let不與window相映射

  var可以在聲明的上面訪問變量,而let有暫存死區(qū)食绿,在聲明的上面訪問變量會(huì)報(bào)錯(cuò)

  const聲明之后必須賦值侈咕,否則會(huì)報(bào)錯(cuò)

  const定義不可變的量,改變了就會(huì)報(bào)錯(cuò)

  const和let一樣不會(huì)與window相映射器紧、支持塊級(jí)作用域耀销、在聲明的上面訪問變量會(huì)報(bào)錯(cuò)


3、使用箭頭函數(shù)應(yīng)注意什么铲汪?

 ⌒芪尽(1)用了箭頭函數(shù),this就不是指向window掌腰,而是父級(jí)(指向是可變的)

 ≌ (2)不能夠使用arguments對(duì)象

  (3)不能用作構(gòu)造函數(shù)齿梁,這就是說(shuō)不能夠使用new命令催植,否則會(huì)拋出一個(gè)錯(cuò)誤

 “褂肌(4)不可以使用yield命令,因此箭頭函數(shù)不能用作 Generator 函數(shù)


4创南、ES6的模板字符串有哪些新特性伦忠?并實(shí)現(xiàn)一個(gè)類模板字符串的功能

  基本的字符串格式化。將表達(dá)式嵌入字符串中進(jìn)行拼接稿辙。用${}來(lái)界定

  在ES5時(shí)我們通過(guò)反斜杠()來(lái)做多行字符串或者字符串一行行拼接昆码。ES6反引號(hào)(``)就能解決

??????? 類模板字符串的功能

??????? let name = 'web';

? ? ? ? let age = 10;

? ? ? ? let str = '你好,${name} 已經(jīng) ${age}歲了'? ? ? ? str = str.replace(/\$\{([^}]*)\}/g,function(){

? ? ? ? ? ? returneval(arguments[1]);

? ? ? ? })

? ? ? ? console.log(str);//你好邻储,web 已經(jīng) 10歲了? ?


5赋咽、介紹下 Set、Map的區(qū)別吨娜?

  應(yīng)用場(chǎng)景Set用于數(shù)據(jù)重組脓匿,Map用于數(shù)據(jù)儲(chǔ)存

  Set:

(1)成員不能重復(fù)

(2)只有鍵值沒有鍵名,類似數(shù)組

(3)可以遍歷宦赠,方法有add, delete,has

  Map:

(1)本質(zhì)上是健值對(duì)的集合亦镶,類似集合

(2)可以遍歷,可以跟各種數(shù)據(jù)格式轉(zhuǎn)換


6袱瓮、ECMAScript 6 怎么寫 class ,為何會(huì)出現(xiàn) class爱咬?

  ES6的class可以看作是一個(gè)語(yǔ)法糖尺借,它的絕大部分功能ES5都可以做到,新的class寫法只是讓對(duì)象原型的寫法更加清晰精拟、更像面向?qū)ο缶幊痰恼Z(yǔ)法

//定義類class Point {

? constructor(x,y) {

? ? ? //構(gòu)造方法this.x = x;//this關(guān)鍵字代表實(shí)例對(duì)象this.y = y;

? } toString() {

? ? ? return'(' +this.x + ',' +this.y + ')';

? }

}


7燎斩、Promise構(gòu)造函數(shù)是同步執(zhí)行還是異步執(zhí)行,那么 then 方法呢蜂绎?

  promise構(gòu)造函數(shù)是同步執(zhí)行的栅表,then方法是異步執(zhí)行的


8、setTimeout师枣、Promise怪瓶、Async/Await 的區(qū)別

?  事件循環(huán)中分為宏任務(wù)隊(duì)列和微任務(wù)隊(duì)列

  其中setTimeout的回調(diào)函數(shù)放到宏任務(wù)隊(duì)列里,等到執(zhí)行棧清空以后執(zhí)行

  promise.then里的回調(diào)函數(shù)會(huì)放到相應(yīng)宏任務(wù)的微任務(wù)隊(duì)列里践美,等宏任務(wù)里面的同步代碼執(zhí)行完再執(zhí)行

  async函數(shù)表示函數(shù)里面可能會(huì)有異步方法洗贰,await后面跟一個(gè)表達(dá)式

  async方法執(zhí)行時(shí),遇到await會(huì)立即執(zhí)行表達(dá)式陨倡,然后把表達(dá)式后面的代碼放到微任務(wù)隊(duì)列里敛滋,讓出執(zhí)行棧讓同步代碼先執(zhí)行


9、promise有幾種狀態(tài)兴革,什么時(shí)候會(huì)進(jìn)入catch绎晃?

  三個(gè)狀態(tài):pending蜜唾、fulfilled、reject

  兩個(gè)過(guò)程:padding ->?fulfilled庶艾、padding ->?rejected

  當(dāng)pending為rejectd時(shí)袁余,會(huì)進(jìn)入catch


10、下面的輸出結(jié)果是多少

const promise =newPromise((resolve, reject) => {

? ? console.log(1);

? ? resolve();

? ? console.log(2);

})

promise.then(() => {

? ? console.log(3);

})

console.log(4);//1243

Promise 新建后立即執(zhí)行落竹,所以會(huì)先輸出 1泌霍,2,而Promise.then()內(nèi)部的代碼在 當(dāng)次 事件循環(huán)的 結(jié)尾 立刻執(zhí)行 述召,所以會(huì)繼續(xù)輸出4朱转,最后輸出3


11、使用結(jié)構(gòu)賦值积暖,實(shí)現(xiàn)兩個(gè)變量的值的交換

let a = 1;let b = 2;

[a,b] = [b,a];


12藤为、設(shè)計(jì)一個(gè)對(duì)象,鍵名的類型至少包含一個(gè)symbol類型夺刑,并且實(shí)現(xiàn)遍歷所有key

let name = Symbol('name');

let product = {

? ? [name]:"洗衣機(jī)",? ?

? ? "price":799? };

? Reflect.ownKeys(product);


13缅疟、下面Set結(jié)構(gòu),打印出的size值是多少

let s =new Set();

s.add([1]);

s.add([1]);console.log(s.size);

答案:2

兩個(gè)數(shù)組[1]并不是同一個(gè)值遍愿,它們分別定義的數(shù)組存淫,在內(nèi)存中分別對(duì)應(yīng)著不同的存儲(chǔ)地址,因此并不是相同的值

都能存儲(chǔ)到Set結(jié)構(gòu)中沼填,所以size為2


14桅咆、Promise 中reject 和 catch 處理上有什么區(qū)別

  reject 是用來(lái)拋出異常,catch 是用來(lái)處理異常

  reject 是 Promise 的方法坞笙,而 catch 是 Promise 實(shí)例的方法

  reject后的東西岩饼,一定會(huì)進(jìn)入then中的第二個(gè)回調(diào),如果then中沒有寫第二個(gè)回調(diào)薛夜,則進(jìn)入catch

  網(wǎng)絡(luò)異常(比如斷網(wǎng))籍茧,會(huì)直接進(jìn)入catch而不會(huì)進(jìn)入then的第二個(gè)回調(diào)


15、使用class 手寫一個(gè)promise

//創(chuàng)建一個(gè)Promise的類

? class Promise{

? ? constructor(executer){//構(gòu)造函數(shù)constructor里面是個(gè)執(zhí)行器

? ? ? this.status = 'pending';//默認(rèn)的狀態(tài) pending

? ? ? this.value = undefined//成功的值默認(rèn)undefined

? ? ? this.reason = undefined//失敗的值默認(rèn)undefined

? ? ? //狀態(tài)只有在pending時(shí)候才能改變

? ? ? let resolveFn = value =>{

? ? ? ? //判斷只有等待時(shí)才能resolve成功

? ? ? ? if(this.status == pending){

? ? ? ? ? this.status = 'resolve';

? ? ? ? ? this.value = value;

? ? ? ? }

? ? ? }

? ? ? //判斷只有等待時(shí)才能reject失敗

? ? ? let rejectFn = reason =>{

? ? ? ? if(this.status == pending){

? ? ? ? ? this.status = 'reject';

? ? ? ? ? this.reason = reason;

? ? ? ? }

? ? ? }? ?

? ? ? try{

? ? ? ? //把resolve和reject兩個(gè)函數(shù)傳給執(zhí)行器executer

? ? ? ? executer(resolve,reject);

? ? ? }catch(e){

? ? ? ? reject(e);//失敗的話進(jìn)catch

? ? ? }

? ? }

? ? then(onFufilled,onReject){

? ? ? //如果狀態(tài)成功調(diào)用onFufilled

? ? ? if(this.status = 'resolve'){

? ? ? ? onFufilled(this.value);

? ? ? }

? ? ? //如果狀態(tài)失敗調(diào)用onReject

? ? ? if(this.status = 'reject'){

? ? ? ? onReject(this.reason);

? ? ? }

? ? }

? }


16梯澜、如何使用Set去重

let arr = [12,43,23,43,68,12];

let item = [...new Set(arr)];

console.log(item);//[12, 43, 23, 68]


17寞冯、將下面for循環(huán)改成for of形式

let arr = [11,22,33,44,55];

let sum = 0;

for(let i=0;i<arr.length;i++){

? ? sum += arr[i];

}

答案:

let arr = [11,22,33,44,55];

let sum = 0;

for(value of arr){

? ? sum += value;

}


18、理解 async/await以及對(duì)Generator的優(yōu)勢(shì)

  ?async await 是用來(lái)解決異步的晚伙,async函數(shù)是Generator函數(shù)的語(yǔ)法糖

  使用關(guān)鍵字async來(lái)表示简十,在函數(shù)內(nèi)部使用 await 來(lái)表示異步

  async函數(shù)返回一個(gè) Promise 對(duì)象,可以使用then方法添加回調(diào)函數(shù)

  當(dāng)函數(shù)執(zhí)行的時(shí)候撬腾,一旦遇到await就會(huì)先返回螟蝙,等到異步操作完成,再接著執(zhí)行函數(shù)體內(nèi)后面的語(yǔ)句

  async較Generator的優(yōu)勢(shì):

 ∶裆怠(1)內(nèi)置執(zhí)行器胰默。Generator 函數(shù)的執(zhí)行必須依靠執(zhí)行器场斑,而 Aysnc 函數(shù)自帶執(zhí)行器,調(diào)用方式跟普通函數(shù)的調(diào)用一樣

 ∏J稹(2)更好的語(yǔ)義漏隐。async 和 await 相較于 * 和 yield 更加語(yǔ)義化

  (3)更廣的適用性奴迅。yield命令后面只能是 Thunk 函數(shù)或 Promise對(duì)象青责,async函數(shù)的await后面可以是Promise也可以是原始類型的值

  (4)返回值是 Promise取具。async 函數(shù)返回的是 Promise 對(duì)象脖隶,比Generator函數(shù)返回的Iterator對(duì)象方便,可以直接使用 then() 方法進(jìn)行調(diào)用


19暇检、forEach产阱、for in、for of三者區(qū)別

  forEach更多的用來(lái)遍歷數(shù)組

  for in 一般常用來(lái)遍歷對(duì)象或json

  for of數(shù)組對(duì)象都可以遍歷块仆,遍歷對(duì)象需要通過(guò)和Object.keys()

  for in循環(huán)出的是key构蹬,for of循環(huán)出的是value


20、說(shuō)一下es6的導(dǎo)入導(dǎo)出模塊

導(dǎo)入通過(guò)import關(guān)鍵字

// 只導(dǎo)入一個(gè)

import {sum} from "./example.js"

// 導(dǎo)入多個(gè)

import {sum,multiply,time} from "./exportExample.js"

// 導(dǎo)入一整個(gè)模塊

import * as example from "./exportExample.js"

導(dǎo)出通過(guò)export關(guān)鍵字

//可以將export放在任何變量,函數(shù)或類聲明的前面

export var firstName = 'Michael';

export var lastName = 'Jackson';

export var year = 1958;

//也可以使用大括號(hào)指定所要輸出的一組變量

var firstName = 'Michael';

var lastName = 'Jackson';

var year = 1958;

export {firstName, lastName, year};

//使用export default時(shí)悔据,對(duì)應(yīng)的import語(yǔ)句不需要使用大括號(hào)

let bosh = function crs(){}

export default bosh;

import crc from 'crc';

//不使用export default時(shí)庄敛,對(duì)應(yīng)的import語(yǔ)句需要使用大括號(hào)

let bosh = function crs(){}

export bosh;

import {crc} from 'crc';


21、ES6 深拷貝

arr=[1,2,3,4]

let […arr1]=arr;

arr.splice(0,1);

console.log(arr1); //[1,2,3,4]


22科汗、數(shù)組去重的幾個(gè)方法:

?? 1.兼容Set 和?Array.from() 的環(huán)境下:

??? let orderedArray = Array.from(new Set(myArray));

??? var myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd'];

??? var myOrderedArray = myArray.reduce(function (accumulator, currentValue) {

? ? ?? if (accumulator.indexOf(currentValue) === -1) {

? ? ? ? ?? accumulator.push(currentValue);

?????? }

? ? ? return accumulator

?? }, [])

?? console.log(myOrderedArray);

? 2.利用arr.reduce()去重

???? //簡(jiǎn)單去重

??? let arr = [1,2,3,4,4,1]

??? let newArr = arr.reduce((pre,cur)=>{

? ????? if(!pre.includes(cur)){

? ? ????? return pre.concat(cur)

? ????? }else{

? ? ????? return pre

? ????? }

??? },[])

??? console.log(newArr);// [1, 2, 3, 4]

??? 3.擴(kuò)展運(yùn)算符數(shù)組去重

??? var arr = [1,2,3,4,5,2,3,1];

??? var set =?new Set(arr);

??? var newArr = [...set ];

??? 4.去重?cái)?shù)組并排序

??? let arr = [1,2,1,2,3,5,4,5,3,4,4,4,4];

??? let result = arr.sort().reduce((init, current) => {

? ????? if(init.length === 0 || init[init.length-1] !== current) {

? ? ? ????? init.push(current);

? ????? }

? ????? return init;

??? }, []);

??? console.log(result); //[1,2,3,4,5]


23铐姚、arr.reduce()方法:

??? 1.數(shù)組求和,求乘積

??? var? arr = [1, 2, 3, 4];

??? var sum = arr.reduce((x,y)=>x+y)

??? var mul = arr.reduce((x,y)=>x*y)

??? console.log( sum ); //求和肛捍,10

??? console.log( mul ); //求乘積,24

??? 2.計(jì)算數(shù)組中每個(gè)元素出現(xiàn)的次數(shù)

???? let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

??? let nameNum = names.reduce((pre,cur)=>{

????? if(cur in pre){

? ????? pre[cur]++

????? }else{

? ????? pre[cur] = 1

????? }

? ? ? return pre

??? },{})

??? console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}

??? 3.將二維數(shù)組轉(zhuǎn)化為一維

???? let arr = [[0, 1], [2, 3], [4, 5]]

??? let newArr = arr.reduce((pre,cur)=>{

? ????? return pre.concat(cur)

??? },[])

??? console.log(newArr); // [0, 1, 2, 3, 4, 5]

??? 4.將多維數(shù)組轉(zhuǎn)化為一維

??? let arr = [[0, 1], [2, 3], [4,[5,6,7]]]

??? const newArr = function(arr){

? ? ?? return arr.reduce((pre,cur)=>pre.concat(Array.isArray(cur)?newArr(cur):cur),[])

??? }

??? console.log(newArr(arr)); //[0, 1, 2, 3, 4, 5, 6, 7]

??? 5.對(duì)象里的屬性求和

??? var result = [

? ? ? ? {

? ? ? ????? subject: 'math',

? ? ? ? ? ? score: 10

? ????? },

? ? ? ? {

? ? ? ? ? ? subject: 'chinese',

? ? ? ? ? ? score: 20

? ????? },

? ? ? ? {

? ? ? ? ? ? subject: 'english',

? ? ? ? ? ? score: 30

? ? ? ? }

??? ];

??? var sum = result.reduce(function(prev, cur) {

? ? ? ? return cur.score + prev;

??? }, 0);

??? console.log(sum) //60

??? 6.找出一個(gè)數(shù)組中的最大/最小值

??? var numbers = [5, 6, 2, 3, 7];

??? /* 應(yīng)用(apply) Math.min/Math.max 內(nèi)置函數(shù)完成 */

??? var max = Math.max.apply(null, numbers); /* 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..) */

??? var min = Math.min.apply(null, numbers)

??? /* 應(yīng)用最新的擴(kuò)展語(yǔ)句spread operator完成 */

??? var arr = [1, 2, 3];

??? var max = Math.max(...arr);

??? var min = Math.min(...arr);

??? /* 應(yīng)用簡(jiǎn)單循環(huán)完成? */

??? max = -Infinity, min = +Infinity;

??? for (var i = 0; i < numbers.length; i++) {

????? if (numbers[i] > max)

? ????? max = numbers[i];

????? if (numbers[i] < min)

? ????? min = numbers[i];

??? }

??? /**在b中找出包含a的所有數(shù)據(jù)*/

??? a = [1,2,3];

???? b = [{id:1,name:1},{id:2,name:2}];

??? const c = b.filter(item => a.includes(item.id));

??? console.log(c,’c’)


24之众、ES6 entries()拙毫,keys(),values() 數(shù)組遍歷

ES6提供了entries()棺禾,keys()缀蹄,values()方法返回?cái)?shù)組的遍歷器,對(duì)于遍歷器(Iterator)可以使用for...of進(jìn)行便利膘婶,也可是使用entries()返回的遍歷器Iterator.next()方法進(jìn)行遍歷缺前。

1.使用keys()遍歷。

keys()返回的是數(shù)組元素索引號(hào)的遍歷器悬襟。

const arr1 = ['a', 'b', 'c']

? for (let index of arr1.keys()) {

? ? console.log(index);

? }

可以看到輸出的都是每個(gè)數(shù)組元素的index衅码。

? 0

? 1

? 2

2.使用values()遍歷。

values()返回的是數(shù)組元素值的遍歷器脊岳。

輸出

a

b

c

3.使用entries()遍歷逝段。

配合解構(gòu)使用垛玻,可以拿到元素的index和value。

? for (let [index, val] of arr1.entries()) {

? ? console.log(index, val);

? }

結(jié)果

0 'a'

1 'b'

2 'c'

4.使用Iterator.next()遍歷奶躯。

基于entries()返回的遍歷器帚桩,調(diào)用遍歷器的next()的方法可以獲取每一個(gè)元素的訪問入口,該入口有一個(gè)done屬性可以表明是否便利結(jié)束嘹黔。通過(guò)入口可以拿到value屬性账嚎,其就是元素的索引和值的數(shù)組。

? ? let arrEntries=arr1.entries();

? ? let entry=arrEntries.next();

? ? while(!entry.done){

? ? ? console.log(entry.value);

? ? ? entry=arrEntries.next();

? ? }

結(jié)果

25儡蔓、ES6常用特性

變量定義(let和const,可變與不可變郭蕉,const定義對(duì)象的特殊情況)

解構(gòu)賦值

模板字符串

數(shù)組新API(例:Array.from(),entries(),values(),keys())

箭頭函數(shù)(rest參數(shù),擴(kuò)展運(yùn)算符浙值,::綁定this)

Set和Map數(shù)據(jù)結(jié)構(gòu)(set實(shí)例成員值唯一存儲(chǔ)key值恳不,map實(shí)例存儲(chǔ)鍵值對(duì)(key-value))

Promise對(duì)象(前端異步解決方案進(jìn)化史,generator函數(shù)开呐,async函數(shù))

Class語(yǔ)法糖(super關(guān)鍵字)


26烟勋、es6中的Module

ES6 中模塊化語(yǔ)法更加簡(jiǎn)潔,使用export拋出筐付,使用import from 接收卵惦,

如果只是輸出一個(gè)唯一的對(duì)象,使用export default即可

// 創(chuàng)建 util1.js 文件瓦戚,內(nèi)容如

export default {

? ? a: 100

}

// 創(chuàng)建 index.js 文件沮尿,內(nèi)容如

import obj from './util1.js’

如果想要輸出許多個(gè)對(duì)象,就不能用default了较解,且import時(shí)候要加{...}畜疾,代碼如下

// 創(chuàng)建 util2.js 文件,內(nèi)容如

export function fn1() {

? ? alert('fn1')

}

export function fn2() {

? ? alert('fn2')

}

// 創(chuàng)建 index.js 文件印衔,內(nèi)容如

import { fn1, fn2 } from './util2.js’


27啡捶、ES6 class 和普通構(gòu)造函數(shù)的區(qū)別

class 其實(shí)一直是 JS 的關(guān)鍵字(保留字),但是一直沒有正式使用奸焙,直到 ES6 瞎暑。 ES6 的 class 就是取代之前構(gòu)造函數(shù)初始化對(duì)象的形式,從語(yǔ)法上更加符合面向?qū)ο蟮膶懛?/p>

1. class 是一種新的語(yǔ)法形式与帆,是class Name {...}這種形式了赌,和函數(shù)的寫法完全不一樣

2. 兩者對(duì)比,構(gòu)造函數(shù)函數(shù)體的內(nèi)容要放在 class 中的constructor函數(shù)中玄糟,constructor即構(gòu)造器勿她,初始化實(shí)例時(shí)默認(rèn)執(zhí)行

3. class 中函數(shù)的寫法是add() {...}這種形式,并沒有function關(guān)鍵字

而且使用 class 來(lái)實(shí)現(xiàn)繼承就更加簡(jiǎn)單了

在class中直接extends關(guān)鍵字就可以實(shí)現(xiàn)繼承阵翎,而不像之前的繼承實(shí)現(xiàn)有多種不同的實(shí)現(xiàn)方式嫂拴,在es6中就只有一種

注意以下兩點(diǎn):

使用extends即可實(shí)現(xiàn)繼承播揪,更加符合經(jīng)典面向?qū)ο笳Z(yǔ)言的寫法,如 Java

子類的constructor一定要執(zhí)行super()筒狠,以調(diào)用父類的constructor


28猪狈、ES6 中新增的數(shù)據(jù)類型有哪些?

Set 和 Map 都是 ES6 中新增的數(shù)據(jù)結(jié)構(gòu)辩恼,是對(duì)當(dāng)前 JS 數(shù)組和對(duì)象這兩種重要數(shù)據(jù)結(jié)構(gòu)的擴(kuò)展雇庙。由于是新增的數(shù)據(jù)結(jié)構(gòu)

1. Set 類似于數(shù)組,但數(shù)組可以允許元素重復(fù)灶伊,Set 不允許元素重復(fù)

2. Map 類似于對(duì)象疆前,但普通對(duì)象的 key 必須是字符串或者數(shù)字,而 Map 的 key 可以是任何數(shù)據(jù)類型


29聘萨、箭頭函數(shù)的作用域上下文和 普通函數(shù)作用域上下文 的區(qū)別

箭頭函數(shù)其實(shí)只是一個(gè)密名函數(shù)的語(yǔ)法糖竹椒,區(qū)別在于普通函數(shù)作用域中的this有特定的指向,一般指向window米辐,而箭頭函數(shù)中的this只有一個(gè)指向那就是指當(dāng)前函數(shù)所在的對(duì)象胸完,其實(shí)現(xiàn)原理其實(shí)就是類似于之前編程的時(shí)候在函數(shù)外圍定義that一樣,用了箭頭函數(shù)就不用定義that了直接使用this


30翘贮、es6如何轉(zhuǎn)為es5赊窥?

使用Babel 轉(zhuǎn)碼器,Babel 的配置文件是.babelrc狸页,存放在項(xiàng)目的根目錄下锨能。使用 Babel 的第一步,就是配置這個(gè)文件芍耘。

兼容與優(yōu)化

1址遇,頁(yè)面重構(gòu)怎么操作?

網(wǎng)站重構(gòu):在不改變外部行為的前提下斋竞,簡(jiǎn)化結(jié)構(gòu)倔约、添加可讀性,而在網(wǎng)站前端保持一致的行為窃页。

也就是說(shuō)是在不改變UI的情況下,對(duì)網(wǎng)站進(jìn)行優(yōu)化复濒,在擴(kuò)展的同時(shí)保持一致的UI脖卖。

對(duì)于傳統(tǒng)的網(wǎng)站來(lái)說(shuō)重構(gòu)通常是:

表格(table)布局改為DIV+CSS

使網(wǎng)站前端兼容于現(xiàn)代瀏覽器(針對(duì)于不合規(guī)范的CSS、如對(duì)IE6有效的)

對(duì)于移動(dòng)平臺(tái)的優(yōu)化

針對(duì)于SEO進(jìn)行優(yōu)化

深層次的網(wǎng)站重構(gòu)應(yīng)該考慮的方面

減少代碼間的耦合? ? ? ? ? ? ?

讓代碼保持彈性

嚴(yán)格按規(guī)范編寫代碼

設(shè)計(jì)可擴(kuò)展的API

代替舊有的框架、語(yǔ)言(如VB)

增強(qiáng)用戶體驗(yàn)

通常來(lái)說(shuō)對(duì)于速度的優(yōu)化也包含在重構(gòu)中

壓縮JS、CSS粒氧、image等前端資源(通常是由服務(wù)器來(lái)解決)

程序的性能優(yōu)化(如數(shù)據(jù)讀寫)

采用CDN來(lái)加速資源加載

對(duì)于JS DOM的優(yōu)化

HTTP服務(wù)器的文件緩存


31盹廷、箭頭函數(shù)

1. 箭頭函數(shù)凌简?

2. 箭頭函數(shù)與普通函數(shù)有什么區(qū)別箱硕?

1.箭頭函數(shù)相當(dāng)于匿名函數(shù)杀糯,是不能作為構(gòu)造函數(shù)的碎捺,不能使用new

2.箭頭函數(shù)不綁定arguments,取而代之用rest參數(shù)…解決

3.箭頭函數(shù)會(huì)捕獲其所在上下文的this值勾栗,作為自己的this值惨篱。即箭頭函數(shù)的作用域會(huì)繼承自外圍的作用域。

4.箭頭函數(shù)當(dāng)方法使用的時(shí)候沒有定義this的綁定

obj = {

?a:10,

? b:()=>{

? ? console.log(this.a);//undefined

? ? console.log(this);//window

? },

? c:function(){

? ? return ()=>{

? ? ? console.log(this.a);//10

? ? }

? }

}

obj.b();

obj.c();

b箭頭函數(shù)運(yùn)行時(shí)的外圍環(huán)境是全局作用域围俘,this指向了window

c內(nèi)部返回的箭頭函數(shù)運(yùn)行在c函數(shù)內(nèi)部砸讳,其外圍的作用域是外部函數(shù)的作用域,外部函數(shù)的this值指向調(diào)用它的obj界牡,所以輸出的值是10

5.使用call()和apply()調(diào)用

通過(guò)call()或者apply()調(diào)用一個(gè)函數(shù)時(shí)簿寂,只是傳入?yún)?shù)而已,對(duì)this并沒有影響宿亡。

? ? var obj = {

? ? ? a:10,

? ? ? b:function(n){

? ? ? var f = (v) => v + this.a;

? ? ? var c = {a:20};

? ? ? return f.call(c,n);

? ? }

? ? }

? ? console.log(obj.b(1));//11

? ? 6.箭頭函數(shù)沒有函數(shù)原型

? ? var a = ()=>{

? ? ? return 1;

? ? }

? ? function b(){

? ? ? return 2;

? ? }

? ? console.log(a.prototype);//undefined;

? ? console.log(b.prototype);//object{...}

7.箭頭函數(shù)不能當(dāng)做Generator函數(shù)常遂,不能使用yield關(guān)鍵字

8.箭頭函數(shù)不能換行

var a = ()

? ? ? ? ? => 1;//SyntaxError:Unexpected token


3. 反引號(hào) ` 標(biāo)識(shí)?

用一對(duì)反引號(hào)(`)標(biāo)識(shí)挽荠,它可以當(dāng)作普通字符串使用克胳,也可以用來(lái)定義多行字符串,也可以在字符串中嵌入變量坤按,js表達(dá)式或函數(shù)毯欣,變量、js表達(dá)式或函數(shù)需要寫在${ }中臭脓。

var str = `abc
def
gh`;
console.log(str);
let name = "小明";
function a() {
??? return "ming";
}
console.log(`我的名字叫做${name}酗钞,年齡${17+2}歲,性別${'男'}来累,游戲ID:${a()}`);

4. 使用 ES6 改下面的模板砚作?

? ? let iam? = "我是";

? ? let name = "Oli";

? ? let str? = "大家好," + iam + name + "嘹锁,多指教葫录。";

// let str? =`大家好${iam}${name}多指教。`


5. 屬性簡(jiǎn)寫领猾、方法簡(jiǎn)寫米同?

屬性簡(jiǎn)寫:

使用一個(gè){}將一個(gè)已經(jīng)復(fù)制的變量括起來(lái)就相當(dāng)于是給一個(gè)對(duì)象添加一個(gè)變量名為名字的屬性及一個(gè)變量值為值得屬性值

var foo='hhhhh';

var b={foo};

上面的寫法相當(dāng)于下面的語(yǔ)句

var b={foo:'hhhhh'};

下面打印結(jié)果為鍵值對(duì)對(duì)象。

var [name,age]=['dale',29];

var obj={name,age};

console.log(obj);

var obj={name,age} 這就是對(duì)象中屬性的簡(jiǎn)寫

對(duì)象的簡(jiǎn)寫:

當(dāng)要定義的對(duì)象鍵和對(duì)象名是一樣的是時(shí)候就可以使用簡(jiǎn)寫方式摔竿。

var obj={name:'dale',age:29};

var o={

? name:'liny'

? ,obj

}

console.log(o.obj.name); //dale

函數(shù)屬性的簡(jiǎn)寫

function fun(){

console.log(this.a+this.b);

}

var obj={

a:1,

b:2,

fun:fun

}

obj.fun();

當(dāng)對(duì)象中的屬性和方法名一致時(shí)面粮,就可以使用簡(jiǎn)寫形式fun:fun =》fun

屬性及方法的簡(jiǎn)寫應(yīng)用

在模塊導(dǎo)出函數(shù)時(shí):

以前的寫法

module.exports={

getOne:function(){},

getAll:function(){}

}

現(xiàn)在的寫法:

function getOne(){};

function getAll(){};

module.exports={

getOne,getAll

}

后面寫法更加的簡(jiǎn)潔,把函數(shù)的申明和模塊導(dǎo)出分開继低。


6. JS中for,for...in,for...of以及foreach循環(huán)的用法熬苍?

1.for()循環(huán)
// for循環(huán)的表達(dá)式之間用的是;號(hào)分隔的,千萬(wàn)不要寫成,
for (初始化表達(dá)式1; 判斷表達(dá)式2; 自增表達(dá)式3) {
? // 循環(huán)體4
}

2.for...in索引遍歷
var obj1 = {
??? name:'張三',
??? age : 17,
??? sex : '男',
}
for(var k in obj1){
??? console.log(k);
??? console.log(obj1[k]);
}
注意:使用for …in語(yǔ)法,同樣可以遍歷數(shù)組
注意:如果屬性名或方法名柴底,是一個(gè)變量婿脸,則使用對(duì)象[變量名] 語(yǔ)法

3.for...of值遍歷
//遍歷數(shù)組
var team = ["師父", "大師兄", "二師兄", "沙師弟", "小白龍"];
for(var v of team){
????? console.log(v);
}
//也可以遍歷字符串
var str = "zhangsan";
for(var v of str){
????? console.log(v);
}
注意:不能遍歷對(duì)象

4.數(shù)組.forEach方法
array.forEach(v=>{?
??? console.log(v);?
});
array.forEach(function(v){?
??? console.log(v);?
});

7. 字符串新增方法

1.String.fromCodePoint()

Es6 提供了 String.fromCodePoint() 方法,可以識(shí)別大于 0xFFFF 的字符柄驻,補(bǔ)充了 String.fromCharCode() 方法的不足狐树。

String.fromCodePoint(0x20BB7)

// '告'

String.fromCodePoint(0x78,0x1f680,0x79) === 'x\uD83D\uDE80y'

// true

如果 String.fromCodePoint() 有多個(gè)參數(shù),則會(huì)合成一個(gè)字符串返回凿歼。

注意:fromCodePoint() 方法定義在 String 對(duì)象上褪迟,而 codePointAt() 方法定義在字符串實(shí)例對(duì)象上。

2.String.raw()

Es6 還為原生的 String 對(duì)象聽哦你了 raw 方法答憔。該方法返回一個(gè)斜桿都被轉(zhuǎn)義(即斜杠前面再加一個(gè)斜杠)的字符串味赃。往往用于模板字符串的處理方法。

String.raw'Hi\n${2+3}!';

// 返回 "Hi\\n5!"

如果原字符串的已經(jīng)轉(zhuǎn)義虐拓,那么 String.raw() 會(huì)再次進(jìn)行轉(zhuǎn)義心俗。

String.raw'Hi\\n'

// 返回 "Hi\\\\n"

String.raw() 方法可以作為處理模板字符串的基本方法,它會(huì)將素有變量替換蓉驹,而且對(duì)斜杠進(jìn)行轉(zhuǎn)義城榛,方便下一步作為字符串來(lái)使用。

3.codePointAt()

JavaScript 內(nèi)部态兴,字符以 UTF-16 的格式存儲(chǔ)狠持,每個(gè)字符固定為 2 個(gè)字節(jié)。對(duì)于那些需要 4 個(gè)字節(jié)存儲(chǔ)的字符(Unicode 碼點(diǎn)大于 0xFFFF 的字符),JavaScript 會(huì)認(rèn)為它們是兩個(gè)字符瞻润。

4.normalize()

Es6 提供了字符串實(shí)例的 normalize() 方法用來(lái)將字符的不同表示方法統(tǒng)一為同樣的形式喘垂,這稱為 Unicode 正規(guī)化。

'\u01D1'.normalize() === '\u004f\u030c'.normalize()

// true

5.includes() startsWith() endsWith()

傳統(tǒng)上绍撞,JavaScript 只有 indexOf 方法正勒,可以用來(lái)確定一個(gè)字符串是否包含在另一個(gè)字符串中。Es 6 又提供了三種新的方法傻铣。

includes():返回布爾值章贞,表示是否找到了參數(shù)字符串

startsWith():返回布爾值,表示參數(shù)字符串是否在原字符串的頭部

endsWith():返回布爾值非洲,表示參數(shù)字符串是否在原字符串的尾部

let str = 'hello world !';

s.startsWith('Hello') // true

s.endsWith('!') // true

s.includes('o') // true

這三個(gè)方法都支持第二個(gè)參數(shù)鸭限,表示搜索的位置。

let s = 'Hello world!';

s.startsWith('world',6)// true

s.endsWith('Hello',5)// true

s.includes('Hello',6)// false

從上面我們可以看出來(lái)两踏,使用第二個(gè)參數(shù)的時(shí)候败京,endsWith 的行為與其他兩個(gè)方法有所不同,它針對(duì)前 n 個(gè)字符缆瓣,而其他兩個(gè)方法喧枷,針對(duì)從第 n 個(gè)位置知道字符串結(jié)束虹统。

6.repeat()

repeat() 方法返回一個(gè)新的字符串弓坞,表示將原字符串重復(fù) n 次隧甚。

'x'.repeat(3) // "xxx"

'haha'.repeat(0) // "" (空字符串。)

如果是小數(shù)渡冻,則會(huì)取整戚扳。

'na'.repeat(2.9);// "nana"

如果參數(shù)是負(fù)數(shù)或者 Infinity族吻,會(huì)報(bào)錯(cuò)帽借。

'str'.repeat(Infinity)

// RangeError

'str'.repeat(-1)

// RangError

但是如果是參數(shù)是 0 到 -1 之間的小數(shù),則等同于 0 ,這是因?yàn)闀?huì)先進(jìn)行取整運(yùn)算超歌。

參數(shù) NaN 等同于 0砍艾。

如果 repeat() 參數(shù)是字符串,則會(huì)先轉(zhuǎn)成數(shù)字

'str'.repeat('nan'); // ""

'str'.repeat('3'); // "nanana"

7.padStart() padEnd()

Es2017 引入了字符串補(bǔ)全長(zhǎng)度的功能巍举,如果某個(gè)字符串不夠指定長(zhǎng)度脆荷,會(huì)在頭部或尾部補(bǔ)全。padStart() 用于頭部補(bǔ)全懊悯,padEnd() 用于尾部補(bǔ)全蜓谋。

'x'.padStart(5, 'st') // 'ststx'

'x'.padStart(4, 'st') // 'stsx'

padStart() 和 padEnd() 一共接受兩個(gè)參數(shù),第一是字符串補(bǔ)全生效的最大長(zhǎng)度炭分,第二個(gè)參數(shù)是用來(lái)補(bǔ)全的字符串桃焕。

如果原字符串的長(zhǎng)度,等于或大于最大長(zhǎng)度捧毛,則字符串補(bǔ)全不生效观堂,返回原字符串。

'xxx'.padStart(2,'s'); // 'xxx'

如果用來(lái)補(bǔ)全的字符串與原字符串岖妄,兩者的長(zhǎng)度之和超過(guò)了最大長(zhǎng)度型将,則會(huì)截取超出位數(shù)的補(bǔ)全字符串。

'abc'.padStart(10,'123456789');

// '1234567abc'

如果省略掉第二個(gè)參數(shù)荐虐,默認(rèn)使用空格補(bǔ)全長(zhǎng)度七兜。

'x'.padStart(4); // '? x'

padStart() 的常用用途是為數(shù)值補(bǔ)全指定位數(shù)。下面代碼生成 10 位數(shù)值字符串福扬。

'1'.padStart(10, '0')? // 0000000001

'12'.padStart(10, '0') // 0000000012

另一個(gè)用途是提示字符串格式腕铸。

'12'.padStart(10, 'YYYY-MM-DD') // YYYY-MM-12

'09-12'.padStart(10, 'YYYY-MM-DD') // YYYY-09-12

8.trimStart() trimEnd()

Es2019 對(duì)字符串實(shí)例新增了 trimStart() 和 trimEnd() 這兩個(gè)方法。它們的行為與 trim() 一致铛碑,trimStart() 消除字符串頭部的空格狠裹,trimEnd() 消除尾部的空格,它們返回都是新字符串汽烦,不會(huì)修改原來(lái)的字符串涛菠。

const s = ' str ';

s.trim() // 'str'

s.trimStart() // 'str '

s.trimEnd() // ' str'

9.matchAll()

matchAll() 方法返回一個(gè)正則表達(dá)式在當(dāng)前字符串的所有匹配。


7.2徹底弄懂各種情況下的 this 指向

1. 如何改變函數(shù)內(nèi)部的 this 指針的指向?

每個(gè)函數(shù)都包含兩個(gè)非繼承來(lái)的方法call()和apply()俗冻;

使用call()或者apply()礁叔,可以改變this的指向;

假設(shè)要改變fn函數(shù)內(nèi)部的this的指向迄薄,指向obj琅关,那么可以fn.call(obj);或者fn.apply(obj);

call和apply的區(qū)別:

call和apply的區(qū)別在于參數(shù),他們兩個(gè)的第一個(gè)參數(shù)都是一樣的讥蔽,表示調(diào)用該函數(shù)的對(duì)象涣易;

apply的第二個(gè)參數(shù)是數(shù)組,是[arg1,arg2,arg3]這種形式冶伞,而call是arg1,arg2,arg3這樣的形式新症。

另外還可以用bind函數(shù):

var bar=fn.bind(obj);

那么fn中的this就指向obj對(duì)象了,bind函數(shù)返回新的函數(shù)响禽,這個(gè)函數(shù)內(nèi)的this指針指向obj對(duì)象账劲。


2. 如何判斷 this?箭頭函數(shù)的 this 是什么金抡?

this 是很多人會(huì)混淆的概念瀑焦,但是其實(shí)它一點(diǎn)都不難,只是網(wǎng)上很多文章把簡(jiǎn)單的東西說(shuō)復(fù)雜了梗肝。在這里榛瓮,你一定會(huì)徹底明白 this 這個(gè)概念的。

我們先來(lái)看幾個(gè)函數(shù)調(diào)用的場(chǎng)景:

function foo() {

? console.log(this.a)

}

var a = 1

foo()

const obj = {

? a: 2,

? foo: foo

}

obj.foo()

const c = new foo()

接下來(lái)我們一個(gè)個(gè)分析上面幾個(gè)場(chǎng)景

對(duì)于直接調(diào)用 foo 來(lái)說(shuō)巫击,不管 foo 函數(shù)被放在了什么地方禀晓,this 一定是 window

對(duì)于 obj.foo() 來(lái)說(shuō),我們只需要記住坝锰,誰(shuí)調(diào)用了函數(shù)粹懒,誰(shuí)就是 this,所以在這個(gè)場(chǎng)景下 foo 函數(shù)中的 this 就是 obj 對(duì)象

對(duì)于 new 的方式來(lái)說(shuō)顷级,this 被永遠(yuǎn)綁定在了 c 上面凫乖,不會(huì)被任何方式改變 this

說(shuō)完了以上幾種情況,其實(shí)很多代碼中的 this 應(yīng)該就沒什么問題了弓颈,下面讓我們看看箭頭函數(shù)中的 this

function a() {

? return () => {

? ? return () => {

? ? ? console.log(this)

? ? }

? }

}

console.log(a()()())

首先箭頭函數(shù)其實(shí)是沒有 this 的帽芽,箭頭函數(shù)中的 this 只取決包裹箭頭函數(shù)的第一個(gè)普通函數(shù)的 this。在這個(gè)例子中翔冀,因?yàn)榘^函數(shù)的第一個(gè)普通函數(shù)是 a导街,所以此時(shí)的 this 是 window。另外對(duì)箭頭函數(shù)使用 bind 這類函數(shù)是無(wú)效的纤子。

最后一種情況也就是 bind 這些改變上下文的 API 了搬瑰,對(duì)于這些函數(shù)來(lái)說(shuō)款票,this 取決于第一個(gè)參數(shù),如果第一個(gè)參數(shù)為空泽论,那么就是 window徽职。

那么說(shuō)到 bind,不知道大家是否考慮過(guò)佩厚,如果對(duì)一個(gè)函數(shù)進(jìn)行多次 bind,那么上下文會(huì)是什么呢说订?

let a = {}

let fn = function () { console.log(this) }

fn.bind().bind(a)() // => ?

如果你認(rèn)為輸出結(jié)果是 a抄瓦,那么你就錯(cuò)了,其實(shí)我們可以把上述代碼轉(zhuǎn)換成另一種形式

// fn.bind().bind(a) 等于

let fn2 = function fn1() {

? return function() {

? ? return fn.apply()

? }.apply(a)

}

fn2()

可以從上述代碼中發(fā)現(xiàn)陶冷,不管我們給函數(shù) bind 幾次钙姊,fn 中的 this 永遠(yuǎn)由第一次 bind 決定,所以結(jié)果永遠(yuǎn)是 window埂伦。

let a = { name: 'qwe' }

function foo() {

? console.log(this.name)

}

foo.bind(a)() // => 'qwe'

以上就是 this 的規(guī)則了煞额,但是可能會(huì)發(fā)生多個(gè)規(guī)則同時(shí)出現(xiàn)的情況,這時(shí)候不同的規(guī)則之間會(huì)根據(jù)優(yōu)先級(jí)最高的來(lái)決定 this 最終指向哪里沾谜。

首先膊毁,new 的方式優(yōu)先級(jí)最高,接下來(lái)是 bind 這些函數(shù)基跑,然后是 obj.foo() 這種調(diào)用方式婚温,最后是 foo 這種調(diào)用方式,同時(shí)媳否,箭頭函數(shù)的 this 一旦被綁定栅螟,就不會(huì)再被任何方式所改變。

如果你還是覺得有點(diǎn)繞篱竭,那么就看以下的這張流程圖吧力图,圖中的流程只針對(duì)于單個(gè)規(guī)則。

3. call掺逼、apply 以及 bind 函數(shù)內(nèi)部實(shí)現(xiàn)是怎么樣的吃媒?

call, apply, bind都是改變函數(shù)執(zhí)行的上下文,說(shuō)的直白點(diǎn)就是改變了函數(shù)this的指向吕喘。不同的是:call和apply改變了函數(shù)的this,并且執(zhí)行了該函數(shù)晓折,而bind是改變了函數(shù)的this,并返回一個(gè)函數(shù)兽泄,但不執(zhí)行該函數(shù)漓概。

看下面的例子1:

var doThu = function(a, b) {

? console.log(this)

? console.log(this.name)

? console.log([a, b])

}

var stu = {

? name: 'xiaoming',

? doThu: doThu,

}

stu.doThu(1, 2) // stu對(duì)象 xiaoming [1, 2]

doThu.call(stu, 1, 2) // stu對(duì)象 xiaoming [1, 2]

由此可見,在stu上添加一個(gè)屬性doThu病梢,再執(zhí)行這個(gè)函數(shù)胃珍,就將doThu的this指向了stu梁肿。而call的作用就與此相當(dāng),只不過(guò)call為stu添加了doThu方法后觅彰,執(zhí)行了doThu吩蔑,然后再將doThu這個(gè)方法從stu中刪除。

下面來(lái)看call函數(shù)的內(nèi)部實(shí)現(xiàn)原理:

Function.prototype.call = function(thisArg, args) {

? // this指向調(diào)用call的對(duì)象

? if (typeof this !== 'function') { // 調(diào)用call的若不是函數(shù)則報(bào)錯(cuò)

? ? ? throw new TypeError('Error')

? }

? thisArg = thisArg || window

? thisArg.fn = this? // 將調(diào)用call函數(shù)的對(duì)象添加到thisArg的屬性中

? const result = thisArg.fn(...[...arguments].slice(1)) // 執(zhí)行該屬性

? delete thisArg.fn? // 刪除該屬性

? return result

}

apply的實(shí)現(xiàn)原理和call一樣填抬,只不過(guò)是傳入的參數(shù)不同而已烛芬。

Function.prototype.apply = function(thisArg, args) {

? if (typeof this !== 'function') {

? ? ? throw new TypeError('Error')

? }

? thisArg = thisArg || window

? thisArg.fn = this

? let result

? if(args) {

? ? ? result = thisArg.fn(...args)

? } else {

? ? ? result = thisArg.fn()

? }

? delete thisArg.fn

? return result

}

bind的實(shí)現(xiàn)原理比call和apply要復(fù)雜一些,bind中需要考慮一些復(fù)雜的邊界條件飒责。bind后的函數(shù)會(huì)返回一個(gè)函數(shù)赘娄,而這個(gè)函數(shù)也可能被用來(lái)實(shí)例化:

Function.prototype.bind = function(thisArg) {

? if(typeof this !== 'function'){

? ? ? throw new TypeError(this + 'must be a function');

? }

? // 存儲(chǔ)函數(shù)本身

? const _this? = this;

? // 去除thisArg的其他參數(shù) 轉(zhuǎn)成數(shù)組

? const args = [...arguments].slice(1)

? // 返回一個(gè)函數(shù)

? const bound = function() {

? ? ? // 可能返回了一個(gè)構(gòu)造函數(shù),我們可以 new F()宏蛉,所以需要判斷

? ? ? if (this instanceof bound) {

? ? ? ? ? return new _this(...args, ...arguments)

? ? ? }

? ? ? // apply修改this指向遣臼,把兩個(gè)函數(shù)的參數(shù)合并傳給thisArg函數(shù),并執(zhí)行thisArg函數(shù)拾并,返回執(zhí)行結(jié)果

? ? ? return _this.apply(thisArg, args.concat(...arguments))

? }

? return bound

}


32揍堰、數(shù)組、對(duì)象嗅义、函數(shù)的解構(gòu)賦值

1. 使用解構(gòu)屏歹,實(shí)現(xiàn)兩個(gè)變量的值的交換?

??? let a=5;

??? let b=3;

??? [a,b]=[b,a]

2. 解構(gòu)賦值之碗?

數(shù)組模型的解構(gòu)(Array)

基本

let [a, b, c] = [1, 2, 3];// a = 1// b = 2// c = 3

可嵌套

let [a, [[b], c]] = [1, [[2], 3]];// a = 1// b = 2// c = 3

可忽略

let [a, , b] = [1, 2, 3];// a = 1// b = 3

不完全解構(gòu)

let [a = 1, b] = []; // a = 1, b = undefined

剩余運(yùn)算符

let [a, ...b] = [1, 2, 3];//a = 1//b = [2, 3]

字符串等

在數(shù)組的解構(gòu)中西采,解構(gòu)的目標(biāo)若為可遍歷對(duì)象,皆可進(jìn)行解構(gòu)賦值继控⌒倒荩可遍歷對(duì)象即實(shí)現(xiàn) Iterator 接口的數(shù)據(jù)。

let [a, b, c, d, e] = 'hello';// a = 'h'// b = 'e'// c = 'l'// d = 'l'// e = 'o'

解構(gòu)默認(rèn)值

let [a = 2] = [undefined]; // a = 2

當(dāng)解構(gòu)模式有匹配結(jié)果武通,且匹配結(jié)果是 undefined 時(shí)霹崎,會(huì)觸發(fā)默認(rèn)值作為返回結(jié)果。

let [a = 3, b = a] = [];? ? // a = 3, b = 3?

let [a = 3, b = a] = [1];? ? // a = 1, b = 1

let [a = 3, b = a] = [1, 2]; // a = 1, b = 2

a 與 b 匹配結(jié)果為 undefined 冶忱,觸發(fā)默認(rèn)值:a = 3; b = a =3

a 正常解構(gòu)賦值尾菇,匹配結(jié)果:a = 1,b 匹配結(jié)果 undefined 囚枪,觸發(fā)默認(rèn)值:b = a =1

a 與 b 正常解構(gòu)賦值派诬,匹配結(jié)果:a = 1,b = 2

對(duì)象模型的解構(gòu)(Object)

基本

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };// foo = 'aaa'// bar = 'bbb'?

let { baz : foo } = { baz : 'ddd' };// foo = 'ddd'

可嵌套可忽略

let obj = {p: ['hello', {y: 'world'}] };

let {p: [x, { y }] } = obj;

// x = 'hello'// y = 'world'

let obj = {p: ['hello', {y: 'world'}] };

let {p: [x, {? }] } = obj;

// x = 'hello'

不完全解構(gòu)

let obj = {p: [{y: 'world'}] };let {p: [{ y }, x ] } = obj;// x = undefined// y = 'world'

剩余運(yùn)算符

let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};// a = 10// b = 20// rest = {c: 30, d: 40}

解構(gòu)默認(rèn)值

let {a = 10, b = 5} = {a: 3};// a = 3; b = 5;let {a: aa = 10, b: bb = 5} = {a: 3};// aa = 3; bb = 5;

3. 函數(shù)默認(rèn)參數(shù)链沼?

函數(shù)默認(rèn)參數(shù)

在ES6中默赂,可以為函數(shù)的參數(shù)指定默認(rèn)值。函數(shù)默認(rèn)參數(shù)允許在沒有值或undefined被傳入時(shí)使用默認(rèn)形參括勺。

function log(x, y = 'World') {

? console.log(x, y);

}

log('Hello') // Hello World

log('Hello', 'China') // Hello China

log('Hello', '') // Hello

默認(rèn)參數(shù)使用注意點(diǎn):參數(shù)變量是默認(rèn)聲明的缆八,所以不能用let或const再次聲明曲掰。

function foo(x = 5) {

? let x = 1; // error

? const x = 2; // error

}

使用參數(shù)默認(rèn)值時(shí),函數(shù)不能有同名參數(shù)奈辰。

// 不報(bào)錯(cuò)

function foo(x, x, y) {

? // ...

}

// 報(bào)錯(cuò)

function foo(x, x, y = 1) {

? // ...

}

// SyntaxError: Duplicate parameter name not allowed in this context

顯式傳入undefined或不傳值時(shí)使用函數(shù)默認(rèn)參數(shù)值栏妖;傳入''或null時(shí)使用傳入的參數(shù)值。

function test(num = 1) {

? console.log(typeof num);

}

test();? ? ? ? ? // 'number' (num is set to 1)

test(undefined); // 'number' (num is set to 1 too)

// test with other falsy values:

test('');? ? ? ? // 'string' (num is set to '')

test(null);? ? ? // 'object' (num is set to null)

參數(shù)默認(rèn)值不是傳值的奖恰,而是在函數(shù)被調(diào)用時(shí)吊趾,參數(shù)默認(rèn)值才會(huì)被解析。

function append(value, array = []) {

? array.push(value);

? return array;

}

append(1); //[1]

append(2); //[2], not [1, 2]


位置在前的默認(rèn)參數(shù)可用于后面的默認(rèn)參數(shù)瑟啃。

function greet(name, greeting, message = greeting + ' ' + name) {

? return [name, greeting, message];

}

greet('David', 'Hi');? // ["David", "Hi", "Hi David"]

greet('David', 'Hi', 'Happy Birthday!');? // ["David", "Hi", "Happy Birthday!"]

通常情況下论泛,定義了默認(rèn)值的參數(shù),應(yīng)該是函數(shù)的尾參數(shù)翰守。因?yàn)檫@樣比較容易看出來(lái),到底省略了哪些參數(shù)疲酌。如果非尾部的參數(shù)設(shè)置默認(rèn)值蜡峰,實(shí)際上這個(gè)參數(shù)是沒法省略的。

// 例一

function f(x = 1, y) {

? return [x, y];

}

f() // [1, undefined]

f(2) // [2, undefined])

f(, 1) // 報(bào)錯(cuò)

f(undefined, 1) // [1, 1]

// 例二

function f(x, y = 5, z) {

? return [x, y, z];

}

f() // [undefined, 5, undefined]

f(1) // [1, 5, undefined]

f(1, ,2) // 報(bào)錯(cuò)

f(1, undefined, 2) // [1, 5, 2]

指定了默認(rèn)值以后朗恳,函數(shù)的length屬性湿颅,將返回沒有指定默認(rèn)值的參數(shù)個(gè)數(shù)。如果設(shè)置了默認(rèn)值的參數(shù)不是尾參數(shù)粥诫,那么length屬性也不再計(jì)入后面的參數(shù)了油航。后文的 rest 參數(shù)也不會(huì)計(jì)入length屬性。

(function (a) {}).length // 1

(function (a = 5) {}).length // 0

(function (a, b, c = 5) {}).length // 2

(function(...args) {}).length // 0

(function (a = 0, b, c) {}).length // 0

(function (a, b = 1, c) {}).length // 1

剩余(rest)參數(shù)

ES6 引入 rest 參數(shù)(形式為...變量名)怀浆,用于獲取函數(shù)的多余參數(shù)谊囚,這樣就不需要使用arguments對(duì)象了。rest參數(shù)搭配的變量是一個(gè)數(shù)組执赡,該變量將多余的參數(shù)放入數(shù)組中镰踏。

function add(...values) {

? let sum = 0;

? for (var val of values) {

? ? sum += val;

? }

? return sum;

}

add(2, 5, 3) // 10

rest 參數(shù)使用注意點(diǎn):rest 參數(shù)之后不能再有其他參數(shù)(即只能是最后一個(gè)參數(shù)),否則會(huì)報(bào)錯(cuò)沙合。

// 報(bào)錯(cuò)

function f(a, ...b, c) {

? // ...

}

函數(shù)的length屬性奠伪,不包括 rest 參數(shù)。

(function(a) {}).length? // 1

(function(...a) {}).length? // 0

(function(a, ...b) {}).length? // 1

rest參數(shù)可以被解構(gòu)首懈,這意味著他們的數(shù)據(jù)可以被解包到不同的變量中绊率。

function f(...[a, b, c]) {

? return a + b + c;

}

f(1)? ? ? ? ? // NaN (b and c are undefined)

f(1, 2, 3)? ? // 6

f(1, 2, 3, 4) // 6 (the fourth parameter is not destructured)

rest參數(shù)和 arguments對(duì)象的區(qū)別:

rest參數(shù)只包含那些沒有對(duì)應(yīng)形參的實(shí)參,而arguments對(duì)象包含了傳給函數(shù)的所有實(shí)參究履。

arguments對(duì)象不是一個(gè)真正的數(shù)組滤否,而rest參數(shù)是真正的Array實(shí)例,也就是說(shuō)你能夠在它上面直接使用所有的數(shù)組方法最仑,比如 sort顽聂,map肥惭,forEach或pop。

arguments對(duì)象還有一些附加的屬性 (如callee屬性)紊搪。


4. JavaScript 中什么是變量提升蜜葱?什么是暫時(shí)性死區(qū)?

變量提升

var命令會(huì)發(fā)生“變量提升”現(xiàn)象耀石,即變量可以在聲明之前使用牵囤,值為undefined。這種現(xiàn)象多多少少是有些奇怪的滞伟,按照一般的邏輯揭鳞,變量應(yīng)該在聲明語(yǔ)句之后才可以使用。

為了糾正這種現(xiàn)象梆奈,let命令改變了語(yǔ)法行為野崇,它所聲明的變量一定要在聲明后使用,否則報(bào)錯(cuò)亩钟。

// var 的情況

console.log(foo); // 輸出undefined

var foo = 2;

// let 的情況

console.log(bar); // 報(bào)錯(cuò)ReferenceError

let bar = 2;

上面代碼中乓梨,變量foo用var命令聲明,會(huì)發(fā)生變量提升清酥,即腳本開始運(yùn)行時(shí)扶镀,變量foo已經(jīng)存在了,但是沒有值焰轻,所以會(huì)輸出undefined臭觉。變量bar用let命令聲明,不會(huì)發(fā)生變量提升辱志。這表示在聲明它之前蝠筑,變量bar是不存在的,這時(shí)如果用到它揩懒,就會(huì)拋出一個(gè)錯(cuò)誤菱肖。

暫時(shí)性死區(qū)

只要塊級(jí)作用域內(nèi)存在let命令,它所聲明的變量就“綁定”(binding)這個(gè)區(qū)域旭从,不再受外部的影響稳强。

var tmp = 123;

if (true) {

? tmp = 'abc'; // ReferenceError

? let tmp;

}

上面代碼中,存在全局變量tmp和悦,但是塊級(jí)作用域內(nèi)let又聲明了一個(gè)局部變量tmp退疫,導(dǎo)致后者綁定這個(gè)塊級(jí)作用域,所以在let聲明變量前鸽素,對(duì)tmp賦值會(huì)報(bào)錯(cuò)褒繁。

ES6 明確規(guī)定,如果區(qū)塊中存在let和const命令馍忽,這個(gè)區(qū)塊對(duì)這些命令聲明的變量棒坏,從一開始就形成了封閉作用域燕差。凡是在聲明之前就使用這些變量,就會(huì)報(bào)錯(cuò)坝冕。

總之徒探,在代碼塊內(nèi),使用let命令聲明變量之前喂窟,該變量都是不可用的测暗。這在語(yǔ)法上,稱為“暫時(shí)性死區(qū)”(temporal dead zone磨澡,簡(jiǎn)稱 TDZ)碗啄。

if (true) {

? // TDZ開始

? tmp = 'abc'; // ReferenceError

? console.log(tmp); // ReferenceError

? let tmp; // TDZ結(jié)束

? console.log(tmp); // undefined

? tmp = 123;

? console.log(tmp); // 123

}

上面代碼中,在let命令聲明變量tmp之前稳摄,都屬于變量tmp的“死區(qū)”稚字。

下面的代碼也會(huì)報(bào)錯(cuò),與var的行為不同厦酬。

// 不報(bào)錯(cuò)

var x = x;

// 報(bào)錯(cuò)

let x = x;

// ReferenceError: x is not defined

上面代碼報(bào)錯(cuò)胆描,也是因?yàn)闀簳r(shí)性死區(qū)。使用let聲明變量時(shí)弃锐,只要變量在還沒有聲明完成前使用袄友,就會(huì)報(bào)錯(cuò)殿托。上面這行就屬于這個(gè)情況霹菊,在變量x的聲明語(yǔ)句還沒有執(zhí)行完成前,就去取x的值支竹,導(dǎo)致報(bào)錯(cuò)”x 未定義“旋廷。

ES6 規(guī)定暫時(shí)性死區(qū)和let、const語(yǔ)句不出現(xiàn)變量提升礼搁,主要是為了減少運(yùn)行時(shí)錯(cuò)誤饶碘,防止在變量聲明前就使用這個(gè)變量,從而導(dǎo)致意料之外的行為馒吴。這樣的錯(cuò)誤在 ES5 是很常見的扎运,現(xiàn)在有了這種規(guī)定,避免此類錯(cuò)誤就很容易了饮戳。

總之豪治,暫時(shí)性死區(qū)的本質(zhì)就是,只要一進(jìn)入當(dāng)前作用域扯罐,所要使用的變量就已經(jīng)存在了负拟,但是不可獲取,只有等到聲明變量的那一行代碼出現(xiàn)歹河,才可以獲取和使用該變量掩浙。

import和require的區(qū)別

import和require都是被模塊化使用

1. require是CommonJs的語(yǔ)法(AMD規(guī)范引入方式)花吟,CommonJs的模塊是對(duì)象。

? ? import是es6的一個(gè)語(yǔ)法標(biāo)準(zhǔn)(瀏覽器不支持厨姚,本質(zhì)是使用node中的babel將es6轉(zhuǎn)碼為es5再執(zhí)行衅澈,import會(huì)被轉(zhuǎn)碼為? require),es6模塊不是對(duì)象

?2. require是運(yùn)行時(shí)加載整個(gè)模塊(即模塊中所有方法)遣蚀,生成一個(gè)對(duì)象矾麻,再?gòu)膶?duì)象上讀取它的方法(只有運(yùn)行時(shí)才能得到這個(gè)對(duì)象,不能在編譯時(shí)做到靜態(tài)化),理論上可以用在代碼的任何地方

? ? ? import是編譯時(shí)調(diào)用芭梯,確定模塊的依賴關(guān)系险耀,輸入變量(es6模塊不是對(duì)象,而是通過(guò)export命令指定輸出代碼玖喘,再通過(guò)import輸入甩牺,只加載import中導(dǎo)的方法,其他方法不加載)累奈,import具有提升效果贬派,會(huì)提升到模塊的頭部(編譯時(shí)執(zhí)行)export和import可以位于模塊中的任何位置,但是必須是在模塊頂層澎媒,如果在其他作用域內(nèi)搞乏,會(huì)報(bào)錯(cuò)es6這樣的設(shè)計(jì)可以提高編譯器效率,但沒法實(shí)現(xiàn)運(yùn)行時(shí)加載

3. require是賦值過(guò)程戒努,把require的結(jié)果(對(duì)象请敦,數(shù)字,函數(shù)等)储玫,默認(rèn)是export的一個(gè)對(duì)象侍筛,賦給某個(gè)變量(復(fù)制或淺拷貝)

? ? ?import是解構(gòu)過(guò)程(需要誰(shuí),加載誰(shuí))

? ? 寫法:

1.? require/exports(僅有下面的三種簡(jiǎn)單寫法)

? ? ? ?const? ? a=require('a')? ? ? //真正被require出來(lái)的是來(lái)自module.exports指向的內(nèi)存塊內(nèi)容

? ? ? ?exports.a=a? ? ? ? ? ? ?//exports?只是?module.exports的引用撒穷,輔助module.exports操作內(nèi)存中的數(shù)據(jù)

? ? ? ?module.exports=a

2.? import / export

? ? ? ? ? import a from 'a'

? ? ? ? ??import { default as a? }?from 'a'

? ? ? ? ? import? *? as a? from 'a'

? ? ? ? ? import { fun1,fun2?}?from 'a'

? ? ? ? ? import { fun1 as myfunction? }?from 'a'

? ? ? ? ? import a, { fun1? }?from 'a'

---------------------------------------------------------

? ? ? ? ? ?export default a

? ? ? ? ? ?export const a=1

? ? ? ? ? ?export functon a{ }

? ? ? ? ? ?export { fun1,fun2?}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匣椰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子端礼,更是在濱河造成了極大的恐慌禽笑,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛤奥,死亡現(xiàn)場(chǎng)離奇詭異佳镜,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)喻括,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門邀杏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事望蜡』秸福” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵脖律,是天一觀的道長(zhǎng)谢肾。 經(jīng)常有香客問我,道長(zhǎng)小泉,這世上最難降的妖魔是什么芦疏? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮微姊,結(jié)果婚禮上酸茴,老公的妹妹穿的比我還像新娘。我一直安慰自己兢交,他們只是感情好薪捍,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著配喳,像睡著了一般酪穿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上晴裹,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天被济,我揣著相機(jī)與錄音,去河邊找鬼涧团。 笑死只磷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的少欺。 我是一名探鬼主播喳瓣,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼馋贤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼赞别!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起配乓,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤仿滔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后犹芹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崎页,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年腰埂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了飒焦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖牺荠,靈堂內(nèi)的尸體忽然破棺而出翁巍,到底是詐尸還是另有隱情,我是刑警寧澤休雌,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布灶壶,位于F島的核電站,受9級(jí)特大地震影響杈曲,放射性物質(zhì)發(fā)生泄漏驰凛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一担扑、第九天 我趴在偏房一處隱蔽的房頂上張望恰响。 院中可真熱鬧,春花似錦涌献、人聲如沸渔隶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)间唉。三九已至,卻和暖如春利术,著一層夾襖步出監(jiān)牢的瞬間呈野,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工印叁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留被冒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓轮蜕,卻偏偏與公主長(zhǎng)得像昨悼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子跃洛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345