前端面試遇到的問(wèn)題(一)

今天面試,不知怎么說(shuō),面試官詢問(wèn)的很多,完全感覺(jué)自己的知識(shí)儲(chǔ)備略微有點(diǎn)不夠,下面看看有些啥問(wèn)題。

問(wèn)題一:是關(guān)于Object類型的數(shù)據(jù),如果只改變屬性值要如何去監(jiān)聽?
在ES5中,便有defineProperty(obj, prop, descriptor)方法,但此方法是用于直接在一個(gè)對(duì)象上定義一個(gè)新的屬性,或者修改一個(gè)已存在的屬性,并返回這個(gè)對(duì)象。
obj: 目標(biāo)對(duì)象;
prop: 目標(biāo)屬性;
descriptor: 對(duì)定義或修改的屬性描述符。

同時(shí)還有defineProperties(obj, prop)方法,來(lái)修改、設(shè)置、監(jiān)聽多個(gè)屬性
obj: 目標(biāo)對(duì)象;
prop: 目標(biāo)屬性組合而成的新對(duì)象;

let obj = {bb:"yang"};
Object.defineProperty(obj,'data',{ //對(duì)單個(gè)屬性進(jìn)行監(jiān)聽設(shè)置等操作
    enumerable: true, //true時(shí),該屬性才會(huì)出現(xiàn)在對(duì)象的枚舉屬性中
    value: val,//默認(rèn)值為undefined,可以是任何有效的JavaScript值(數(shù)值,對(duì)象,函數(shù)等)
    writable: true,//true時(shí),value才能被賦值運(yùn)算符改變
    上面三個(gè)屬性,是用于設(shè)置 ’data‘ 的一些配制,當(dāng)下面有g(shù)et或者set時(shí),則不能使用writable與value,否則會(huì)報(bào)錯(cuò)
    get:function(){
        return data;
    },
    set:function(newValue){
        data = newValue;
        console.log('set :',newValue);
        //需要觸發(fā)的渲染函數(shù)寫在這...
    }
});
Object.defineProperties(obj,{
    bb : {
        configurable: false, //true時(shí),該屬性才能被刪除
        get: function(){
            return bb;
        },
        set: function(value){
            bb = value;
            console.log('b',value);
        }
    },
    data: {
        enumerable: true,
        configurable: false,
        get: function(){
            return data;
        },
        set: function(value){
            data = value;
            console.log('data',value);
        }
    }
});
obj.data = 5;
obj.bb = "ll"

這里會(huì)存在一個(gè)明顯的問(wèn)題,那就是如果直接 console.log(obj),打印結(jié)果:{},但是如果用obj.data又能獲取到值:5。

上面兩個(gè)操作在ES6也有對(duì)應(yīng)的Proxy(target, handler)方法,該方法還可以劫持?jǐn)?shù)組,但兼容性不好

target:目標(biāo)對(duì)象(可以是任何類型的對(duì)象,包括原生數(shù)組,函數(shù),甚至可以是另一個(gè)代理)
handler: 一個(gè)對(duì)象,其屬性是當(dāng)執(zhí)行一個(gè)操作時(shí)定義代理的行為函數(shù)

let handler = {
    get: function(target,name){//如果沒(méi)有屬性,則返回默認(rèn)值37
        return name in target ? target[name] : 37;
    },
    set: function(obj, prop, value){
        if(prop == 'age'){
            if(!Number.isInteger(value)){
            //Number.isInteger,是用來(lái)校驗(yàn)數(shù)據(jù)是不是整數(shù),’10‘,也是false
                throw new TypeError('age屬性設(shè)置的值非整數(shù)!');
            }
        }
    }
}
let p = new Proxy({},handler);
p.age = '123'; //拋出錯(cuò)誤
p.age = 123; //正常

查詢相關(guān)質(zhì)料時(shí),發(fā)現(xiàn)一個(gè)很騷的操作:如何讓 a==1 && a==2 && a==3 為 true

方法一:
let b = 1;
Object.defineProperty(window,'a',{
    get: function(){
        return b++;
    }
})
方法二:
let a = {
    b: 1,
    toString(){
        return this.b++;
    }
}

