中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

廣州網(wǎng)站建設培訓學校網(wǎng)站alexa排名查詢

廣州網(wǎng)站建設培訓學校,網(wǎng)站alexa排名查詢,智達世通建設集團有限公司網(wǎng)站,做網(wǎng)站虛擬主機要多大哪些情況會導致內(nèi)存泄漏 以下四種情況會造成內(nèi)存的泄漏: 意外的全局變量: 由于使用未聲明的變量,而意外的創(chuàng)建了一個全局變量,而使這個變量一直留在內(nèi)存中無法被回收。被遺忘的計時器或回調(diào)函數(shù): 設置了 setInterval…

哪些情況會導致內(nèi)存泄漏

以下四種情況會造成內(nèi)存的泄漏:

  • 意外的全局變量: 由于使用未聲明的變量,而意外的創(chuàng)建了一個全局變量,而使這個變量一直留在內(nèi)存中無法被回收。
  • 被遺忘的計時器或回調(diào)函數(shù): 設置了 setInterval 定時器,而忘記取消它,如果循環(huán)函數(shù)有對外部變量的引用的話,那么這個變量會被一直留在內(nèi)存中,而無法被回收。
  • 脫離 DOM 的引用: 獲取一個 DOM 元素的引用,而后面這個元素被刪除,由于一直保留了對這個元素的引用,所以它也無法被回收。
  • 閉包: 不合理的使用閉包,從而導致某些變量一直被留在內(nèi)存當中。

常見的CSS布局單位

常用的布局單位包括像素(px),百分比(%),emremvw/vh。

(1)像素px)是頁面布局的基礎,一個像素表示終端(電腦、手機、平板等)屏幕所能顯示的最小的區(qū)域,像素分為兩種類型:CSS像素和物理像素:

  • CSS像素:為web開發(fā)者提供,在CSS中使用的一個抽象單位;
  • 物理像素:只與設備的硬件密度有關,任何設備的物理像素都是固定的。

(2)百分比%),當瀏覽器的寬度或者高度發(fā)生變化時,通過百分比單位可以使得瀏覽器中的組件的寬和高隨著瀏覽器的變化而變化,從而實現(xiàn)響應式的效果。一般認為子元素的百分比相對于直接父元素。

(3)em和rem相對于px更具靈活性,它們都是相對長度單位,它們之間的區(qū)別:em相對于父元素,rem相對于根元素。

  • em: 文本相對長度單位。相對于當前對象內(nèi)文本的字體尺寸。如果當前行內(nèi)文本的字體尺寸未被人為設置,則相對于瀏覽器的默認字體尺寸(默認16px)。(相對父元素的字體大小倍數(shù))。
  • rem: rem是CSS3新增的一個相對單位,相對于根元素(html元素)的font-size的倍數(shù)。作用:利用rem可以實現(xiàn)簡單的響應式布局,可以利用html元素中字體的大小與屏幕間的比值來設置font-size的值,以此實現(xiàn)當屏幕分辨率變化時讓元素也隨之變化。

(4)vw/vh是與視圖窗口有關的單位,vw表示相對于視圖窗口的寬度,vh表示相對于視圖窗口高度,除了vw和vh外,還有vmin和vmax兩個相關的單位。

  • vw:相對于視窗的寬度,視窗寬度是100vw;
  • vh:相對于視窗的高度,視窗高度是100vh;
  • vmin:vw和vh中的較小值;
  • vmax:vw和vh中的較大值;

vw/vh 和百分比很類似,兩者的區(qū)別:

  • 百分比(%):大部分相對于祖先元素,也有相對于自身的情況比如(border-radius、translate等)
  • vw/vm:相對于視窗的尺寸

position的屬性有哪些,區(qū)別是什么

position有以下屬性值:

屬性值概述
absolute生成絕對定位的元素,相對于static定位以外的一個父元素進行定位。元素的位置通過left、top、right、bottom屬性進行規(guī)定。
relative生成相對定位的元素,相對于其原來的位置進行定位。元素的位置通過left、top、right、bottom屬性進行規(guī)定。
fixed生成絕對定位的元素,指定元素相對于屏幕視?(viewport)的位置來指定元素位置。元素的位置在屏幕滾動時不會改變,?如回到頂部的按鈕?般都是?此定位?式。
static默認值,沒有定位,元素出現(xiàn)在正常的文檔流中,會忽略 top, bottom, left, right 或者 z-index 聲明,塊級元素從上往下縱向排布,?級元素從左向右排列。
inherit規(guī)定從父元素繼承position屬性的值

前面三者的定位方式如下:

  • relative: 元素的定位永遠是相對于元素自身位置的,和其他元素沒關系,也不會影響其他元素。

  • fixed: 元素的定位是相對于 window (或者 iframe)邊界的,和其他元素沒有關系。但是它具有破壞性,會導致其他元素位置的變化。

  • absolute: 元素的定位相對于前兩者要復雜許多。如果為 absolute 設置了 top、left,瀏覽器會根據(jù)什么去確定它的縱向和橫向的偏移量呢?答案是瀏覽器會遞歸查找該元素的所有父元素,如果找到一個設置了position:relative/absolute/fixed的元素,就以該元素為基準定位,如果沒找到,就以瀏覽器邊界定位。如下兩個圖所示:

水平垂直居中的實現(xiàn)

  • 利用絕對定位,先將元素的左上角通過top:50%和left:50%定位到頁面的中心,然后再通過translate來調(diào)整元素的中心點到頁面的中心。該方法需要考慮瀏覽器兼容問題。
.parent {    position: relative;} .child {    position: absolute;    left: 50%;    top: 50%;    transform: translate(-50%,-50%);}
  • 利用絕對定位,設置四個方向的值都為0,并將margin設置為auto,由于寬高固定,因此對應方向?qū)崿F(xiàn)平分,可以實現(xiàn)水平和垂直方向上的居中。該方法適用于盒子有寬高的情況:
.parent {position: relative;
}.child {position: absolute;top: 0;bottom: 0;left: 0;right: 0;margin: auto;
}
  • 利用絕對定位,先將元素的左上角通過top:50%和left:50%定位到頁面的中心,然后再通過margin負值來調(diào)整元素的中心點到頁面的中心。該方法適用于盒子寬高已知的情況
.parent {position: relative;
}.child {position: absolute;top: 50%;left: 50%;margin-top: -50px;     /* 自身 height 的一半 */margin-left: -50px;    /* 自身 width 的一半 */
}
  • 使用flex布局,通過align-items:center和justify-content:center設置容器的垂直和水平方向上為居中對齊,然后它的子元素也可以實現(xiàn)垂直和水平的居中。該方法要考慮兼容的問題,該方法在移動端用的較多:
.parent {display: flex;justify-content:center;align-items:center;
}

說一下 HTML5 drag API

  • dragstart:事件主體是被拖放元素,在開始拖放被拖放元素時觸發(fā)。
  • darg:事件主體是被拖放元素,在正在拖放被拖放元素時觸發(fā)。
  • dragenter:事件主體是目標元素,在被拖放元素進入某元素時觸發(fā)。
  • dragover:事件主體是目標元素,在被拖放在某元素內(nèi)移動時觸發(fā)。
  • dragleave:事件主體是目標元素,在被拖放元素移出目標元素是觸發(fā)。
  • drop:事件主體是目標元素,在目標元素完全接受被拖放元素時觸發(fā)。
  • dragend:事件主體是被拖放元素,在整個拖放操作結(jié)束時觸發(fā)。

設置小于12px的字體

在谷歌下css設置字體大小為12px及以下時,顯示都是一樣大小,都是默認12px。

解決辦法:

  • 使用Webkit的內(nèi)核的-webkit-text-size-adjust的私有CSS屬性來解決,只要加了-webkit-text-size-adjust:none;字體大小就不受限制了。但是chrome更新到27版本之后就不可以用了。所以高版本chrome谷歌瀏覽器已經(jīng)不再支持-webkit-text-size-adjust樣式,所以要使用時候慎用。
  • 使用css3的transform縮放屬性-webkit-transform:scale(0.5); 注意-webkit-transform:scale(0.75);收縮的是整個元素的大小,這時候,如果是內(nèi)聯(lián)元素,必須要將內(nèi)聯(lián)元素轉(zhuǎn)換成塊元素,可以使用display:block/inline-block/…;
  • 使用圖片:如果是內(nèi)容固定不變情況下,使用將小于12px文字內(nèi)容切出做圖片,這樣不影響兼容也不影響美觀。

