今天面試,不知怎么說(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)什么情況?


看到?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è)言吧?。?!