問(wèn)題二:深淺拷貝
目的是為了解決引用數(shù)據(jù)類型復(fù)制的問(wèn)題。
來(lái),直接上我之前寫過(guò)的一個(gè)方法:

    getType(data){
        // console.log(Object.prototype.toString.call(data));
        return Object.prototype.toString.call(data).slice(8,-1);
    },
    dpClone(obj){
        let that = this, getT = this.getType(obj);
        switch(getT){
            case "Object":
                (function(){
                    let o = {};
                    for(let val in obj){
                        o[val] = that.dpClone(obj[val]);
                    }
                })();
                break;
            case "Array":
                (function(){
                    let arr = [];
                    for(let i = 0; i < obj.length; i++){
                        arr[i] = that.dpClone(obj[i]);
                    }
                })();
                break;
            case "Function":
                return new Function('return ' + obj.toString()).call(that);
            case "Date":
                return new Date(obj.valueOf());
            case "RegExp":
                return new RegExp(obj);
            case "Map":
                return (function(){
                    let m = new Map();
                    obj.forEach((v,k) => {
                        m.set(k,that.dpClone(v));
                    });
                    return m;
                })();
            case "Set":
                return (function(){
                    let s = new Set();
                    for(let val of obj.values()){
                        s.add(that.dpClone(val))
                    }
                    return s;
                })();
            default :
                return obj;
        }
    }

問(wèn)題三:map()函數(shù)
它是定義在Array中,返回一個(gè)新的數(shù)組,數(shù)組中的元素為原始數(shù)組調(diào)用函數(shù)處理后的值。
map()函數(shù)不會(huì)對(duì)空數(shù)組進(jìn)行檢測(cè),也不會(huì)改變?cè)瓟?shù)組。

arr.map(function(item,index,a){
//do something
},thisIndex)

item:當(dāng)前元素的值; 必須
index:當(dāng)前元素的索引; 可選
a:當(dāng)前元素屬于的數(shù)組對(duì)象; 可選
thisIndex:對(duì)象作為該執(zhí)行回調(diào)時(shí)使用,傳遞給函數(shù),用作“this”的值

擴(kuò)充知識(shí):Map集合與Set集合

Map集合

它是一組鍵值對(duì)的結(jié)構(gòu),具有極快的查找速度。
比如說(shuō),用兩個(gè)數(shù)組分存水果與對(duì)應(yīng)價(jià)格

let fruits = ['蘋果','香蕉','菠蘿','蜜桃'];
let prices = [23,15,18,20];

如果需要找菠蘿的價(jià)格,是不是比較麻煩,要先確定菠蘿在fruits數(shù)組的位置,然后再到prices數(shù)組里取。
而用Map來(lái)實(shí)現(xiàn),就很好處理。

let fruitPrices = new Map([['蘋果',23],['香蕉',15],['菠蘿',18],['蜜桃',20]]);

現(xiàn)在取菠蘿價(jià)格的就很簡(jiǎn)單了,

fruitPrices.get('菠蘿');

而添加、刪除數(shù)據(jù)也很方便,

fruitPrices.set('鳳梨',26);
fruitPrices.delete("香蕉");

注意:如果出現(xiàn)重復(fù)設(shè)置,后面會(huì)將前面的替換,因?yàn)?,這里是鍵值對(duì),鍵重復(fù),后者替換前者。

Set集合

Set與Map不同,它是 的集合,不存 值 ,所以 Set 集合里不存在重復(fù)的 鍵,很多時(shí)候,數(shù)組去重,就是用Set來(lái)操作的。

let setA = new Set(['a','b',1,2,3,'3']);

注意:這里的 3 與 ‘3’ 是不一樣的,所以不會(huì)被去重。
添加、刪除數(shù)據(jù)

setA.add('e');
setA.delete(3);

在數(shù)組去重時(shí),可以用map()函數(shù),以及Set集合來(lái)處理。

問(wèn)題四:slice()與splice()之間的區(qū)別
相同:都是數(shù)組的內(nèi)置方法,都可以用來(lái)截取數(shù)組的。

不同:
1.入?yún)⒉灰粯樱?br> slice,
一個(gè)參數(shù)時(shí),便是截取的起始下標(biāo),一直到數(shù)組最后;
兩個(gè)參數(shù)時(shí),則是截取的起、止下標(biāo),