參考 前端進階面試題詳細解答

link和@import的區(qū)別

兩者都是外部引用CSS的方式,它們的區(qū)別如下:

  • link是XHTML標簽,除了加載CSS外,還可以定義RSS等其他事務;@import屬于CSS范疇,只能加載CSS。
  • link引用CSS時,在頁面載入時同時加載;@import需要頁面網(wǎng)頁完全載入以后加載。
  • link是XHTML標簽,無兼容問題;@import是在CSS2.1提出的,低版本的瀏覽器不支持。
  • link支持使用Javascript控制DOM去改變樣式;而@import不支持。

對BFC的理解,如何創(chuàng)建BFC

先來看兩個相關的概念:

  • Box: Box 是 CSS 布局的對象和基本單位,?個??是由很多個 Box 組成的,這個Box就是我們所說的盒模型。
  • Formatting context:塊級上下?格式化,它是??中的?塊渲染區(qū)域,并且有?套渲染規(guī)則,它決定了其?元素將如何定位,以及和其他元素的關系和相互作?。

塊格式化上下文(Block Formatting Context,BFC)是Web頁面的可視化CSS渲染的一部分,是布局過程中生成塊級盒子的區(qū)域,也是浮動元素與其他元素的交互限定區(qū)域。

通俗來講:BFC是一個獨立的布局環(huán)境,可以理解為一個容器,在這個容器中按照一定規(guī)則進行物品擺放,并且不會影響其它環(huán)境中的物品。如果一個元素符合觸發(fā)BFC的條件,則BFC中的元素布局不受外部影響。

創(chuàng)建BFC的條件:

  • 根元素:body;
  • 元素設置浮動:float 除 none 以外的值;
  • 元素設置絕對定位:position (absolute、fixed);
  • display 值為:inline-block、table-cell、table-caption、flex等;
  • overflow 值為:hidden、auto、scroll;

BFC的特點:

  • 垂直方向上,自上而下排列,和文檔流的排列方式一致。
  • 在BFC中上下相鄰的兩個容器的margin會重疊
  • 計算BFC的高度時,需要計算浮動元素的高度
  • BFC區(qū)域不會與浮動的容器發(fā)生重疊
  • BFC是獨立的容器,容器內(nèi)部元素不會影響外部元素
  • 每個元素的左margin值和容器的左border相接觸

BFC的作用:

  • 解決margin的重疊問題:由于BFC是一個獨立的區(qū)域,內(nèi)部的元素和外部的元素互不影響,將兩個元素變?yōu)閮蓚€BFC,就解決了margin重疊的問題。
  • 解決高度塌陷的問題:在對子元素設置浮動后,父元素會發(fā)生高度塌陷,也就是父元素的高度變?yōu)?。解決這個問題,只需要把父元素變成一個BFC。常用的辦法是給父元素設置overflow:hidden
  • 創(chuàng)建自適應兩欄布局:可以用來創(chuàng)建自適應兩欄布局:左邊的寬度固定,右邊的寬度自適應。
.left{width: 100px;height: 200px;background: red;float: left;}.right{height: 300px;background: blue;overflow: hidden;}<div class="left"></div>
<div class="right"></div>

左側(cè)設置float:left,右側(cè)設置overflow: hidden。這樣右邊就觸發(fā)了BFC,BFC的區(qū)域不會與浮動元素發(fā)生重疊,所以兩側(cè)就不會發(fā)生重疊,實現(xiàn)了自適應兩欄布局。

DOM 節(jié)點操作

(1)創(chuàng)建新節(jié)點

createDocumentFragment()    //創(chuàng)建一個DOM片段
createElement()   //創(chuàng)建一個具體的元素
createTextNode()   //創(chuàng)建一個文本節(jié)點

(2)添加、移除、替換、插入

appendChild(node)
removeChild(node)
replaceChild(new,old)
insertBefore(new,old)

(3)查找

getElementById();
getElementsByName();
getElementsByTagName();
getElementsByClassName();
querySelector();
querySelectorAll();

(4)屬性操作

getAttribute(key);
setAttribute(key, value);
hasAttribute(key);
removeAttribute(key);

CSS動畫和過渡

animation / keyframes

  • animation-name: 動畫名稱,對應@keyframes
  • animation-duration: 間隔
  • animation-timing-function: 曲線
  • animation-delay: 延遲
  • animation-iteration-count: 次數(shù)
    • infinite: 循環(huán)動畫
  • animation-direction: 方向
    • alternate: 反向播放
  • animation-fill-mode: 靜止模式
    • forwards: 停止時,保留最后一幀
    • backwards: 停止時,回到第一幀
    • both: 同時運用 forwards / backwards
  • 常用鉤子: animationend

動畫屬性: 盡量使用動畫屬性進行動畫,能擁有較好的性能表現(xiàn)

  • translate
  • scale
  • rotate
  • skew
  • opacity
  • color

transform

  • 位移屬性 translate( x , y )
  • 旋轉(zhuǎn)屬性 rotate()
  • 縮放屬性 scale()
  • 傾斜屬性 skew()

transition

  • transition-property(過渡的屬性的名稱)。
  • transition-duration(定義過渡效果花費的時間,默認是 0)。
  • transition-timing-function:linear(勻速) ease(慢速開始,然后變快,然后慢速結(jié)束)(規(guī)定過渡效果的時間曲線,最常用的是這兩個)。
  • transition-delay(規(guī)定過渡效果何時開始。默認是 0)

般情況下,我們都是寫一起的,比如:transition: width 2s ease 1s

關鍵幀動畫animation

一個關鍵幀動畫,最少包含兩部分,animation 屬性及屬性值(動畫的名稱和運行方式運行時間等)。@keyframes(規(guī)定動畫的具體實現(xiàn)過程)

animation 屬性可以拆分為

  • animation-name 規(guī)定@keyframes 動畫的名稱。
  • animation-duration 規(guī)定動畫完成一個周期所花費的秒或毫秒。默認是 0。
  • animation-timing-function 規(guī)定動畫的速度曲線。默認是 “ease”,常用的還有linear,同transtion 。
  • animation-delay 規(guī)定動畫何時開始。默認是 0。
  • animation-iteration-count 規(guī)定動畫被播放的次數(shù)。默認是 1,但我們一般用infinite,一直播放

@keyframes的使用方法,可以是from->to(等同于0%和100%),也可以是從0%->100%之間任意個的分層設置。我們通過下面一個稍微復雜點的demo來看一下,基本上用到了上面說到的大部分知識

eg:@keyframes mymove{from {top:0px;}to {top:200px;}}/* 等同于: */@keyframes mymove
{0%   {top:0px;}25%  {top:200px;}50%  {top:100px;}75%  {top:200px;}100% {top:0px;}
}

用css3動畫使一個圖片旋轉(zhuǎn)

#loader {display: block;position: relative;-webkit-animation: spin 2s linear infinite;animation: spin 2s linear infinite;}@-webkit-keyframes spin {0%   {-webkit-transform: rotate(0deg);-ms-transform: rotate(0deg);transform: rotate(0deg);}100% {-webkit-transform: rotate(360deg);-ms-transform: rotate(360deg);transform: rotate(360deg);}}@keyframes spin {0%   {-webkit-transform: rotate(0deg);-ms-transform: rotate(0deg);transform: rotate(0deg);}100% {-webkit-transform: rotate(360deg);-ms-transform: rotate(360deg);transform: rotate(360deg);}}

template預編譯是什么

對于 Vue 組件來說,模板編譯只會在組件實例化的時候編譯一次,生成渲染函數(shù)之后在也不會進行編譯。因此,編譯對組件的 runtime 是一種性能損耗。

而模板編譯的目的僅僅是將template轉(zhuǎn)化為render function,這個過程,正好可以在項目構(gòu)建的過程中完成,這樣可以讓實際組件在 runtime 時直接跳過模板渲染,進而提升性能,這個在項目構(gòu)建的編譯template的過程,就是預編譯。

viewport

<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />// width    設置viewport寬度,為一個正整數(shù),或字符串‘device-width’// device-width  設備寬度// height   設置viewport高度,一般設置了寬度,會自動解析出高度,可以不用設置// initial-scale    默認縮放比例(初始縮放比例),為一個數(shù)字,可以帶小數(shù)// minimum-scale    允許用戶最小縮放比例,為一個數(shù)字,可以帶小數(shù)// maximum-scale    允許用戶最大縮放比例,為一個數(shù)字,可以帶小數(shù)// user-scalable    是否允許手動縮放
  • 延伸提問
    • 怎樣處理 移動端 1px 被 渲染成 2px問題

