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都是被模塊化使用
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?}