注意:slice截取,含起不含止,很多這種類似需要起始位置的操作,基本都是
同時(shí),下標(biāo)是可以為負(fù)值,當(dāng)截止為負(fù)值,則說(shuō)位置是從后往前數(shù),記住一點(diǎn) -1 表示數(shù)組最后一位,-2表示數(shù)組倒數(shù)第二位。舉個(gè)例子:

let arr = [1,2,3,4,5,6,7,'a','b','c',8,9];
console.log(arr.slice(-2,-1));//輸出[8],起始是8,結(jié)束是9(不包括)

splice,
一個(gè)參數(shù),從下標(biāo)開始截取到數(shù)組最后一個(gè);
兩個(gè)參數(shù),第一個(gè)參數(shù)是截取起始位置,第二個(gè)參數(shù)是截取個(gè)數(shù);
三個(gè)參數(shù)以上,第一個(gè)參數(shù)是截取起始位置,第二個(gè)參數(shù)是截取個(gè)數(shù),第三個(gè)參數(shù)即后面的參數(shù),都是替換被截取的位置。

let arr = [1,2,3,4,5,6,7,'a','b','c',8,9];
arr.splice(2,1,'fg','lk')
console.log(arr);//輸出[1, 2, 'fg', 'lk', 4, 5, 6, 7, 'a', 'b', 'c', 8, 9]

2.截取的效果也不一樣
slice返回的是生成新的數(shù)組,不影響原數(shù)組,只是截取操作。
splice返回的是不需要的部分,而原數(shù)組也變成了我們需要的部分,這是直接在原數(shù)組上操作,可以截取、替換,插入操作。

問(wèn)題五:Promise
目的:解決回調(diào)深淵的問(wèn)題,讓代碼看起來(lái)更加舒服,更容易理解與書寫。

這里面存在一些問(wèn)題,第一,Promise一旦新建就會(huì)立即執(zhí)行,無(wú)法中途取消;第二,如果沒(méi)有設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出錯(cuò)誤,不會(huì)反應(yīng)到外部;第三,當(dāng)處于pending狀態(tài)時(shí),無(wú)法得知目前進(jìn)展到哪一個(gè)階段。
pending:待定;
fulfilled:解決(resolved);
rejected:拒絕;

剛剛細(xì)查了一下Promise的資料,加上自測(cè),發(fā)現(xiàn)一些我自己沒(méi)有想到的問(wèn)題,下面總結(jié)一下。

1、Promise里面是一個(gè)方法,這個(gè)方法里面有兩個(gè)固定參數(shù),兩個(gè)參數(shù)都是方法,參數(shù)一 (res) 表示成功,參數(shù)二 (rej) 表示失敗,res(值) 會(huì)被后面緊跟著的.then(res1,rej1),里面的res1接收到,rej(值) 會(huì)被rej1接收,這里接收的是括號(hào)里面的--值。
這個(gè)兩個(gè)方法參數(shù),只要有一個(gè)執(zhí)行,便直接進(jìn)入后續(xù)。
注意,這個(gè)--值,可以是普通數(shù)據(jù)類型,也可以是引用數(shù)據(jù)類型,還可以是一個(gè)方法,等。

2、.then(res,rej)里面的rej,是可以接收到前面未接收過(guò)的 rej(值) ,如果當(dāng)前的rej里面有返回值,會(huì)被下一個(gè).then(res1,rej1)里面的res1所接收

3、.catch(rej=>{}),這里是捕獲錯(cuò)誤,但前提是,前面的then里面沒(méi)有第二個(gè)參數(shù)rej才行,一旦某個(gè)then里面有rej,那么前面的失敗是走不到catch里面,catch也是有返回值的,如果后面再接一個(gè)then也是可以接收到這個(gè)返回值。

4、.finally(),它沒(méi)有入?yún)?,只要寫上去就?huì)執(zhí)行,不管是中間,還是最后,建議是最后,這個(gè)方法類似最后的收尾工作,
個(gè)人建議,在有.finally()時(shí),catch寫在它前面一個(gè)就行。

如果看不明白,可以拿我這個(gè)自測(cè)的代碼玩一下,

let promise = new Promise(function(res,rej){
    // res("su");
    rej(1);
  }).then(res1=>{
    console.log('res1',res1);
  },rej1=>{
    console.log('rej1',rej1);
    return 0;
  }).then(res2=>{
    console.log('res2',res2);
    return 'res2'
  },rej2=>{
    console.log('rej2',rej2);
  }).catch(rej3=>{
    console.log('catch',rej3);
    return 2;
  }).finally(()=>{
    console.log("finally");
  })