局部處理

  • meta標簽中的 viewport屬性 ,initial-scale 設置為 1
  • rem按照設計稿標準走,外加利用transfromescale(0.5) 縮小一倍即可;

全局處理

  • mate標簽中的 viewport屬性 ,initial-scale 設置為 0.5
  • rem 按照設計稿標準走即可

深淺拷貝

1. 淺拷貝的原理和實現(xiàn)

自己創(chuàng)建一個新的對象,來接受你要重新復制或引用的對象值。如果對象屬性是基本的數(shù)據(jù)類型,復制的就是基本類型的值給新對象;但如果屬性是引用數(shù)據(jù)類型,復制的就是內(nèi)存中的地址,如果其中一個對象改變了這個內(nèi)存中的地址,肯定會影響到另一個對象

方法一:object.assign

object.assign是 ES6 中 object 的一個方法,該方法可以用于 JS 對象的合并等多個用途,其中一個用途就是可以進行淺拷貝。該方法的第一個參數(shù)是拷貝的目標對象,后面的參數(shù)是拷貝的來源對象(也可以是多個來源)。

object.assign 的語法為:Object.assign(target, ...sources)

object.assign 的示例代碼如下:

let target = {};
let source = { a: { b: 1 } };
Object.assign(target, source);
console.log(target); // { a: { b: 1 } };

但是使用 object.assign 方法有幾點需要注意

  • 它不會拷貝對象的繼承屬性;
  • 它不會拷貝對象的不可枚舉的屬性;
  • 可以拷貝 Symbol 類型的屬性。
let obj1 = { a:{ b:1 }, sym:Symbol(1)}; 
Object.defineProperty(obj1, 'innumerable' ,{value:'不可枚舉屬性',enumerable:false
});
let obj2 = {};
Object.assign(obj2,obj1)
obj1.a.b = 2;
console.log('obj1',obj1);
console.log('obj2',obj2);

從上面的樣例代碼中可以看到,利用 object.assign 也可以拷貝 Symbol 類型的對象,但是如果到了對象的第二層屬性 obj1.a.b 這里的時候,前者值的改變也會影響后者的第二層屬性的值,說明其中依舊存在著訪問共同堆內(nèi)存的問題,也就是說這種方法還不能進一步復制,而只是完成了淺拷貝的功能

方法二:擴展運算符方式

  • 我們也可以利用 JS 的擴展運算符,在構(gòu)造對象的同時完成淺拷貝的功能。
  • 擴展運算符的語法為:let cloneObj = { ...obj };
/* 對象的拷貝 */
let obj = {a:1,b:{c:1}}
let obj2 = {...obj}
obj.a = 2
console.log(obj)  //{a:2,b:{c:1}} console.log(obj2); //{a:1,b:{c:1}}
obj.b.c = 2
console.log(obj)  //{a:2,b:{c:2}} console.log(obj2); //{a:1,b:{c:2}}
/* 數(shù)組的拷貝 */
let arr = [1, 2, 3];
let newArr = [...arr]; //跟arr.slice()是一樣的效果

擴展運算符 和 object.assign 有同樣的缺陷,也就是實現(xiàn)的淺拷貝的功能差不多,但是如果屬性都是基本類型的值,使用擴展運算符進行淺拷貝會更加方便

方法三:concat 拷貝數(shù)組

數(shù)組的 concat 方法其實也是淺拷貝,所以連接一個含有引用類型的數(shù)組時,需要注意修改原數(shù)組中的元素的屬性,因為它會影響拷貝之后連接的數(shù)組。不過 concat 只能用于數(shù)組的淺拷貝,使用場景比較局限。代碼如下所示。

let arr = [1, 2, 3];
let newArr = arr.concat();
newArr[1] = 100;
console.log(arr);  // [ 1, 2, 3 ]
console.log(newArr); // [ 1, 100, 3 ]

方法四:slice 拷貝數(shù)組

slice 方法也比較有局限性,因為它僅僅針對數(shù)組類型。slice方法會返回一個新的數(shù)組對象,這一對象由該方法的前兩個參數(shù)來決定原數(shù)組截取的開始和結(jié)束時間,是不會影響和改變原始數(shù)組的。

