您好,登錄后才能下訂單哦!
今天小編給大家分享一下web前端高頻知識(shí)點(diǎn)面試題有哪些的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
區(qū)別 | let | const | var |
---|---|---|---|
重復(fù)聲明 | 不能重復(fù)聲明,會(huì)報(bào)SyntaxError錯(cuò) | const 定義常量,值不能修改的變量叫做常量,一定要賦初始值,因?yàn)椴荒苄薷摹?/td> | 可以重復(fù)聲明 |
塊級(jí)作用域 | 擁有 | 擁有 | 不擁有 |
會(huì)不會(huì)污染全局變量(掛載在window上) | 不會(huì) | 不會(huì) | 會(huì) |
說明
1.let和const也存在變量提升,只是提升的方式不同
var變量提升:變量的聲明提升到頂部,值為undefined
let、const變量提升: 變量聲明提升到頂部,只不過將該變量標(biāo)記為尚未初始化
let 和 const存在暫時(shí)性死區(qū)
,代碼執(zhí)行過程中的一段時(shí)間內(nèi),在此期間無法使用標(biāo)識(shí)符,也不能引用外層作用域的變量。
let answer;
function fn(){
//如果此時(shí)沒有將變量變量提升到這里,answer應(yīng)該取外層answer的值
console.log(answer); //Uncaught ReferenceError: Cannot access 'answer' before initialization
let answer=42;
}
2.var創(chuàng)建的全局變量->全局對(duì)象的屬性,let和const在全局作用域聲明的變量->不是全局對(duì)象的屬性
3.如果常量是個(gè)數(shù)組或?qū)ο螅瑢?duì)其內(nèi)部元素修改,不算對(duì)常量的修改,不會(huì)報(bào)錯(cuò)。常量指向了一個(gè)地址,地址不變就不會(huì)報(bào)錯(cuò)。
變量聲明升級(jí)
通過var定義(聲明)的變量,在定義語句之前的就可以訪問到
但是值是undefined
函數(shù)聲明提升
通過function聲明的函數(shù),在之前就可以直接調(diào)用。
值是函數(shù)體
//變量提升先于函數(shù)提升,提升后被函數(shù)聲明function覆蓋,所以就算換了順序也是function
function a(){
}
var a ;
console.log(typeof a); //function
var f1 = function () {
console.log(1);
}
function f1 () {
console.log(2);
}
f1() ; //1
//變量提升后
var f1;//變量提升
function f1(){};//函數(shù)提升
f1 = function () {
console.log(1);
}
f1() ;
變量提升練習(xí)題
理解:一個(gè)代碼段所在的區(qū)域,是靜態(tài)的,在編寫代碼時(shí)就確定了。
作用:變量綁定在這個(gè)作用域內(nèi)有效,隔離變量,不同作用域下同名變量不會(huì)有沖突。
作用域分類
全局作用域
函數(shù)作用域
塊級(jí)作用域
作用域鏈:多個(gè)作用域嵌套,就近選擇,先在自己作用域找,然后去就近的作用域找。
函數(shù)的作用域在聲明的時(shí)候就已經(jīng)決定了,與調(diào)用位置無關(guān)
所以執(zhí)行aaa()的時(shí)候先在aaa的作用域里面找,沒有找到a,再去父級(jí)作用域window里面找,找到a=10
var a = 10;
function aaa() {
alert(a);
}
function bbb() {
var a = 20;
aaa();
}
bbb();
對(duì)當(dāng)前JavaScript的執(zhí)行環(huán)境的抽象,每當(dāng)JavaScript開始執(zhí)行的時(shí)候,它都在執(zhí)行上下文中運(yùn)行。
全局執(zhí)行上下文:在執(zhí)行全局代碼前將window確定為全局執(zhí)行上下文
對(duì)全局?jǐn)?shù)據(jù)進(jìn)行預(yù)處理
var定義的全局變量 --> undefined,添加為window的屬性
function聲明的全局函數(shù) --> 賦值(函數(shù)體),添加為window的方法
this --> 賦值window
開始執(zhí)行全局代碼
函數(shù)執(zhí)行上下文:在調(diào)用函數(shù),準(zhǔn)備執(zhí)行函數(shù)體之前,創(chuàng)建對(duì)應(yīng)的函數(shù)執(zhí)行上下文對(duì)象
對(duì)局部數(shù)據(jù)進(jìn)行預(yù)處理
形參變量 --> 賦值(實(shí)參)–> 添加到函數(shù)執(zhí)行上下文的屬性
arguments(形參列表封裝成的偽數(shù)組)–>賦值(實(shí)參列表),添加到函數(shù)執(zhí)行上下文的屬性
var定義的局部變量–>undefined,添加為函數(shù)執(zhí)行上下文的屬性
function聲明的函數(shù)–>賦值(函數(shù)體),添加為函數(shù)執(zhí)行上下文的方法
this–>賦值(調(diào)用函數(shù)的對(duì)象)
開始執(zhí)行函數(shù)體代碼
執(zhí)行上下文棧
1.在全局代碼執(zhí)行前,JS引擎就會(huì)創(chuàng)建一個(gè)棧來存儲(chǔ)管理所有的執(zhí)行上下文對(duì)象
2.在全局執(zhí)行上下文(window)確定后,將其添加到棧中(壓棧)
3.在函數(shù)執(zhí)行上下文創(chuàng)建后,將其添加到棧中(壓棧)
4.在當(dāng)前函數(shù)執(zhí)行完成后,將棧頂?shù)膶?duì)象移除(出棧)
5.當(dāng)所有的代碼執(zhí)行完后,棧中只剩下window
作用域 | 執(zhí)行上下文 |
---|---|
定義了幾個(gè)函數(shù) + 1 = 幾個(gè)作用域 | 執(zhí)行了幾個(gè)函數(shù) + 1 = 幾個(gè)執(zhí)行上下文 |
函數(shù)定義時(shí)就確定了,一直存在,不會(huì)再變化,是靜態(tài)的 | 全局執(zhí)行上下文環(huán)境實(shí)在全局作用域確定之后,js代碼執(zhí)行之前創(chuàng)建的 調(diào)用函數(shù)時(shí)創(chuàng)建,函數(shù)調(diào)用結(jié)束被釋放,是動(dòng)態(tài)的 |
var foo = 1;
function bar () {
console.log(foo);
var foo = 10;
console.log(foo);
}
bar();
//變量提升后
var foo = 1;
function bar () {
var foo = undefined;
console.log(foo); //undefined
foo = 10;
console.log(foo);//10
}
bar();
let
:使用立即執(zhí)行函數(shù)創(chuàng)造出一個(gè)塊級(jí)作用域
(function(){
var a = 1;
console.log('內(nèi)部a:', a);
})();
const
1.使用立即執(zhí)行函數(shù)創(chuàng)造出一個(gè)塊級(jí)作用域。
2.對(duì)于不可變性,可以利用Object.defineProperty
將變量掛載在對(duì)象上
var __const = function __const(data, value) {
this.data = value // 把要定義的data掛載到某個(gè)對(duì)象,并賦值value
Object.defineProperty(this,data, { // 利用Object.defineProperty的能力劫持當(dāng)前對(duì)象,并修改其屬性描述符
enumerable: false,
configurable: false,
get: function () {
return value
},
set: function (data) {
if (data !== value) { // 當(dāng)要對(duì)當(dāng)前屬性進(jìn)行賦值時(shí),則拋出錯(cuò)誤!
throw new TypeError('Assignment to constant variable.')
} else {
return value
}
}
})
}
//然后和立即執(zhí)行函數(shù)結(jié)合
(function(){
var obj = {}
_const.call(obj,'a',10)
})()
//當(dāng)執(zhí)行完畢后,全局上就不會(huì)有obj,也不會(huì)有obj.a這個(gè)變量,進(jìn)而實(shí)現(xiàn)了塊級(jí)作用域的功能
function a(){
a.name ='aaa';
return this.name;
}
var b = {
a,
name:'bbb',
getName:function(){
return this.name;
}
}
var c =b.getName;
console.log(a()); //this指向window,window上沒有name,所以輸出undefined
console.log(b.a()); //b.a 是function,b調(diào)用a函數(shù),所以this指向b,所以輸出'bbb'
console.log(b.getName);//通過b調(diào)用getName,所以getName指向b,所以輸出'bbb'
console.log(c());//c是function,this指向window,window上沒有name,所以輸出undefined
筆記鏈接
JS數(shù)據(jù)類型有哪些
介紹一下Symbol和Bigint
如何判斷一個(gè)數(shù)據(jù)類型
Object.prototype.toString.call() 的缺點(diǎn)?
各個(gè)方法的原理是什么
typeof(NaN) typeof(Null)
手寫 instanceof 方法
null==undefined 和 null===undefined
隱式轉(zhuǎn)換規(guī)則 === 和 == 的區(qū)別
map和weakmap區(qū)別
map和object區(qū)別
for in、for of 區(qū)別,分別對(duì)對(duì)象和數(shù)組使用問結(jié)果
講一下數(shù)組的遍歷方法,filter與map的使用場(chǎng)景,some,every的區(qū)別
map的操作原理
map和forEach的區(qū)別
使用迭代器實(shí)現(xiàn)for-of
手寫數(shù)組去重
手寫數(shù)組扁平化
map和filter的區(qū)別
數(shù)組的常用方法
用reduce實(shí)現(xiàn)map
ES5 function類 | ES6 class |
---|---|
可以new 可以調(diào)用 | 必須new調(diào)用,不能直接執(zhí)行 |
function存在變量提升 | class不存在變量提升 |
static靜態(tài)方法只能通過類調(diào)用,不會(huì)出現(xiàn)在實(shí)例上 |
一般函數(shù)中this的指向會(huì)在調(diào)用時(shí)向函數(shù)傳遞執(zhí)行上下文對(duì)象中設(shè)置。
以函數(shù)形式調(diào)用,指向window
以方法形式調(diào)用,this指向調(diào)用的方法
以構(gòu)造函數(shù)的形式調(diào)用,this是新創(chuàng)建的對(duì)象
箭頭函數(shù):本身沒有this,它的this可以沿作用域鏈(定義時(shí)就確定了的)查找
apply、call、bind 函數(shù)可以改變 this 的指向。
區(qū)別 | call | apply | bind |
---|---|---|---|
調(diào)用函數(shù) | √ | √ | × |
參數(shù) | 從第二個(gè)參數(shù)開始依次傳遞 | 封裝成數(shù)組傳遞 | 從第二個(gè)參數(shù)開始依次傳遞 |
bind函數(shù)的特殊點(diǎn)
多次綁定,只指向第一次綁定的obj對(duì)象。
多次綁定,一次生效。
原因:返回函數(shù),后續(xù)bind修改的是返回函數(shù)的this
call函數(shù)的實(shí)現(xiàn)
//從第二個(gè)參數(shù)開始依次傳入,所以接收時(shí)使用rest參數(shù)
Function.prototype.call=function(obj,...args){
obj = obj || window;
args = args ? args : [];
//給obj新增一個(gè)獨(dú)一無二的屬性以免覆蓋原有屬性
const key = Symbol()
obj[key] = this;
const res = obj[key](...args);
delete obj[key];
return res;
}
apply函數(shù)的實(shí)現(xiàn)
Function.prototype.apply=function(obj,args){
obj = obj || window;
args = args ? args : [];
//給obj新增一個(gè)獨(dú)一無二的屬性以免覆蓋原有屬性
const key = Symbol()
obj[key] = this;
const res = obj[key](...args);
delete obj[key];
return res;
}
bind函數(shù)的實(shí)現(xiàn)
Function.prototype.bind=function(obj,...args){
obj = obj || window;
args = args ? args : [];
return (...args2) => {
return this.apply(obj,[...args,...args2])
}
}
箭頭函數(shù)的作用:確保函數(shù)內(nèi)部的this和外部的this是一樣的
箭頭函數(shù)是普通函數(shù)的語法糖,書寫要更加簡潔
區(qū)別 | 一般函數(shù) | 箭頭函數(shù) |
---|---|---|
this指向 | 調(diào)用時(shí)確定 | 定義時(shí)確定,沒有自己的this,沿著作用域鏈找父級(jí)的this |
改變this指向 | call,apply,bind | 不能改變,靜態(tài) |
arguments | 有 | 沒有,可以用rest參數(shù)代替 |
作為構(gòu)造函數(shù) | √ | × 沒有prototype屬性 |
匿名函數(shù) | 可以匿名可以不匿名 | 匿名函數(shù) |
是什么
閉包就是在函數(shù)中能夠讀取其他函數(shù)內(nèi)部變量
本質(zhì)就是上級(jí)作用域內(nèi)變量的生命周期,因?yàn)楸幌录?jí)作用域內(nèi)引用,而沒有被釋放。
正常情況下,代碼執(zhí)行完成之后,函數(shù)的執(zhí)行上下文出棧被回收。但是如果當(dāng)前函數(shù)執(zhí)行上下文執(zhí)行完成之后中的某個(gè)東西被執(zhí)行上下文以外的東西占用,則當(dāng)前函數(shù)執(zhí)行上下文就不會(huì)出棧釋放,也就是形成了不被銷毀的上下文,閉包。
function foo(){
var a=2;
function bar(){ //覆蓋foo()內(nèi)部作用域的閉包
console.log(a++);
}
return bar;
}
var bar = foo(); //foo執(zhí)行創(chuàng)建一個(gè)執(zhí)行上下文環(huán)境,由于bar引用了其內(nèi)部變量,也就是bar持有foo本次執(zhí)行上下文的引用,foo本次的執(zhí)行上下文不會(huì)被銷魂
bar();//2
bar();//3
var fn = foo(); //foo執(zhí)行創(chuàng)建一個(gè)新的執(zhí)行上下文環(huán)境,fn持有了foo本次執(zhí)行上下文的引用
fn();//2
有什么用
1.可以讀取函數(shù)內(nèi)部的變量
2.使函數(shù)的內(nèi)部變量執(zhí)行完后,仍然存活在棧內(nèi)存中(延長了局部變量的生命周期)。
JavaScript閉包就是在另一個(gè)作用域中保存了一份它從上一級(jí)函數(shù)或者作用域得到的變量,而這些變量是不會(huì)隨上一級(jí)函數(shù)的執(zhí)行完成而銷毀
常用場(chǎng)景:節(jié)流防抖
缺點(diǎn)是什么
1.函數(shù)執(zhí)行完后,函數(shù)內(nèi)的局部變量沒有釋放,占用內(nèi)存時(shí)間會(huì)變長
2.容易造成內(nèi)存泄露
怎么解決
及時(shí)釋放:讓內(nèi)部函數(shù)成為垃圾對(duì)象(將閉包手動(dòng)設(shè)置為null)–> 回收閉包
作用是:控制回調(diào)函數(shù)觸發(fā)的頻率,進(jìn)行性能優(yōu)化
參數(shù): 控制觸發(fā)頻率的回調(diào)函數(shù)和時(shí)間wait
輸出: 到時(shí)間后,返回callback函數(shù)
節(jié)流:在函數(shù)被頻繁觸發(fā)時(shí), 函數(shù)執(zhí)行一次后,只有大于設(shè)定的執(zhí)行周期后才會(huì)執(zhí)行第二次。一個(gè)時(shí)間段,只觸發(fā)一次
語法:throttle(callback, wait)
常用場(chǎng)景:比如拖動(dòng)、滾動(dòng)和輸入框聯(lián)想
//使用形式,綁定時(shí)候throttle函數(shù)就會(huì)執(zhí)行,所以this是window
window.addEventListener('scroll',throttle(()=>{},500))
/*
思路
需要記錄上一次觸發(fā)的時(shí)間,才可以和當(dāng)前時(shí)間比較,是否超過了間隔時(shí)間
第一次必然立刻觸發(fā)
*/
function throttle(callback,wait){
let pre = new Date();
//這里的this是window
return function(...args){
//這里的this是綁定的DOM
const now = new Date();
if(now-pre>=wait){
callback.apply(this,args);
pre = now;
}
}
}
/*
使用setTimeout實(shí)現(xiàn)
第一次需要延遲delay后觸發(fā)
*/
function throttle(callback,delay){
let timer = null;
//這里的this是window
return function(...args){
if(timer){//說明已經(jīng)觸發(fā)了
return;
}
timer = setTimeout(()=>{
callback.apply(this,args);
timer = null;
},delay)
}
}
函數(shù)防抖:指定時(shí)間間隔內(nèi)只會(huì)執(zhí)行一次任務(wù)。如果在等待的過程中再一次觸發(fā)了事件,計(jì)時(shí)器重新開始計(jì)時(shí),直到達(dá)到時(shí)間后執(zhí)行最后一次的回調(diào)
語法:debounce(callback, wait)
常用場(chǎng)景: 登錄、發(fā)短信等按鈕避免用戶點(diǎn)擊太快,以致于發(fā)送了多次請(qǐng)求,需要防抖。
function debounce(callback,delay){
let timer = null;
//這里的this是window
return function(){
if(timer){//說明已經(jīng)觸發(fā)了
clearTimeout(timer);
}
timer = setTimeout(()=>{
callback.apply(this,arguments);
timer = null;
},delay)
}
}
//立即執(zhí)行
function debounce(func,delay) {
let timeout;
return function (...args) {
if (timeout) clearTimeout(timeout);
const callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, delay)
if (callNow) func.apply(this, args)
}
}
筆記鏈接
原型和原型鏈
繼承
筆記鏈接
內(nèi)容
進(jìn)程和線程
進(jìn)程的通信方式
瀏覽器多進(jìn)程架構(gòu)
如何實(shí)現(xiàn)瀏覽器多標(biāo)簽之間的通訊
H5 Web Workers JS多線程運(yùn)行
瀏覽器的事件循環(huán)機(jī)制
Node的事件循環(huán)機(jī)制
node事件循環(huán)代碼輸出題 用于理解
代碼輸出題
筆記
內(nèi)容
DOM的渲染過程
DOM渲染的時(shí)機(jī)與渲染進(jìn)程的概述
-瀏覽器的渲染流程
CSS、JS、DOM解析和渲染阻塞問題
JS加載阻塞DOM渲染問題,怎么解決? - 異步JS,JS三種異步加載的方式
script 標(biāo)簽中的 async 和 defer 屬性
DOMContentLoaded和Load
DOM渲染優(yōu)化
筆記
內(nèi)容
什么是重繪和回流
回流和重繪觸發(fā)的時(shí)機(jī)
優(yōu)化方案
GPU加速,如何開啟GPU加速
JS優(yōu)化減少重繪和回流的觸發(fā)
筆記
內(nèi)容
setTimeout(cb, 0)會(huì)立刻執(zhí)行嗎?
settimeout定時(shí)的時(shí)間準(zhǔn)確嗎? 為什么不準(zhǔn)確? 怎么解決?
setTimeout和requestAnimation的區(qū)別
requestAnimationFrame講一下你的理解
setTimeout實(shí)際延遲時(shí)間
用setTimeout實(shí)現(xiàn)setInterval,實(shí)現(xiàn)一個(gè)隨時(shí)停止的版本
setTimeout 和 setInterval區(qū)別
JS實(shí)現(xiàn)動(dòng)畫的方式
requestAnimationFrame與requestIdleCallback分別是什么?
requestAnimationFrame的執(zhí)行時(shí)機(jī)?
requestanimationframe回調(diào)函數(shù)中進(jìn)行大量計(jì)算,會(huì)阻塞頁面的渲染嗎
每隔一秒輸出一個(gè)數(shù)字
觀察者是軟件設(shè)計(jì)模式中的一種,但發(fā)布訂閱只是軟件架構(gòu)中的一種消息范式
觀察者模式
觀察者模式定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。
被依賴的對(duì)象叫做subject
,依賴的對(duì)象叫做觀察者Observer
。被依賴的對(duì)象首先需要提供一個(gè)添加觀察者方法,以供觀察者調(diào)用。所以還需要維護(hù)一個(gè)觀察者列表,自身發(fā)生變化后,依次通知觀察者。
//subject 被觀察者
class Subject {
constructor() {
this.observerList = [];
}
addObserver(observer) {
this.observerList.push(observer);
}
removeObserver(observer) {
const index = this.observerList.findIndex(o => o.name === observer.name);
this.observerList.splice(index, 1);
}
notifyObservers(message) {
const observers = this.observeList;
observers.forEach(observer => observer.notified(message));
}
}
//Observer 觀察者
class Observer {
constructor(name, subject) {
this.name = name;
if (subject) {
subject.addObserver(this);
}
}
notified(message) {
console.log(this.name, 'got message', message);
}
}
//使用
const subject = new Subject();
const observerA = new Observer('observerA', subject);
const observerB = new Observer('observerB');
subject.addObserver(observerB);
subject.notifyObservers('Hello from subject');
subject.removeObserver(observerA);
subject.notifyObservers('Hello again');
發(fā)布訂閱機(jī)制
發(fā)布者和訂閱者不直接進(jìn)行通信,通過事件調(diào)度中心進(jìn)行管理。發(fā)布者將要發(fā)布的消息交由事件調(diào)度中心管理,訂閱者也是根據(jù)自己的情況,按需訂閱事件調(diào)度中心的消息。
//事件調(diào)度中心
class PubSub {
constructor() {
// 存儲(chǔ)格式: warTask: [], routeTask: []
// {訂閱事件:[回調(diào)1,回調(diào)2...],訂閱事件2:[回調(diào)1,回調(diào)2..]}
this.events = {}
}
// 訂閱方法 訂閱哪個(gè)類型type就把對(duì)應(yīng)的回調(diào)函數(shù)放入
subscribe(type, cb) {
if (!this.events[type]) {
this.events[type] = [];
}
this.events[type].push(cb);
}
// 發(fā)布方法
publish(type, ...args) {
if (this.events[type]) {
this.events[type].forEach(cb => cb(...args))
}
}
// 取消訂閱方法 的某一個(gè)類型的某一個(gè)回調(diào)
unsubscribe(type, cb) {
if (this.events[type]) {
const cbIndex = this.events[type].findIndex(e=> e === cb)
if (cbIndex != -1) {
this.events[type].splice(cbIndex, 1);
}
}
if (this.events[type].length === 0) {
delete this.events[type];
}
}
}
//測(cè)試
let pubsub = new PubSub();
//訂閱
pubsub.subscribe('warTask', function (taskInfo){
console.log("宗門殿發(fā)布戰(zhàn)斗任務(wù),任務(wù)信息:" + taskInfo);
})
pubsub.subscribe('routeTask', function (taskInfo) {
console.log("宗門殿發(fā)布日常任務(wù),任務(wù)信息:" + taskInfo);
});
pubsub.subscribe('allTask', function (taskInfo) {
console.log("宗門殿發(fā)布五星任務(wù),任務(wù)信息:" + taskInfo);
});
//發(fā)布
pubsub.publish('warTask', "獵殺時(shí)刻");
pubsub.publish('allTask', "獵殺時(shí)刻");
pubsub.publish('routeTask', "種樹澆水");
pubsub.publish('allTask', "種樹澆水");
區(qū)別
類型 | 描述 | 特點(diǎn) |
---|---|---|
觀察者模式 | 觀察者和被觀察者互相知道身份,目標(biāo)直接將通知分發(fā)到觀察者身上 | 高耦合 |
發(fā)布訂閱機(jī)制 | 發(fā)布訂閱機(jī)制通過事件調(diào)度中心來協(xié)調(diào),訂閱者和發(fā)布者互相不知道身份 | 低耦合 |
筆記鏈接
筆記內(nèi)容
Generator生成器函數(shù)
Generator生成器函數(shù)使用上的補(bǔ)充 了解
基于Promise對(duì)象的簡單自動(dòng)執(zhí)行器
iterator迭代器
async/await是什么? 使用場(chǎng)景是什么?
await/async與generator函數(shù)的區(qū)別
await/async內(nèi)部實(shí)現(xiàn)原理 Generator函數(shù)和自動(dòng)執(zhí)行器
async錯(cuò)誤捕獲方式
promise概述
promise知識(shí)點(diǎn) 了解
promise.then、catch、finally的原理與實(shí)現(xiàn)
Promise.all/Promise.race/Promise.allSettled的原理和實(shí)現(xiàn)
手寫題:請(qǐng)求五秒未完成則終止
promise實(shí)現(xiàn)并發(fā)的異步任務(wù)調(diào)度器
深淺拷貝只是針對(duì)引用數(shù)據(jù)類型
區(qū)分點(diǎn): 復(fù)制之后的副本進(jìn)行修改會(huì)不會(huì)影響到原來的
淺拷貝:修改拷貝以后的數(shù)據(jù)會(huì)影響原數(shù)據(jù)。使得原數(shù)據(jù)不安全。(只拷貝一層)
深拷貝:修改拷貝以后的數(shù)據(jù)不會(huì)影響原數(shù)據(jù),拷貝的時(shí)候生成新數(shù)據(jù)。
淺拷貝
擴(kuò)展運(yùn)算符,適用于數(shù)組/對(duì)象
Aarry.prototype.concat(拷貝對(duì)象1,拷貝對(duì)象2...)
數(shù)組的合并方法,將多個(gè)數(shù)組或?qū)ο罂截愡M(jìn)目標(biāo)數(shù)組,返回新數(shù)組。
Object.assign(目標(biāo)對(duì)象1,拷貝對(duì)象1,拷貝對(duì)象2.....)
對(duì)象的合并方法,將拷貝對(duì)象拷貝進(jìn)目標(biāo)對(duì)象
方式一: JSON.parse(JSON.stringify())
JSON.stringify()
:將JavaScript對(duì)象轉(zhuǎn)換為JSON字符串
JSON.parse()
:可以將JSON字符串轉(zhuǎn)為一個(gè)對(duì)象。
問題1: 函數(shù)屬性會(huì)丟失,不能克隆方法
問題2: 循環(huán)引用會(huì)出錯(cuò)
//循環(huán)引用:b中引用了c,c中又有b
obj = {
b:['a','f'],
c:{h:20}
}
obj.b.push(obj.c);
obj.c.j = obj.b;
b:['a','f',{h:20,j:[]}],
c:{h:20,j:['a','f',[]]}
function deepClone1(target) {
//通過數(shù)組創(chuàng)建JSON格式的字符串
let str = JSON.stringify(target);
//將JSON格式的字符串轉(zhuǎn)換為JS數(shù)據(jù)
let data = JSON.parse(str);
return data;
}
方式二:遞歸+map
遞歸:實(shí)現(xiàn)深拷貝,不丟失屬性
map:存儲(chǔ)已經(jīng)拷貝過的對(duì)象,解決循環(huán)引用問題
//map存放已經(jīng)拷貝過的對(duì)象,key為需要拷貝的對(duì)象,value為拷貝后的對(duì)象
function deepClone(target,map=new Map()){
//1.判斷是否是引用類型
if(typeof target === 'object' && target !==null ){
if(map.has(target))return map.get(target); //說明已經(jīng)拷貝過了
let isArr = Array.isArray(target);
let res = isArr?[]:{};
map.set(target,res)
if(isArr){//拷貝的是數(shù)組
target.forEach((item,index) => {
res[index] = deepClone(item,map);
});
}else{//拷貝的是對(duì)象
Object.keys(target).forEach(key=>{
res[key]=deepClone(target[key],map);
})
}
return res; //返回的是一個(gè)數(shù)組或?qū)ο?
}else{
return target;
}
}
//測(cè)試
console.log(deepClone([1,[1,2,[3,4]]]))
以上就是“web前端高頻知識(shí)點(diǎn)面試題有哪些”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。