上面的都是串行,一個(gè)接一個(gè)的操作,多個(gè)并行,Promise自然也有:Promise.all()方法

let fn1 = function(){};
let fn2 = function(){};
let arr = [fn1,fn2];//fn1,fn2都是異步函數(shù)
Promise.all(arr).then(([data1,data2])=>{}).catch((rej)=>{})

該方法存在一個(gè)問(wèn)題,如果其中一個(gè)方法出問(wèn)題,大家一起GG。
所以,在有已知異步函數(shù)的情況下,還是串行相對(duì)要好。

那如果存方法的數(shù)組里不確定有多少個(gè)異步方法?
于是乎,用reduce()來(lái)改進(jìn)

let arr = [fn1,fn2,…];
arr.reduce((task,promise)=>{
    return task.then(()=> return promise).then(res=>{})
},Promise,resolve())

面對(duì)數(shù)組的reduce()方法,我又開始進(jìn)行探索,(喵的,寫了快一整天了,居然才寫到問(wèn)題五,這樣研究下去,可以研究一個(gè)星期)
來(lái)來(lái),介紹一下reduce(callback,[initVal])
這個(gè)方法類型for循環(huán)跟forEach方法的功能類似,就是遍歷。

參數(shù)一:
callback(prev,cur,index,arr):就是一個(gè)回調(diào)函數(shù),不過(guò)這回調(diào)函數(shù)的入?yún)⒂悬c(diǎn)子多--四個(gè)
prev: 在reduce有第二個(gè)參數(shù)initVal時(shí),prev的第一次就是initVal,記住是第一次,第二次開始就是前面處理的返回值。
如果reduce沒(méi)有第二個(gè)參數(shù),那么他就是數(shù)組的第一個(gè)元素,而下面的cur變成了第二個(gè)
cur:當(dāng)前被遍歷到的元素
index:當(dāng)前元素的下標(biāo)
arr:就是調(diào)用reduce的數(shù)組

參數(shù)二:
initVal:作為callback第一次調(diào)用時(shí)的入?yún)ⅲ?/p>

下面代碼,大家拿回去自測(cè)一下

var arr = [1, 2, 3, 4];
var sum = arr.reduce(function(prev, cur, index, arr) {
    console.log(prev, cur, index);
    return prev + cur;
})
console.log(arr, sum);

最開始不是說(shuō)Promise一旦建立就沒(méi)法中途取消,我就想著,能不能搞點(diǎn)事情,比如說(shuō)異步處理中定一個(gè)超時(shí)。
想了一下,真的取消是不可能,那就只能利用Promise的機(jī)制,通過(guò)reject,resolve讓他直接跳到最后的catch。

function stopPromise(fn,times){ //fn可以是reject,也可以是resolve
    setTimeout(()=>{
        fn(定義傳給后面的數(shù)據(jù))
    },times)
}
//使用
function myPromise(callback){
    return new Promise((res,rej)=>{
        //處理代碼
        callback && callback(rej,5000);//上面代碼5s內(nèi)沒(méi)有處理,便直接跳到下一步
    })
}
myPromise(stopPromise).then().catch()

然后我又百度了一下資料,發(fā)現(xiàn)還有另外一種方法,Promise.race()
科普Promise.race():該方法也是傳入 異步函數(shù)數(shù)組,與Promise.all()類似。

不同之處:
all()的返回值是大家一起執(zhí)行完,將結(jié)果組成一個(gè)新數(shù)組返回,其順序是根據(jù)入?yún)?shù)組里的順序排列的,
race()誰(shuí)先執(zhí)行完,就返回那個(gè)結(jié)果,不管結(jié)果是成功還是失敗。
因此,利用這個(gè)特點(diǎn)來(lái)操作。

let myStop = new Promise((res,rej)=>{
    setTimeout(()=>{
        //res()或rej()
    },5000)
})
let myPromise = new Promise(res,rej)=>{
    //執(zhí)行代碼
})

Promise.race([myStop,myPromise]).then().catch()

這里有一個(gè)狠明顯的問(wèn)題,那就是超時(shí)時(shí)間不能隨心所欲。