slice 的語法為:arr.slice(begin, end);
let arr = [1, 2, {val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;
console.log(arr);  //[ 1, 2, { val: 1000 } ]

從上面的代碼中可以看出,這就是淺拷貝的限制所在了——它只能拷貝一層對象。如果存在對象的嵌套,那么淺拷貝將無能為力。因此深拷貝就是為了解決這個問題而生的,它能解決多層對象嵌套問題,徹底實現(xiàn)拷貝

手工實現(xiàn)一個淺拷貝

根據(jù)以上對淺拷貝的理解,如果讓你自己實現(xiàn)一個淺拷貝,大致的思路分為兩點:

  • 對基礎類型做一個最基本的一個拷貝;
  • 對引用類型開辟一個新的存儲,并且拷貝一層對象屬性。
const shallowClone = (target) => {if (typeof target === 'object' && target !== null) {const cloneTarget = Array.isArray(target) ? []: {};for (let prop in target) {if (target.hasOwnProperty(prop)) {cloneTarget[prop] = target[prop];}}return cloneTarget;} else {return target;}
}

利用類型判斷,針對引用類型的對象進行 for 循環(huán)遍歷對象屬性賦值給目標對象的屬性,基本就可以手工實現(xiàn)一個淺拷貝的代碼了

2. 深拷貝的原理和實現(xiàn)

淺拷貝只是創(chuàng)建了一個新的對象,復制了原有對象的基本類型的值,而引用數(shù)據(jù)類型只拷貝了一層屬性,再深層的還是無法進行拷貝。深拷貝則不同,對于復雜引用數(shù)據(jù)類型,其在堆內(nèi)存中完全開辟了一塊內(nèi)存地址,并將原有的對象完全復制過來存放。

這兩個對象是相互獨立、不受影響的,徹底實現(xiàn)了內(nèi)存上的分離。總的來說,深拷貝的原理可以總結(jié)如下

將一個對象從內(nèi)存中完整地拷貝出來一份給目標對象,并從堆內(nèi)存中開辟一個全新的空間存放新對象,且新對象的修改并不會改變原對象,二者實現(xiàn)真正的分離。

方法一:乞丐版(JSON.stringify)

JSON.stringify() 是目前開發(fā)過程中最簡單的深拷貝方法,其實就是把一個對象序列化成為 JSON 的字符串,并將對象里面的內(nèi)容轉(zhuǎn)換成字符串,最后再用 JSON.parse() 的方法將 JSON 字符串生成一個新的對象

let a = {age: 1,jobs: {first: 'FE'}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE

但是該方法也是有局限性的

  • 會忽略 undefined
  • 會忽略 symbol
  • 不能序列化函數(shù)
  • 無法拷貝不可枚舉的屬性
  • 無法拷貝對象的原型鏈
  • 拷貝 RegExp 引用類型會變成空對象
  • 拷貝 Date 引用類型會變成字符串
  • 對象中含有 NaN、Infinity 以及 -InfinityJSON 序列化的結(jié)果會變成 null
  • 不能解決循環(huán)引用的對象,即對象成環(huán) (obj[key] = obj)。
function Obj() { this.func = function () { alert(1) }; this.obj = {a:1};this.arr = [1,2,3];this.und = undefined; this.reg = /123/; this.date = new Date(0); this.NaN = NaN;this.infinity = Infinity;this.sym = Symbol(1);
} 
let obj1 = new Obj();
Object.defineProperty(obj1,'innumerable',{ enumerable:false,value:'innumerable'
});
console.log('obj1',obj1);
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
console.log('obj2',obj2);

使用 JSON.stringify 方法實現(xiàn)深拷貝對象,雖然到目前為止還有很多無法實現(xiàn)的功能,但是這種方法足以滿足日常的開發(fā)需求,并且是最簡單和快捷的。而對于其他的也要實現(xiàn)深拷貝的,比較麻煩的屬性對應的數(shù)據(jù)類型,JSON.stringify 暫時還是無法滿足的,那么就需要下面的幾種方法了

方法二:基礎版(手寫遞歸實現(xiàn))

下面是一個實現(xiàn) deepClone 函數(shù)封裝的例子,通過 for in 遍歷傳入?yún)?shù)的屬性值,如果值是引用類型則再次遞歸調(diào)用該函數(shù),如果是基礎數(shù)據(jù)類型就直接復制

let obj1 = {a:{b:1}
}
function deepClone(obj) { let cloneObj = {}for(let key in obj) {                 //遍歷if(typeof obj[key] ==='object') { cloneObj[key] = deepClone(obj[key])  //是對象就再次調(diào)用該函數(shù)遞歸} else {cloneObj[key] = obj[key]  //基本類型的話直接復制值}}return cloneObj
}
let obj2 = deepClone(obj1);
obj1.a.b = 2;
console.log(obj2);   //  {a:{b:1}}

雖然利用遞歸能實現(xiàn)一個深拷貝,但是同上面的 JSON.stringify 一樣,還是有一些問題沒有完全解決,例如:

  • 這個深拷貝函數(shù)并不能復制不可枚舉的屬性以及 Symbol 類型;
  • 這種方法只是針對普通的引用類型的值做遞歸復制,而對于 Array、Date、RegExp、Error、Function 這樣的引用類型并不能正確地拷貝;
  • 對象的屬性里面成環(huán),即循環(huán)引用沒有解決。

這種基礎版本的寫法也比較簡單,可以應對大部分的應用情況。但是你在面試的過程中,如果只能寫出這樣的一個有缺陷的深拷貝方法,有可能不會通過。

所以為了“拯救”這些缺陷,下面我?guī)阋黄鹂纯锤倪M的版本,以便于你可以在面試種呈現(xiàn)出更好的深拷貝方法,贏得面試官的青睞。

方法三:改進版(改進后遞歸實現(xiàn))

針對上面幾個待解決問題,我先通過四點相關的理論告訴你分別應該怎么做。

  • 針對能夠遍歷對象的不可枚舉屬性以及 Symbol 類型,我們可以使用 Reflect.ownKeys 方法;
  • 當參數(shù)為 Date、RegExp 類型,則直接生成一個新的實例返回;
  • 利用 ObjectgetOwnPropertyDescriptors 方法可以獲得對象的所有屬性,以及對應的特性,順便結(jié)合 Object.create 方法創(chuàng)建一個新對象,并繼承傳入原對象的原型鏈;
  • 利用 WeakMap 類型作為 Hash 表,因為 WeakMap 是弱引用類型,可以有效防止內(nèi)存泄漏(你可以關注一下 MapweakMap 的關鍵區(qū)別,這里要用 weakMap),作為檢測循環(huán)引用很有幫助,如果存在循環(huán),則引用直接返回 WeakMap 存儲的值

如果你在考慮到循環(huán)引用的問題之后,還能用 WeakMap 來很好地解決,并且向面試官解釋這樣做的目的,那么你所展示的代碼,以及你對問題思考的全面性,在面試官眼中應該算是合格的了

實現(xiàn)深拷貝

const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)const deepClone = function (obj, hash = new WeakMap()) {if (obj.constructor === Date) {return new Date(obj)       // 日期對象直接返回一個新的日期對象}if (obj.constructor === RegExp){return new RegExp(obj)     //正則對象直接返回一個新的正則對象}//如果循環(huán)引用了就用 weakMap 來解決if (hash.has(obj)) {return hash.get(obj)}let allDesc = Object.getOwnPropertyDescriptors(obj)//遍歷傳入?yún)?shù)所有鍵的特性let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)// 把cloneObj原型復制到obj上hash.set(obj, cloneObj)for (let key of Reflect.ownKeys(obj)) { cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]}return cloneObj
}
// 下面是驗證代碼
let obj = {num: 0,str: '',boolean: true,unf: undefined,nul: null,obj: { name: '我是一個對象', id: 1 },arr: [0, 1, 2],func: function () { console.log('我是一個函數(shù)') },date: new Date(0),reg: new RegExp('/我是一個正則/ig'),[Symbol('1')]: 1,
};
Object.defineProperty(obj, 'innumerable', {enumerable: false, value: '不可枚舉屬性' }
);
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj    // 設置loop成循環(huán)引用的屬性
let cloneObj = deepClone(obj)
cloneObj.arr.push(4)
console.log('obj', obj)
console.log('cloneObj', cloneObj)

我們看一下結(jié)果,cloneObjobj 的基礎上進行了一次深拷貝,cloneObj 里的 arr 數(shù)組進行了修改,并未影響到 obj.arr 的變化,如下圖所示

TCP粘包是怎么回事,如何處理?

默認情況下, TCP 連接會啟?延遲傳送算法 (Nagle 算法), 在數(shù)據(jù)發(fā)送之前緩存他們. 如果短時間有多個數(shù)據(jù)發(fā)送, 會緩沖到?起作?次發(fā)送 (緩沖??? socket.bufferSize ), 這樣可以減少 IO 消耗提?性能.

如果是傳輸?件的話, 那么根本不?處理粘包的問題, 來?個包拼?個包就好了。但是如果是多條消息, 或者是別的?途的數(shù)據(jù)那么就需要處理粘包.

下面看?個例?, 連續(xù)調(diào)?兩次 send 分別發(fā)送兩段數(shù)據(jù) data1 和 data2, 在接收端有以下?種常?的情況:
A. 先接收到 data1, 然后接收到 data2 .
B. 先接收到 data1 的部分數(shù)據(jù), 然后接收到 data1 余下的部分以及 data2 的全部.
C. 先接收到了 data1 的全部數(shù)據(jù)和 data2 的部分數(shù)據(jù), 然后接收到了 data2 的余下的數(shù)據(jù).
D. ?次性接收到了 data1 和 data2 的全部數(shù)據(jù).

其中的 BCD 就是我們常?的粘包的情況. ?對于處理粘包的問題, 常?的解決?案有:

  • 多次發(fā)送之前間隔?個等待時間:只需要等上?段時間再進?下?次 send 就好, 適?于交互頻率特別低的場景. 缺點也很明顯, 對于?較頻繁的場景??傳輸效率實在太低,不過?乎不?做什么處理.
  • 關閉 Nagle 算法:關閉 Nagle 算法, 在 Node.js 中你可以通過 socket.setNoDelay() ?法來關閉 Nagle 算法, 讓每?次 send 都不緩沖直接發(fā)送。該?法?較適?于每次發(fā)送的數(shù)據(jù)都?較? (但不是?件那么?), 并且頻率不是特別?的場景。如果是每次發(fā)送的數(shù)據(jù)量?較?, 并且頻率特別?的, 關閉 Nagle 純屬?廢武功。另外, 該?法不適?于?絡較差的情況, 因為 Nagle 算法是在服務端進?的包合并情況, 但是如果短時間內(nèi)客戶端的?絡情況不好, 或者應?層由于某些原因不能及時將 TCP 的數(shù)據(jù) recv, 就會造成多個包在客戶端緩沖從?粘包的情況。 (如果是在穩(wěn)定的機房內(nèi)部通信那么這個概率是?較?可以選擇忽略的)
  • 進?封包/拆包: 封包/拆包是?前業(yè)內(nèi)常?的解決?案了。即給每個數(shù)據(jù)包在發(fā)送之前, 于其前/后放?些有特征的數(shù)據(jù), 然后收到數(shù)據(jù)的時 候根據(jù)特征數(shù)據(jù)分割出來各個數(shù)據(jù)包。

Proxy代理

proxy在目標對象的外層搭建了一層攔截,外界對目標對象的某些操作,必須通過這層攔截

var proxy = new Proxy(target, handler);

new Proxy()表示生成一個Proxy實例,target參數(shù)表示所要攔截的目標對象,handler參數(shù)也是一個對象,用來定制攔截行為

var target = {name: 'poetries'};var logHandler = {get: function(target, key) {console.log(`${key} 被讀取`);return target[key];},set: function(target, key, value) {console.log(`${key} 被設置為 ${value}`);target[key] = value;}}var targetWithLog = new Proxy(target, logHandler);targetWithLog.name; // 控制臺輸出:name 被讀取targetWithLog.name = 'others'; // 控制臺輸出:name 被設置為 othersconsole.log(target.name); // 控制臺輸出: others
  • targetWithLog 讀取屬性的值時,實際上執(zhí)行的是 logHandler.get :在控制臺輸出信息,并且讀取被代理對象 target 的屬性。
  • targetWithLog 設置屬性值時,實際上執(zhí)行的是 logHandler.set :在控制臺輸出信息,并且設置被代理對象 target 的屬性的值
// 由于攔截函數(shù)總是返回35,所以訪問任何屬性都得到35
var proxy = new Proxy({}, {get: function(target, property) {return 35;}
});proxy.time // 35
proxy.name // 35
proxy.title // 35

Proxy 實例也可以作為其他對象的原型對象

var proxy = new Proxy({}, {get: function(target, property) {return 35;}
});let obj = Object.create(proxy);
obj.time // 35

proxy對象是obj對象的原型,obj對象本身并沒有time屬性,所以根據(jù)原型鏈,會在proxy對象上讀取該屬性,導致被攔截

Proxy的作用

對于代理模式 Proxy 的作用主要體現(xiàn)在三個方面

  • 攔截和監(jiān)視外部對對象的訪問
  • 降低函數(shù)或類的復雜度
  • 在復雜操作前對操作進行校驗或?qū)λ栀Y源進行管理

Proxy所能代理的范圍–handler

實際上 handler 本身就是ES6所新設計的一個對象.它的作用就是用來 自定義代理對象的各種可代理操作 。它本身一共有13中方法,每種方法都可以代理一種操作.其13種方法如下

// 在讀取代理對象的原型時觸發(fā)該操作,比如在執(zhí)行 Object.getPrototypeOf(proxy) 時。
handler.getPrototypeOf()// 在設置代理對象的原型時觸發(fā)該操作,比如在執(zhí)行 Object.setPrototypeOf(proxy, null) 時。
handler.setPrototypeOf()// 在判斷一個代理對象是否是可擴展時觸發(fā)該操作,比如在執(zhí)行 Object.isExtensible(proxy) 時。
handler.isExtensible()// 在讓一個代理對象不可擴展時觸發(fā)該操作,比如在執(zhí)行 Object.preventExtensions(proxy) 時。
handler.preventExtensions()// 在獲取代理對象某個屬性的屬性描述時觸發(fā)該操作,比如在執(zhí)行 Object.getOwnPropertyDescriptor(proxy, "foo") 時。
handler.getOwnPropertyDescriptor()// 在定義代理對象某個屬性時的屬性描述時觸發(fā)該操作,比如在執(zhí)行 Object.defineProperty(proxy, "foo", {}) 時。
andler.defineProperty()// 在判斷代理對象是否擁有某個屬性時觸發(fā)該操作,比如在執(zhí)行 "foo" in proxy 時。
handler.has()// 在讀取代理對象的某個屬性時觸發(fā)該操作,比如在執(zhí)行 proxy.foo 時。
handler.get()// 在給代理對象的某個屬性賦值時觸發(fā)該操作,比如在執(zhí)行 proxy.foo = 1 時。
handler.set()// 在刪除代理對象的某個屬性時觸發(fā)該操作,比如在執(zhí)行 delete proxy.foo 時。
handler.deleteProperty()// 在獲取代理對象的所有屬性鍵時觸發(fā)該操作,比如在執(zhí)行 Object.getOwnPropertyNames(proxy) 時。
handler.ownKeys()// 在調(diào)用一個目標對象為函數(shù)的代理對象時觸發(fā)該操作,比如在執(zhí)行 proxy() 時。
handler.apply()// 在給一個目標對象為構(gòu)造函數(shù)的代理對象構(gòu)造實例時觸發(fā)該操作,比如在執(zhí)行new proxy() 時。
handler.construct()

為何Proxy不能被Polyfill

  • 如class可以用function模擬;promise可以用callback模擬
  • 但是proxy不能用Object.defineProperty模擬

目前谷歌的polyfill只能實現(xiàn)部分的功能,如get、set https://github.com/GoogleChrome/proxy-polyfill

// commonJS require
const proxyPolyfill = require('proxy-polyfill/src/proxy')();// Your environment may also support transparent rewriting of commonJS to ES6:
import ProxyPolyfillBuilder from 'proxy-polyfill/src/proxy';
const proxyPolyfill = ProxyPolyfillBuilder();// Then use...
const myProxy = new proxyPolyfill(...);

HTTP狀態(tài)碼

狀態(tài)碼的類別:

類別原因描述
1xxInformational(信息性狀態(tài)碼)接受的請求正在處理
2xxSuccess(成功狀態(tài)碼)請求正常處理完畢
3xxRedirection(重定向狀態(tài)碼)需要進行附加操作一完成請求
4xxClient Error (客戶端錯誤狀態(tài)碼)服務器無法處理請求
5xxServer Error(服務器錯誤狀態(tài)碼)服務器處理請求出錯

1. 2XX (Success 成功狀態(tài)碼)

狀態(tài)碼2XX表示請求被正常處理了。

(1)200 OK

200 OK表示客戶端發(fā)來的請求被服務器端正常處理了。

(2)204 No Content

該狀態(tài)碼表示客戶端發(fā)送的請求已經(jīng)在服務器端正常處理了,但是沒有返回的內(nèi)容,響應報文中不包含實體的主體部分。一般在只需要從客戶端往服務器端發(fā)送信息,而服務器端不需要往客戶端發(fā)送內(nèi)容時使用。

(3)206 Partial Content

該狀態(tài)碼表示客戶端進行了范圍請求,而服務器端執(zhí)行了這部分的 GET 請求。響應報文中包含由 Content-Range 指定范圍的實體內(nèi)容。

2. 3XX (Redirection 重定向狀態(tài)碼)

3XX 響應結(jié)果表明瀏覽器需要執(zhí)行某些特殊的處理以正確處理請求。

(1)301 Moved Permanently

永久重定向。 該狀態(tài)碼表示請求的資源已經(jīng)被分配了新的 URI,以后應使用資源指定的 URI。新的 URI 會在 HTTP 響應頭中的 Location 首部字段指定。若用戶已經(jīng)把原來的URI保存為書簽,此時會按照 Location 中新的URI重新保存該書簽。同時,搜索引擎在抓取新內(nèi)容的同時也將舊的網(wǎng)址替換為重定向之后的網(wǎng)址。

使用場景:

  • 當我們想換個域名,舊的域名不再使用時,用戶訪問舊域名時用301就重定向到新的域名。其實也是告訴搜索引擎收錄的域名需要對新的域名進行收錄。
  • 在搜索引擎的搜索結(jié)果中出現(xiàn)了不帶www的域名,而帶www的域名卻沒有收錄,這個時候可以用301重定向來告訴搜索引擎我們目標的域名是哪一個。
(2)302 Found

臨時重定向。 該狀態(tài)碼表示請求的資源被分配到了新的 URI,希望用戶(本次)能使用新的 URI 訪問資源。和 301 Moved Permanently 狀態(tài)碼相似,但是 302 代表的資源不是被永久重定向,只是臨時性質(zhì)的。也就是說已移動的資源對應的 URI 將來還有可能發(fā)生改變。若用戶把 URI 保存成書簽,但不會像 301 狀態(tài)碼出現(xiàn)時那樣去更新書簽,而是仍舊保留返回 302 狀態(tài)碼的頁面對應的 URI。同時,搜索引擎會抓取新的內(nèi)容而保留舊的網(wǎng)址。因為服務器返回302代碼,搜索引擎認為新的網(wǎng)址只是暫時的。

使用場景:

  • 當我們在做活動時,登錄到首頁自動重定向,進入活動頁面。
  • 未登陸的用戶訪問用戶中心重定向到登錄頁面。
  • 訪問404頁面重新定向到首頁。
(3)303 See Other

該狀態(tài)碼表示由于請求對應的資源存在著另一個 URI,應使用 GET 方法定向獲取請求的資源。
303 狀態(tài)碼和 302 Found 狀態(tài)碼有著相似的功能,但是 303 狀態(tài)碼明確表示客戶端應當采用 GET 方法獲取資源。

303 狀態(tài)碼通常作為 PUT 或 POST 操作的返回結(jié)果,它表示重定向鏈接指向的不是新上傳的資源,而是另外一個頁面,比如消息確認頁面或上傳進度頁面。而請求重定向頁面的方法要總是使用 GET。

注意:

  • 當 301、302、303 響應狀態(tài)碼返回時,幾乎所有的瀏覽器都會把 POST 改成GET,并刪除請求報文內(nèi)的主體,之后請求會再次自動發(fā)送。
  • 301、302 標準是禁止將 POST 方法變成 GET方法的,但實際大家都會這么做。