問(wèn)題六:變量聲明的區(qū)別
let聲明的變量不能在聲明之前使用;
var聲明的變量很隨意,這也是ES6 新增兩個(gè)聲明的原因;
const聲明的變量是不允許修改的,不過(guò)對(duì)象里修改屬性,或者新增屬性,不會(huì)報(bào)錯(cuò),因?yàn)榈刂窙](méi)變

問(wèn)題七:ES6中some與every之間的區(qū)別
既然都說(shuō)了ES6的方法了,那索性就一并拿出來(lái)瞅瞅。

數(shù)組相關(guān)

filter方法,用來(lái)過(guò)濾數(shù)組,生成一個(gè)新的數(shù)組

let newArr = arr.filter((val,index,arr)=>{
    //val當(dāng)前元素,index當(dāng)前元素下標(biāo),arr原數(shù)組
    //操作過(guò)程
    return true; 返回值為true,則當(dāng)前元素返回
})

reduce方法前面說(shuō)了,就不復(fù)述了
reduceRight方法,與reduce方法一樣,但是,它是從數(shù)組右邊往左遍歷
Array.from方法,將類數(shù)組轉(zhuǎn)為數(shù)組,只要是含有l(wèi)ength屬性的都可以轉(zhuǎn),
最騷的是,如果一個(gè)對(duì)象里面有l(wèi)ength屬性,也能轉(zhuǎn),看例子

let str = 'abcd';
let strArr = Array.from(str);//['a','b','c','d']

let obj = {name:"test",age:18,2:'two',5:'fi','3':'three',length:6}
console.log(Array.from(obj));//輸出[undefined,undefined,'two','three',undefined,'fi']

從這個(gè)例子,大家應(yīng)該看出點(diǎn)門道來(lái)了吧,第一個(gè)就不說(shuō)了,第二個(gè)能轉(zhuǎn),就是一個(gè) length:6 這個(gè)屬性,就轉(zhuǎn)成了一個(gè)長(zhǎng)度為6的數(shù)組,轉(zhuǎn)換的規(guī)矩是看鍵名,是否有 0-5 之間的 數(shù)字與字符串,匹配上,其鍵值就是新數(shù)組對(duì)應(yīng)位置上的值,匹配不上的位置就是undefined。

Array.of方法,將一組值轉(zhuǎn)換數(shù)組,類似聲明一個(gè)數(shù)組(new Array())

Array.of('123');//['123']
Array.of({a:23,b:4})//[{a:23,b:4}]
new Array('33');//['33']

copyWithin方法,在數(shù)組內(nèi)部將指定的一段數(shù)組,復(fù)制到其他位置,會(huì)改變?cè)瓟?shù)組
參數(shù)一,替換的開始位置,必傳
參數(shù)二,指定數(shù)組的起始下標(biāo),默認(rèn)0,為負(fù)值,則從右向左(類似splice方法里為負(fù)值),可選
參數(shù)三,指定數(shù)組的結(jié)束下標(biāo),默認(rèn)為數(shù)組長(zhǎng)度,為負(fù)值,表示倒數(shù),可選

let arr = [1, 2, 3, 4, 5];
  // console.log(arr.copyWithin(3));       [1, 2, 3, 1, 2]
  // console.log(arr.copyWithin(0,3));     [4, 5, 3, 4, 5]
  // console.log(arr.copyWithin(0,3,4));   [4, 2, 3, 4, 5]
  
  // console.log(arr.copyWithin(-1));      [1, 2, 3, 4, 1]
  // console.log(arr.copyWithin(-2,-3));   [1, 2, 3, 3, 4]
  //console.log(arr.copyWithin(-5,-3,-1)); [3, 4, 3, 4, 5]

find方法,找出第一條符合條件的數(shù)組項(xiàng)

let arr = [2,3,4,5]
arr.find((item,index,arr)=>{
    // item當(dāng)前元素,index當(dāng)前元素下標(biāo),arr當(dāng)前數(shù)組
    return item > 3;//返回4
})

findIndex方法,找出第一條符合條件的數(shù)組想的下標(biāo),與find方法一樣操作

fill方法,使用指定值填充整個(gè)數(shù)組,會(huì)改變?cè)瓟?shù)組
參數(shù)一,填充值
參數(shù)二,開始填充的起始下標(biāo)
參數(shù)三,結(jié)束填充的截止下標(biāo),不包括