(4)304 Not Modified

瀏覽器緩存相關。 該狀態(tài)碼表示客戶端發(fā)送附帶條件的請求時,服務器端允許請求訪問資源,但未滿足條件的情況。304 狀態(tài)碼返回時,不包含任何響應的主體部分。304 雖然被劃分在 3XX 類別中,但是和重定向沒有關系。

帶條件的請求(Http 條件請求):使用 Get方法 請求,請求報文中包含(if-match、if-none-match、if-modified-since、if-unmodified-since、if-range)中任意首部。

狀態(tài)碼304并不是一種錯誤,而是告訴客戶端有緩存,直接使用緩存中的數(shù)據(jù)。返回頁面的只有頭部信息,是沒有內(nèi)容部分的,這樣在一定程度上提高了網(wǎng)頁的性能。

(5)307 Temporary Redirect

307表示臨時重定向。 該狀態(tài)碼與 302 Found 有著相同含義,盡管 302 標準禁止 POST 變成 GET,但是實際使用時還是這樣做了。

307 會遵守瀏覽器標準,不會從 POST 變成 GET。但是對于處理請求的行為時,不同瀏覽器還是會出現(xiàn)不同的情況。規(guī)范要求瀏覽器繼續(xù)向 Location 的地址 POST 內(nèi)容。規(guī)范要求瀏覽器繼續(xù)向 Location 的地址 POST 內(nèi)容。

3. 4XX (Client Error 客戶端錯誤狀態(tài)碼)

4XX 的響應結(jié)果表明客戶端是發(fā)生錯誤的原因所在。

(1)400 Bad Request

該狀態(tài)碼表示請求報文中存在語法錯誤。當錯誤發(fā)生時,需修改請求的內(nèi)容后再次發(fā)送請求。另外,瀏覽器會像 200 OK 一樣對待該狀態(tài)碼。

(2)401 Unauthorized

該狀態(tài)碼表示發(fā)送的請求需要有通過 HTTP 認證(BASIC 認證、DIGEST 認證)的認證信息。若之前已進行過一次請求,則表示用戶認證失敗

返回含有 401 的響應必須包含一個適用于被請求資源的 WWW-Authenticate 首部用以質(zhì)詢(challenge)用戶信息。當瀏覽器初次接收到 401 響應,會彈出認證用的對話窗口。

以下情況會出現(xiàn)401:

  • 401.1 - 登錄失敗。
  • 401.2 - 服務器配置導致登錄失敗。
  • 401.3 - 由于 ACL 對資源的限制而未獲得授權。
  • 401.4 - 篩選器授權失敗。
  • 401.5 - ISAPI/CGI 應用程序授權失敗。
  • 401.7 - 訪問被 Web 服務器上的 URL 授權策略拒絕。這個錯誤代碼為 IIS 6.0 所專用。
(3)403 Forbidden

該狀態(tài)碼表明請求資源的訪問被服務器拒絕了,服務器端沒有必要給出詳細理由,但是可以在響應報文實體的主體中進行說明。進入該狀態(tài)后,不能再繼續(xù)進行驗證。該訪問是永久禁止的,并且與應用邏輯密切相關。

IIS 定義了許多不同的 403 錯誤,它們指明更為具體的錯誤原因:

  • 403.1 - 執(zhí)行訪問被禁止。
  • 403.2 - 讀訪問被禁止。
  • 403.3 - 寫訪問被禁止。
  • 403.4 - 要求 SSL。
  • 403.5 - 要求 SSL 128。
  • 403.6 - IP 地址被拒絕。
  • 403.7 - 要求客戶端證書。
  • 403.8 - 站點訪問被拒絕。
  • 403.9 - 用戶數(shù)過多。
  • 403.10 - 配置無效。
  • 403.11 - 密碼更改。
  • 403.12 - 拒絕訪問映射表。
  • 403.13 - 客戶端證書被吊銷。
  • 403.14 - 拒絕目錄列表。
  • 403.15 - 超出客戶端訪問許可。
  • 403.16 - 客戶端證書不受信任或無效。
  • 403.17 - 客戶端證書已過期或尚未生效
  • 403.18 - 在當前的應用程序池中不能執(zhí)行所請求的 URL。這個錯誤代碼為 IIS 6.0 所專用。
  • 403.19 - 不能為這個應用程序池中的客戶端執(zhí)行 CGI。這個錯誤代碼為 IIS 6.0 所專用。
  • 403.20 - Passport 登錄失敗。這個錯誤代碼為 IIS 6.0 所專用。
(4)404 Not Found

該狀態(tài)碼表明服務器上無法找到請求的資源。除此之外,也可以在服務器端拒絕請求且不想說明理由時使用。
以下情況會出現(xiàn)404:

  • 404.0 -(無) – 沒有找到文件或目錄。
  • 404.1 - 無法在所請求的端口上訪問 Web 站點。
  • 404.2 - Web 服務擴展鎖定策略阻止本請求。
  • 404.3 - MIME 映射策略阻止本請求。
(5)405 Method Not Allowed

該狀態(tài)碼表示客戶端請求的方法雖然能被服務器識別,但是服務器禁止使用該方法。GET 和 HEAD 方法,服務器應該總是允許客戶端進行訪問??蛻舳丝梢酝ㄟ^ OPTIONS 方法(預檢)來查看服務器允許的訪問方法, 如下

Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE

4. 5XX (Server Error 服務器錯誤狀態(tài)碼)

5XX 的響應結(jié)果表明服務器本身發(fā)生錯誤.

(1)500 Internal Server Error

該狀態(tài)碼表明服務器端在執(zhí)行請求時發(fā)生了錯誤。也有可能是 Web 應用存在的 bug 或某些臨時的故障。

(2)502 Bad Gateway

該狀態(tài)碼表明扮演網(wǎng)關或代理角色的服務器,從上游服務器中接收到的響應是無效的。注意,502 錯誤通常不是客戶端能夠修復的,而是需要由途經(jīng)的 Web 服務器或者代理服務器對其進行修復。以下情況會出現(xiàn)502:

  • 502.1 - CGI (通用網(wǎng)關接口)應用程序超時。
  • 502.2 - CGI (通用網(wǎng)關接口)應用程序出錯。
(3)503 Service Unavailable

該狀態(tài)碼表明服務器暫時處于超負載或正在進行停機維護,現(xiàn)在無法處理請求。如果事先得知解除以上狀況需要的時間,最好寫入 RetryAfter 首部字段再返回給客戶端。

使用場景:

  • 服務器停機維護時,主動用503響應請求;
  • nginx 設置限速,超過限速,會返回503。
(4)504 Gateway Timeout

該狀態(tài)碼表示網(wǎng)關或者代理的服務器無法在規(guī)定的時間內(nèi)獲得想要的響應。他是HTTP 1.1中新加入的。

使用場景:代碼執(zhí)行時間超時,或者發(fā)生了死循環(huán)。

5. 總結(jié)

(1)2XX 成功

  • 200 OK,表示從客戶端發(fā)來的請求在服務器端被正確處理
  • 204 No content,表示請求成功,但響應報文不含實體的主體部分
  • 205 Reset Content,表示請求成功,但響應報文不含實體的主體部分,但是與 204 響應不同在于要求請求方重置內(nèi)容
  • 206 Partial Content,進行范圍請求

(2)3XX 重定向

  • 301 moved permanently,永久性重定向,表示資源已被分配了新的 URL
  • 302 found,臨時性重定向,表示資源臨時被分配了新的 URL
  • 303 see other,表示資源存在著另一個 URL,應使用 GET 方法獲取資源
  • 304 not modified,表示服務器允許訪問資源,但因發(fā)生請求未滿足條件的情況
  • 307 temporary redirect,臨時重定向,和302含義類似,但是期望客戶端保持請求方法不變向新的地址發(fā)出請求

(3)4XX 客戶端錯誤

  • 400 bad request,請求報文存在語法錯誤
  • 401 unauthorized,表示發(fā)送的請求需要有通過 HTTP 認證的認證信息
  • 403 forbidden,表示對請求資源的訪問被服務器拒絕
  • 404 not found,表示在服務器上沒有找到請求的資源

(4)5XX 服務器錯誤

  • 500 internal sever error,表示服務器端在執(zhí)行請求時發(fā)生了錯誤
  • 501 Not Implemented,表示服務器不支持當前請求所需要的某個功能
  • 503 service unavailable,表明服務器暫時處于超負載或正在停機維護,無法處理請求

執(zhí)行上下文

當執(zhí)行 JS 代碼時,會產(chǎn)生三種執(zhí)行上下文

  • 全局執(zhí)行上下文
  • 函數(shù)執(zhí)行上下文
  • eval 執(zhí)行上下文

每個執(zhí)行上下文中都有三個重要的屬性

  • 變量對象(VO),包含變量、函數(shù)聲明和函數(shù)的形參,該屬性只能在全局上下文中訪問
  • 作用域鏈(JS 采用詞法作用域,也就是說變量的作用域是在定義時就決定了)
  • this
var a = 10
function foo(i) {var b = 20
}
foo()

對于上述代碼,執(zhí)行棧中有兩個上下文:全局上下文和函數(shù) foo 上下文。

stack = [globalContext,fooContext
]

對于全局上下文來說,VO大概是這樣的

globalContext.VO === globe
globalContext.VO = {a: undefined,foo: <Function>,
}

對于函數(shù) foo 來說,VO 不能訪問,只能訪問到活動對象(AO

fooContext.VO === foo.AO
fooContext.AO {i: undefined,b: undefined,arguments: <>
}
// arguments 是函數(shù)獨有的對象(箭頭函數(shù)沒有)
// 該對象是一個偽數(shù)組,有 `length` 屬性且可以通過下標訪問元素
// 該對象中的 `callee` 屬性代表函數(shù)本身
// `caller` 屬性代表函數(shù)的調(diào)用者

對于作用域鏈,可以把它理解成包含自身變量對象和上級變量對象的列表,通過 [[Scope]]屬性查找上級變量

fooContext.[[Scope]] = [globalContext.VO
]
fooContext.Scope = fooContext.[[Scope]] + fooContext.VO
fooContext.Scope = [fooContext.VO,globalContext.VO
]

接下來讓我們看一個老生常談的例子,var

b() // call b
console.log(a) // undefinedvar a = 'Hello world'function b() {console.log('call b')
}

想必以上的輸出大家肯定都已經(jīng)明白了,這是因為函數(shù)和變量提升的原因。通常提升的解釋是說將聲明的代碼移動到了頂部,這其實沒有什么錯誤,便于大家理解。但是更準確的解釋應該是:在生成執(zhí)行上下文時,會有兩個階段。第一個階段是創(chuàng)建的階段(具體步驟是創(chuàng)建 VO),JS 解釋器會找出需要提升的變量和函數(shù),并且給他們提前在內(nèi)存中開辟好空間,函數(shù)的話會將整個函數(shù)存入內(nèi)存中,變量只聲明并且賦值為 undefined,所以在第二個階段,也就是代碼執(zhí)行階段,我們可以直接提前使用。

  • 在提升的過程中,相同的函數(shù)會覆蓋上一個函數(shù),并且函數(shù)優(yōu)先于變量提升
b() // call b secondfunction b() {console.log('call b fist')
}
function b() {console.log('call b second')
}
var b = 'Hello world'

var會產(chǎn)生很多錯誤,所以在 ES6中引入了 letlet不能在聲明前使用,但是這并不是常說的 let 不會提升,let 提升了聲明但沒有賦值,因為臨時死區(qū)導致了并不能在聲明前使用。

  • 對于非匿名的立即執(zhí)行函數(shù)需要注意以下一點
var foo = 1
(function foo() {foo = 10console.log(foo)
}()) // -> ? foo() { foo = 10 ; console.log(foo) }

因為當 JS 解釋器在遇到非匿名的立即執(zhí)行函數(shù)時,會創(chuàng)建一個輔助的特定對象,然后將函數(shù)名稱作為這個對象的屬性,因此函數(shù)內(nèi)部才可以訪問到 foo,但是這個值又是只讀的,所以對它的賦值并不生效,所以打印的結(jié)果還是這個函數(shù),并且外部的值也沒有發(fā)生更改。

specialObject = {};Scope = specialObject + Scope;foo = new FunctionExpression;
foo.[[Scope]] = Scope;
specialObject.foo = foo; // {DontDelete}, {ReadOnly}delete Scope[0]; // remove specialObject from the front of scope chain

總結(jié)

執(zhí)行上下文可以簡單理解為一個對象:

它包含三個部分:

  • 變量對象(VO)
  • 作用域鏈(詞法作用域)
  • this指向

它的類型:

  • 全局執(zhí)行上下文
  • 函數(shù)執(zhí)行上下文
  • eval執(zhí)行上下文

代碼執(zhí)行過程:

  • 創(chuàng)建 全局上下文 (global EC)
  • 全局執(zhí)行上下文 (caller) 逐行 自上而下 執(zhí)行。遇到函數(shù)時,函數(shù)執(zhí)行上下文 (callee) 被push到執(zhí)行棧頂層
  • 函數(shù)執(zhí)行上下文被激活,成為 active EC, 開始執(zhí)行函數(shù)中的代碼,caller 被掛起
  • 函數(shù)執(zhí)行完后,calleepop移除出執(zhí)行棧,控制權交還全局上下文 (caller),繼續(xù)執(zhí)行

介紹一下 Tree Shaking

對tree-shaking的了解

作用:

它表示在打包的時候會去除一些無用的代碼

原理

  • ES6的模塊引入是靜態(tài)分析的,所以在編譯時能正確判斷到底加載了哪些模塊
  • 分析程序流,判斷哪些變量未被使用、引用,進而刪除此代碼

特點:

  • 在生產(chǎn)模式下它是默認開啟的,但是由于經(jīng)過babel編譯全部模塊被封裝成IIFE,它存在副作用無法被tree-shaking
  • 可以在package.json中配置sideEffects來指定哪些文件是有副作用的。它有兩種值,一個是布爾類型,如果是false則表示所有文件都沒有副作用;如果是一個數(shù)組的話,數(shù)組里的文件路徑表示改文件有副作用
  • rollupwebpack中對tree-shaking的層度不同,例如對babel轉(zhuǎn)譯后的class,如果babel的轉(zhuǎn)譯是寬松模式下的話(也就是loosetrue),webpack依舊會認為它有副作用不會tree-shaking掉,而rollup會。這是因為rollup有程序流分析的功能,可以更好的判斷代碼是否真正會產(chǎn)生副作用。

原理

  • ES6 Module 引入進行靜態(tài)分析,故而編譯的時候正確判斷到底加載了那些模塊
  • 靜態(tài)分析程序流,判斷那些模塊和變量未被使用或者引用,進而刪除對應代碼

依賴于import/export

通過導入所有的包后再進行條件獲取。如下:

import foo from "foo";
import bar from "bar";if(condition) {// foo.xxxx
} else {// bar.xxx
}

ES6的import語法完美可以使用tree shaking,因為可以在代碼不運行的情況下就能分析出不需要的代碼

CommonJS的動態(tài)特性模塊意味著tree shaking不適用 。因為它是不可能確定哪些模塊實際運行之前是需要的或者是不需要的。在ES6中,進入了完全靜態(tài)的導入語法:import。這也意味著下面的導入是不可行的:

// 不可行,ES6 的import是完全靜態(tài)的
if(condition) {myDynamicModule = require("foo");
} else {myDynamicModule = require("bar");
}

模塊化

js 中現(xiàn)在比較成熟的有四種模塊加載方案:

  • 第一種是 CommonJS 方案,它通過 require 來引入模塊,通過 module.exports 定義模塊的輸出接口。這種模塊加載方案是服務器端的解決方案,它是以同步的方式來引入模塊的,因為在服務端文件都存儲在本地磁盤,所以讀取非常快,所以以同步的方式加載沒有問題。但如果是在瀏覽器端,由于模塊的加載是使用網(wǎng)絡請求,因此使用異步加載的方式更加合適。
  • 第二種是 AMD 方案,這種方案采用異步加載的方式來加載模塊,模塊的加載不影響后面語句的執(zhí)行,所有依賴這個模塊的語句都定義在一個回調(diào)函數(shù)里,等到加載完成后再執(zhí)行回調(diào)函數(shù)。require.js 實現(xiàn)了 AMD 規(guī)范
  • 第三種是 CMD 方案,這種方案和 AMD 方案都是為了解決異步模塊加載的問題,sea.js 實現(xiàn)了 CMD 規(guī)范。它和require.js的區(qū)別在于模塊定義時對依賴的處理不同和對依賴模塊的執(zhí)行時機的處理不同。
  • 第四種方案是 ES6 提出的方案,使用 import 和 export 的形式來導入導出模塊