let arr = [1, 2, 3, 4, 5];
console.log(arr.fill('a',2,4));//[1, 2, 'a', 'a', 5]

some方法,數(shù)組迭代方法,用來(lái)判斷數(shù)組里面有沒(méi)有符合要求的數(shù)據(jù),只要有一個(gè)滿足,返回true,沒(méi)有符合的返回false
every方法,數(shù)組迭代方法,用來(lái)判斷數(shù)組里面的所有數(shù)據(jù)是否符合要求,必須要全部符合才會(huì)返回true,有一個(gè)不符合,返回false

[44,32,54,12].some((item,index,arr)=>{return item > 50})  //true
[44,32,54,12].every((item,index,arr)=>{return item > 50}) //false

keys方法,遍歷數(shù)組的鍵名(一般針對(duì)Map/Set集合)

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

//keys方法
let arr2 = arr.keys();
console.log(arr2)//打印 Array Iterator {}
for(let key of arr2){
    console.log(key);//0,1,2,3
}

//value方法
let arr3 = arr.value();
console.log(arr3);//Array Iterator {}
for(let val of arr3){
    console.log(val);//1,2,3,4
}

//entries方法
let arr4 = arr.entries();
console.log(arr4);//Array Iterator {}
for(let item of arr4){
    console.log(item);//[0,1],[1,2],[2,3],[3,4]
}

有沒(méi)有發(fā)現(xiàn)上面的那個(gè)奇怪的問(wèn)題,直接打印數(shù)組是沒(méi)有什么數(shù)據(jù),但遍歷還是有數(shù)據(jù)的,ES6新增的Object.defineProprety()、Object.definePropreties()中的set操作也會(huì)有類似的問(wèn)題
value方法,與keys()方法相對(duì)應(yīng),它遍歷鍵值(一般針對(duì)Map/Set集合),案例就寫上面了
entries方法,結(jié)合keys與value,遍歷數(shù)組的鍵值與鍵名(一般針對(duì)Map/Set集合),案例就寫上面了方便一起對(duì)比

for…of與for…in的區(qū)別

for…of只能遍歷數(shù)組,而for…in能遍歷對(duì)象和數(shù)組(下標(biāo)可以視為鍵,數(shù)據(jù)項(xiàng)為值)
如果動(dòng)態(tài)給數(shù)組添加一個(gè)鍵值對(duì),for…in會(huì)數(shù)組的鍵包括添加的屬性名一起遍歷,而for…of只會(huì)遍歷原數(shù)組據(jù)項(xiàng)

字符串新增方法

includes方法,校驗(yàn)字符串中是否包含指定字符串,返回true、false
參數(shù)一:指定字符串;
參數(shù)二:查找起始下標(biāo)位置

let str = 'ewwrfsdfsf';
console.log(str.includes("ww")); //true
console.log(str.includes("ww",2));//false,從下標(biāo)2開始往后查,是匹配不到的

console.log(str.startsWith("ww",1));//true

console.log(str.endsWith("ww",3));//true

startWith方法,校驗(yàn)字符串是特定位置開始,是否以特定字符開頭
參數(shù)一:指定字符串
參數(shù)二:起始下標(biāo)
案例寫在上面了。

endsWith方法,校驗(yàn)字符串是否以特定字符串結(jié)尾,
參數(shù)一:指定字符串
參數(shù)二:結(jié)束下標(biāo)
案例寫上面

repeat方法,重復(fù)當(dāng)前字符串,不會(huì)影響原數(shù)組
參數(shù):重復(fù)次數(shù)

let str1 = 'abc';
console.log(str1.repeat(2));//abcabc
console.log(str1);//abc

padStart方法,字符串首位補(bǔ)全,生成新字符串
參數(shù)一:字符串的長(zhǎng)度
參數(shù)二:補(bǔ)充的字符串

let str2 = 'efgj';
console.log(str2.padStart(6,'12'));//12efgj
console.log(str2.padStart(8,'12'));//1212efgj,補(bǔ)充字符串長(zhǎng)度不夠,則重復(fù)替換
console.log(str2.padStart(8));//    efgj,沒(méi)有傳補(bǔ)充字符串,則以空格代替
console.log(str2.padStart(8,'123456'));//1234efgj,補(bǔ)充字符串長(zhǎng)度過(guò)長(zhǎng),則截取前面部分


console.log(str2.padEnd(6,'12'));//efgj12,
console.log(str2.padEnd(8,'12'));//efgj1212,
console.log(str2.padEnd(8));//efgj    ,
console.log(str2.padEnd(8,'123456'));//efgj1234

padEnd方法,與padStart方法相反,從字符串末尾補(bǔ)全
參數(shù)一:字符串長(zhǎng)度
參數(shù)二:補(bǔ)充字符串
案例寫上面

trimStart方法,過(guò)渡字符串前面的空格部分,
trimEnd方法,過(guò)濾字符串后面的空格部分,
trim方法,過(guò)濾字符串前后的空格部分

let str3 = '   hjk  ';
console.log(str3.trim());     //hjk,
console.log(str3.trimStart());//hjk   ,
console.log(str3.trimEnd());  //   hjk,

replace方法,替換字符串中所以指定的字符片段
參數(shù)一:被替換的字符片段
參數(shù)二:替換的字符片段

let str4 = 'bcdfjk';
console.log(str4.replace('df','hhh'));//bchhhjk
console.log(str4.replace('dj','hhh'));//bcdfjk

問(wèn)題八:forEach如何跳出循環(huán)
首先,return false是沒(méi)有用的,然后break、continue這些也不管用。
查了一下資料,forEach、map這兩個(gè)方法都是不能中途終止的,除非拋出異常錯(cuò)誤才能終止(遍歷完不算),所以,想終止那就手動(dòng)拋異常,
還有一點(diǎn)要注意,如果用throw new Error(),那就得用try{}catch(e){}來(lái)捕捉這個(gè)錯(cuò)誤,不然代碼就 走不下去了。

問(wèn)題九:vue v-for與v-if
vue 2.X版本里,同一個(gè)元素上,v-for的優(yōu)先級(jí)是大于v-if的,在同一個(gè)標(biāo)簽一起使用就有點(diǎn)耗費(fèi)性能,一般情況下,會(huì)選擇通過(guò)computed計(jì)算屬性,將if判斷為true的數(shù)據(jù)篩選,然后篩選的數(shù)據(jù)拿去v-for。
網(wǎng)上還有一個(gè)操作,避免渲染本該隱藏的列表項(xiàng),將v-if放到template,將v-for包括起來(lái)。
但是,在vue 3.X版本里,v-if總是優(yōu)先與v-for生效

問(wèn)題十::key的基礎(chǔ),為什么index沒(méi)有id好(v-for里最為明顯)
首先,咱先說(shuō)說(shuō):key的作用,它相當(dāng)于給DOM對(duì)象加了一個(gè)標(biāo)識(shí),方便在diff算法執(zhí)行時(shí),能更快的找到對(duì)于的節(jié)點(diǎn),高效的更新虛擬dom。
如果DOM發(fā)生變化,Vue里面首先要做的是,生產(chǎn)最新的虛擬DOM,然后拿舊的虛擬DOM來(lái)與之對(duì)比,對(duì)比就是以:key綁定的值為準(zhǔn),如果值相同,就直接拿來(lái)用,如果不同則重新創(chuàng)建。
這樣就可以看出,用index的問(wèn)題,如果新值是從數(shù)據(jù)最前面插入的,那么v-for遍歷修改后的數(shù)據(jù)時(shí),是不是每個(gè)新的虛擬DOM都要重新創(chuàng)建,而老的虛擬DOM都要?jiǎng)h除,而用id作為唯一標(biāo)識(shí),那么就只需要將新增的虛擬DOM加入就行。

拓展一下:
如果沒(méi)有:key,那么vue會(huì)使用一種最大限度減少動(dòng)態(tài)元素并盡可能的嘗試就地修改/復(fù)用相同類型元素的算法。
說(shuō)一下響應(yīng)式數(shù)據(jù)更新后,是怎么個(gè)操作,
先會(huì)觸發(fā) 渲染W(wǎng)atcher 的回調(diào)函數(shù) vm._update(vm._render()) 驅(qū)動(dòng)視圖更新,
vm._render() 其實(shí)生成的就是 vnode ,而 vm._update 就會(huì)帶著新的 vnode 去觸發(fā) patch
patch的過(guò)程:
1、不是相同節(jié)點(diǎn):isSameNode為false,直接銷毀舊的 vnode ,渲染新的 vnode。
2、是相同的節(jié)點(diǎn),就會(huì)繼續(xù)往下對(duì)比,盡快能做到節(jié)點(diǎn)的復(fù)用
這里會(huì)調(diào)用 src/core/vdom/patch.js 里的patchVNode方法
新vnode是文字:直接調(diào)用瀏覽器的 dom api 將節(jié)點(diǎn)直接替換文字內(nèi)容。
新vnode不是文字:那就要開始對(duì)比 子節(jié)點(diǎn) children,一直類推。
有新children沒(méi)有舊children:直接 addVnodes 添加新子節(jié)點(diǎn)
無(wú)新children有舊children:直接 removeVnodes 刪除舊子節(jié)點(diǎn)
寫到這里我想到一個(gè)問(wèn)題:

<ul>
     <li v-for="(val,index) in vFor" :key="index">
         {{val}}
         <input type="text">
     </li>
</ul>
<button @click="addFor">addvFor</button>
methods:{
     addFor(){
         this.vFor.unshift(8);
     }
}

這一部分代碼放入vue中,點(diǎn)擊按鈕會(huì)出現(xiàn)什么情況?


vFor1.png

vFor2.png

看到?jīng)]有,很騷氣吧,直接復(fù)用了!
而且,哪怕在input里加入了:key="index",也是這個(gè)結(jié)果,這也是在說(shuō)明index的不足之處

問(wèn)題十一:v-model這個(gè)語(yǔ)法糖是優(yōu)化那些方法的,原理是什么?
v-model即可以作用于表單元素,又可作用于自定義組件,最終會(huì)生成一個(gè)屬性和一個(gè)事件。
當(dāng)其作用于表單元素時(shí),vue會(huì)根據(jù)作用的表單元素類型而生成合適的屬性和事件。例如,作用于普通文本框的時(shí)候,它會(huì)生成value屬性和input事件,而當(dāng)其作用于單選框或多選框時(shí),它會(huì)生成checked屬性和change事件。
當(dāng)其作用于自定義組件時(shí),默認(rèn)情況下,它會(huì)生成一個(gè)value屬性和input事件;可以通過(guò)組件的model配置來(lái)改變生成的屬性和事件

問(wèn)題十二:compute與watch直接的區(qū)別,以及他們的使用
1、computed支持緩存,只有當(dāng)依賴的數(shù)據(jù)發(fā)生變化,才會(huì)重新計(jì)算。而watch不支持緩存,數(shù)據(jù)變化,立即出發(fā)相應(yīng)操作。
2、computed不支持異步,異步操作在computed內(nèi)是無(wú)效的,無(wú)法監(jiān)聽到數(shù)據(jù)變化。而watch支持異步。
3、computed屬性值會(huì)默認(rèn)走緩存,計(jì)算屬性是基于它們的響應(yīng)式依賴進(jìn)行緩存的,也就是基于data中的聲明過(guò)或者父組件傳遞的props中的數(shù)據(jù)通過(guò)計(jì)算得到的值;watch監(jiān)聽的函數(shù)接收兩個(gè)參數(shù),第一個(gè)參數(shù)是最新值,第二個(gè)參數(shù)是變化之前的舊值
4、如果一個(gè)屬性是由其他屬性計(jì)算而來(lái),這個(gè)屬性依賴其他屬性,是一對(duì)一或者一對(duì)多,或者說(shuō)多個(gè)數(shù)據(jù)影響該計(jì)算屬性,一般就用computed。如果屬性變化時(shí),需要執(zhí)行一些操作,或者該數(shù)據(jù)會(huì)影響多個(gè)數(shù)據(jù),一般就是watch
5、在computed中,屬性都有g(shù)et和set方法,如果computed的屬性值是函數(shù),那么就會(huì)走get方法,函數(shù)的返回值就是屬性的屬性值,如果依賴的數(shù)據(jù)變化,則調(diào)用set方法。

一次面試,總結(jié)三天,我太難了!
寫了近七千字的總結(jié),都快寫吐我了。
嘿嘿!其實(shí),還有兩個(gè)問(wèn)題,vuex狀態(tài)管理,以及為何不用瀏覽器緩存而是用vuex來(lái)管理數(shù)據(jù),(剛剛準(zhǔn)備上傳文章時(shí),才想起來(lái))
能看到最后的小伙伴,可以自己思考一二,然后留個(gè)言吧?。?!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容