在有 Babel 的情況下,我們可以直接使用 ES6的模塊化

// file a.js
export function a() {}
export function b() {}
// file b.js
export default function() {}import {a, b} from './a.js'
import XXX from './b.js'

CommonJS

CommonJsNode 獨有的規(guī)范,瀏覽器中使用就需要用到 Browserify解析了。

// a.js
module.exports = {a: 1
}
// or
exports.a = 1// b.js
var module = require('./a.js')
module.a // -> log 1

在上述代碼中,module.exportsexports 很容易混淆,讓我們來看看大致內(nèi)部實現(xiàn)

var module = require('./a.js')
module.a
// 這里其實就是包裝了一層立即執(zhí)行函數(shù),這樣就不會污染全局變量了,
// 重要的是 module 這里,module 是 Node 獨有的一個變量
module.exports = {a: 1
}
// 基本實現(xiàn)
var module = {exports: {} // exports 就是個空對象
}
// 這個是為什么 exports 和 module.exports 用法相似的原因
var exports = module.exports
var load = function (module) {// 導出的東西var a = 1module.exports = areturn module.exports
};

再來說說 module.exportsexports,用法其實是相似的,但是不能對 exports 直接賦值,不會有任何效果。

對于 CommonJSES6 中的模塊化的兩者區(qū)別是:

  • 前者支持動態(tài)導入,也就是 require(${path}/xx.js),后者目前不支持,但是已有提案,前者是同步導入,因為用于服務端,文件都在本地,同步導入即使卡住主線程影響也不大。
  • 而后者是異步導入,因為用于瀏覽器,需要下載文件,如果也采用同步導入會對渲染有很大影響
  • 前者在導出時都是值拷貝,就算導出的值變了,導入的值也不會改變,所以如果想更新值,必須重新導入一次。
  • 但是后者采用實時綁定的方式,導入導出的值都指向同一個內(nèi)存地址,所以導入值會跟隨導出值變化
  • 后者會編譯成 require/exports 來執(zhí)行的

AMD

AMD 是由 RequireJS 提出的

AMD 和 CMD 規(guī)范的區(qū)別?

  • 第一個方面是在模塊定義時對依賴的處理不同。AMD推崇依賴前置,在定義模塊的時候就要聲明其依賴的模塊。而 CMD 推崇就近依賴,只有在用到某個模塊的時候再去 require。
  • 第二個方面是對依賴模塊的執(zhí)行時機處理不同。首先 AMD 和 CMD 對于模塊的加載方式都是異步加載,不過它們的區(qū)別在于模塊的執(zhí)行時機,AMD 在依賴模塊加載完成后就直接執(zhí)行依賴模塊,依賴模塊的執(zhí)行順序和我們書寫的順序不一定一致。而 CMD在依賴模塊加載完成后并不執(zhí)行,只是下載而已,等到所有的依賴模塊都加載好后,進入回調(diào)函數(shù)邏輯,遇到 require 語句的時候才執(zhí)行對應的模塊,這樣模塊的執(zhí)行順序就和我們書寫的順序保持一致了。
// CMD
define(function(require, exports, module) {var a = require("./a");a.doSomething();// 此處略去 100 行var b = require("./b"); // 依賴可以就近書寫b.doSomething();// ...
});// AMD 默認推薦
define(["./a", "./b"], function(a, b) {// 依賴必須一開始就寫好a.doSomething();// 此處略去 100 行b.doSomething();// ...
})
  • AMDrequirejs 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出,提前執(zhí)行,推崇依賴前置
  • CMDseajs 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出,延遲執(zhí)行,推崇依賴就近
  • CommonJs :模塊輸出的是一個值的 copy,運行時加載,加載的是一個對象(module.exports 屬性),該對象只有在腳本運行完才會生成
  • ES6 Module :模塊輸出的是一個值的引用,編譯時輸出接口,ES6模塊不是對象,它對外接口只是一種靜態(tài)定義,在代碼靜態(tài)解析階段就會生成。

談談對模塊化開發(fā)的理解

  • 我對模塊的理解是,一個模塊是實現(xiàn)一個特定功能的一組方法。在最開始的時候,js 只實現(xiàn)一些簡單的功能,所以并沒有模塊的概念,但隨著程序越來越復雜,代碼的模塊化開發(fā)變得越來越重要。
  • 由于函數(shù)具有獨立作用域的特點,最原始的寫法是使用函數(shù)來作為模塊,幾個函數(shù)作為一個模塊,但是這種方式容易造成全局變量的污染,并且模塊間沒有聯(lián)系。
  • 后面提出了對象寫法,通過將函數(shù)作為一個對象的方法來實現(xiàn),這樣解決了直接使用函數(shù)作為模塊的一些缺點,但是這種辦法會暴露所有的所有的模塊成員,外部代碼可以修改內(nèi)部屬性的值。
  • 現(xiàn)在最常用的是立即執(zhí)行函數(shù)的寫法,通過利用閉包來實現(xiàn)模塊私有作用域的建立,同時不會對全局作用域造成污染。

盒模型

content(元素內(nèi)容) + padding(內(nèi)邊距) + border(邊框) + margin(外邊距)

延伸:box-sizing

  • content-box:默認值,總寬度 = margin + border + padding + width
  • border-box:盒子寬度包含 paddingborder總寬度 = margin + width
  • inherit:從父元素繼承 box-sizing 屬性
http://www.risenshineclean.com/news/65088.html

相關文章:

  • 深圳有哪些做網(wǎng)站的公司好企業(yè)如何做好網(wǎng)絡營銷
  • 會員卡系統(tǒng)多少錢一套谷歌seo公司
  • 網(wǎng)站建設漢獅怎么樣seo研究協(xié)會網(wǎng)
  • 如何創(chuàng)建網(wǎng)站教程視頻微信朋友圈推廣
  • 重慶市建設工程信息網(wǎng)聯(lián)系電話自動seo系統(tǒng)
  • 開了網(wǎng)站建設公司 如何接業(yè)務競價托管公司
  • 哪些網(wǎng)站是用python做的seo如何快速排名百度首頁
  • 如何在百度上做公司做網(wǎng)站百度上首頁
  • 通過做政府門戶網(wǎng)站的實驗獲得什么發(fā)軟文是什么意思
  • 編制網(wǎng)站建設策劃書淘寶排名查詢工具
  • 長沙有哪些網(wǎng)站建設公司經(jīng)典廣告語
  • 做網(wǎng)站地圖的步驟網(wǎng)絡推廣優(yōu)化seo
  • 動態(tài)網(wǎng)站沒有數(shù)據(jù)庫怎么做巨量數(shù)據(jù)官網(wǎng)
  • 小程序開發(fā) 網(wǎng)站建設網(wǎng)站做優(yōu)化好還是推廣好
  • 北京海淀區(qū)建設局網(wǎng)站萬網(wǎng)
  • 做網(wǎng)站定制只要做好關鍵詞優(yōu)化
  • 用戶體驗設計案例鄭州seo技術服務顧問
  • 團隊網(wǎng)站怎么做網(wǎng)絡營銷首先要
  • 世紀佳緣網(wǎng)站開發(fā)語言關鍵字是什么意思
  • 網(wǎng)站建設可行性研究鏈接提交
  • 一個旅游網(wǎng)站怎么做電商網(wǎng)站有哪些
  • 三水網(wǎng)站制作媒體資源網(wǎng)官網(wǎng)
  • nas服務器 做網(wǎng)站佛山優(yōu)化推廣
  • 青島網(wǎng)站建設優(yōu)化質(zhì)量可靠網(wǎng)站推廣的作用
  • 做網(wǎng)站需要視頻銜接怎么口碑最好的it培訓機構(gòu)
  • 小程序怎么做優(yōu)惠券網(wǎng)站制作一個網(wǎng)站的基本步驟
  • 建設網(wǎng)站服務器三十個知識點帶你學黨章
  • 音樂網(wǎng)站模板免費源碼教育培訓報名
  • 便利的集團網(wǎng)站建設朋友圈廣告投放平臺
  • 虞城網(wǎng)站建設廣州最新重大新聞