<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JavaScript面試題大全之基礎(chǔ)面試題(附答案)

          共 29616字,需瀏覽 60分鐘

           ·

          2021-07-27 20:15

          近年來,從事web前端開發(fā)的程序員越來越多,都需要使用JavaScript,這篇文章主要整理一些最常見的JavaScript面試題以及答案。

          介紹JavaScript的數(shù)據(jù)類型

          值類型(基本類型):字符串(String)、數(shù)字(Number)、布爾(Boolean)、對空(Null)、未定義(Undefined)、Symbol(獨(dú)一無二的值)。
          引用數(shù)據(jù)類型:對象(Object)、數(shù)組(Array)、函數(shù)(Function)。
          注:Symbol 是 ES6 引入了一種新的原始數(shù)據(jù)類型,表示獨(dú)一無二的值。

          基本數(shù)據(jù)類型與引用類型在存儲上有什么區(qū)別?

          1.存儲位置不同:
          基本數(shù)據(jù)類型:以棧的形式存儲, 保存與賦值指向數(shù)據(jù)本身, 用typeof 來判斷類型,存儲空間固定。
          引用類型:以堆的形式存儲, 保存于賦值指向?qū)ο蟮囊粋€指針, 用instanceof 來判斷類型 , 存儲空間不固定。
          2.傳值方式不同:
          基本數(shù)據(jù)類型按值傳遞,無法改變一個基本數(shù)據(jù)類型的值
          引用類型按引用傳遞,應(yīng)用類型值可以改變

          判斷 js 類型的方式

          1. typeof
          可以判斷出'string','number','boolean','undefined','symbol'
          但判斷 typeof(null) 時值為 'object'; 判斷數(shù)組和對象時值均為 'object'
          2. instanceof
          原理是構(gòu)造函數(shù)的 prototype 屬性是否出現(xiàn)在對象的原型鏈中的任何位置
          function A() {}let a = new A();a instanceof A     //true,因為 Object.getPrototypeOf(a) === A.prototype;

          3. Object.prototype.toString.call()

          常用于判斷瀏覽器內(nèi)置對象,對于所有基本的數(shù)據(jù)類型都能進(jìn)行判斷,即使是 null 和 undefined

          Object.prototype.toString.call(null)//"[object Null]"Object.prototype.toString.call(undefined)//"[object Undefined]"Object.prototype.toString.call(Object)//"[object Function]"

          4. Array.isArray()

          用于判斷是否為數(shù)組。

          typeof運(yùn)算符和instanceof運(yùn)算符以及isPrototypeOf()方法的區(qū)別

          typeof是一個運(yùn)算符,用于檢測數(shù)據(jù)的類型,比如基本數(shù)據(jù)類型null、undefined、string、number、boolean,以及引用數(shù)據(jù)類型object、function,但是對于正則表達(dá)式、日期、數(shù)組這些引用數(shù)據(jù)類型,它會全部識別為object

          instanceof同樣也是一個運(yùn)算符,它就能很好識別數(shù)據(jù)具體是哪一種引用類型。它與isPrototypeOf的區(qū)別就是它是用來檢測構(gòu)造函數(shù)的原型是否存在于指定對象的原型鏈當(dāng)中;

          而isPrototypeOf是用來檢測調(diào)用此方法的對象是否存在于指定對象的原型鏈中,所以本質(zhì)上就是檢測目標(biāo)不同。

          NaN 是什么

          NaN 即非數(shù)值(Not a Number),NaN 屬性用于引用特殊的非數(shù)字值,該屬性指定的并不是不合法的數(shù)字。

          NaN 屬性 與 Number.Nan 屬性相同。

          提示: 請使用 isNaN() 來判斷一個值是否是數(shù)字。原因是 NaN 與所有值都不相等,包括它自己。

          js中本地對象?內(nèi)置對象?宿主對象?

          本地對象:ECMA-262 把本地對象(native object)定義為“獨(dú)立于宿主環(huán)境的 ECMAScript 實現(xiàn)提供的對象”。

          包含了:Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError。

          內(nèi)置對象:ECMA-262 把內(nèi)置對象(built-in object)定義為“由 ECMAScript 實現(xiàn)提供的、獨(dú)立于宿主環(huán)境的所有對象,在 ECMAScript 程序開始執(zhí)行時出現(xiàn)”。這意味著開發(fā)者不必明確實例化內(nèi)置對象,它已被實例化了。包含了:Gobal 和 Math。

          宿主對象:由ECMAScript實現(xiàn)的宿主環(huán)境提供的對象,可以理解為:瀏覽器提供的對象。所有的BOM和DOM都是宿主對象。

          棧和堆的區(qū)別?

          棧(stack):由編譯器自動分配釋放,存放函數(shù)的參數(shù)值,局部變量等;

          堆(heap):一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時可能由操作系統(tǒng)釋放。

          描述以下變量的區(qū)別:null,undefined或undeclared

          null 表示"沒有對象",即該處不應(yīng)該有值,轉(zhuǎn)為數(shù)值時為0。典型用法是:

          (1) 作為函數(shù)的參數(shù),表示該函數(shù)的參數(shù)不是對象。

          (2) 作為對象原型鏈的終點(diǎn)。

          undefined 表示"缺少值",就是此處應(yīng)該有一個值,但是還沒有定義,轉(zhuǎn)為數(shù)值時為NaN。典型用法是:

          (1)變量被聲明了,但沒有賦值時,就等于undefined。

          (2) 調(diào)用函數(shù)時,應(yīng)該提供的參數(shù)沒有提供,該參數(shù)等于undefined。

          (3)對象沒有賦值的屬性,該屬性的值為undefined。

          (4)函數(shù)沒有返回值時,默認(rèn)返回undefined。

          undeclared :js語法錯誤,沒有申明直接使用,js無法找到對應(yīng)的上下文。

          for..in 和 object.keys的區(qū)別

          Object.keys不會遍歷繼承的原型屬性

          for...in 會遍歷繼承的原型屬性

          JS中的匿名函數(shù)是什么?

          匿名函數(shù):就是沒有函數(shù)名的函數(shù),如:

          (function(x, y){    alert(x + y);  })(2, 3);

          這里創(chuàng)建了一個匿名函數(shù)(在第一個括號內(nèi)),第二個括號用于調(diào)用該匿名函數(shù),并傳入?yún)?shù)。

          解釋 JS 中的函數(shù)提升

          JS允許將聲明移動到頂部的默認(rèn)行為稱為提升。JS中創(chuàng)建函數(shù)的兩種方法是函數(shù)聲明和函數(shù)表達(dá)式。

          函數(shù)聲明

          具有特定參數(shù)的函數(shù)稱為函數(shù)聲明,在JS中創(chuàng)建變量稱為聲明。如:

          hoisted(); // logs "foo"function hoisted() {  console.log('foo');}

          函數(shù)表達(dá)式

          當(dāng)使用表達(dá)式創(chuàng)建函數(shù)時,稱為函數(shù)表達(dá)式。如:

          notHoisted(); // TypeError: notHoisted is not a functionvar notHoisted = function() {   console.log('bar');};

          Js隱式轉(zhuǎn)換介紹

          在js中,當(dāng)運(yùn)算符在運(yùn)算時,如果兩邊數(shù)據(jù)不統(tǒng)一,CPU就無法計算,這時我們編譯器會自動將運(yùn)算符兩邊的數(shù)據(jù)做一個數(shù)據(jù)類型轉(zhuǎn)換,轉(zhuǎn)成一樣的數(shù)據(jù)類型再計算

          這種無需程序員手動轉(zhuǎn)換,而由編譯器自動轉(zhuǎn)換的方式就稱為隱式轉(zhuǎn)換

          例如1 > "0"這行代碼在js中并不會報錯,編譯器在運(yùn)算符時會先把右邊的"0"轉(zhuǎn)成數(shù)字0`然后在比較大小

          隱式轉(zhuǎn)換規(guī)則:

          1. 轉(zhuǎn)成string類型: +(字符串連接符) 2..轉(zhuǎn)成number類型:++/--(自增自減運(yùn)算符) + - * / %(算術(shù)運(yùn)算符) > < >= <= == != === !=== (關(guān)系運(yùn)算符)

          2. 轉(zhuǎn)成boolean類型:!(邏輯非運(yùn)算符)

          例子:

          console.log([] == []) // falseconsole.log([] == ![]) // trueconsole.log([] !== [])  // trueconsole.log(NaN != NaN) // trueconsole.log(null == undefined) // trueconsole.log(null === undefined) // falseconsole.log(1 == true) // trueconsole.log(null > 0) // falseconsole.log(true + 1) // 2console.log(undefined + 1) // NaNconsole.log({} + 1) // [object Object]1console.log([] + {}) // [object Object]console.log([2,3] + [1,2])  // 2,31,2

          Object.is()與原來的比較操作符"==="、"==” 的區(qū)別?

          (1)兩等號判等,會在比較時進(jìn)行類型轉(zhuǎn)換;

          (2)三等號判等(判斷嚴(yán)格),比較時不進(jìn)行隱式類型轉(zhuǎn)換,(類 型不同則會返回false);

          (3)Object.is 在三等號判等的基礎(chǔ)上特別處理了NaN、-0和+0,保證-0和+0不再相同,但Object.is(NaN, NaN)會返回true。Object.is應(yīng)被認(rèn)為有其特殊的用途,而不能用它認(rèn)為它比其它的相等對比更寬松或嚴(yán)格。

          JS 中 == 和 === 區(qū)別是什么?

          1、對于string,number等基礎(chǔ)類型,==和===有區(qū)別

          1)不同類型間比較,==之比較“轉(zhuǎn)化成同一類型后的值”看“值”是否相等,===如果類型不同,其結(jié)果就是不等。
          2)同類型比較,直接進(jìn)行“值”比較,兩者結(jié)果一樣。

          2、對于Array,Object等高級類型,==和===沒有區(qū)別

          進(jìn)行“指針地址”比較。

          3、基礎(chǔ)類型與高級類型,==和===有區(qū)別

          1)對于==,將高級轉(zhuǎn)化為基礎(chǔ)類型,進(jìn)行“值”比較。
          2)因為類型不同,===結(jié)果為false。

          ES5 和 ES6 分別幾種方式聲明變量

          ES5 有倆種:var 和 function

          ES6 有六種:增加四種,let、const、class 和 import

          注意:let、const、class聲明的全局變量再也不會和全局對象的屬性掛鉤。

          什么是事件代理/事件委托?

          事件代理/事件委托是利用事件冒泡的特性,將本應(yīng)該綁定在多個元素上的事件綁定在他們的祖先元素上,尤其在動態(tài)添加子元素的時候,可以非常方便的提高程序性能,減小內(nèi)存空間。

          什么是事件冒泡?什么是事件捕獲?

          冒泡型事件:事件按照從最特定的事件目標(biāo)到最不特定的事件目標(biāo)(document對象)的順序觸發(fā)。

          捕獲型事件:事件從最不精確的對象(document 對象)開始觸發(fā),然后到最精確(也可以在窗口級別捕獲事件,不過必須由開發(fā)人員特別指定)。

          在添加事件時用addEventListener(event,fn,useCapture)方法,基中第3個參數(shù)useCapture是一個Boolean值,用來設(shè)置事件是在事件捕獲時執(zhí)行,還是事件冒泡時執(zhí)行。

          注意:IE瀏覽器用attachEvent()方法,此方法沒有相關(guān)設(shè)置,不過IE的事件模型默認(rèn)是在事件冒泡時執(zhí)行的,也就是在useCapture等于false的時候執(zhí)行,所以把在處理事件時把useCapture設(shè)置為false是比較安全,也實現(xiàn)兼容瀏覽器的效果。

          如何阻止事件冒泡?

          w3c的方法是e.stopPropagation(),IE則是使用e.cancelBubble = true。例如:

          window.event.cancelBubble = true;e.stopPropagation();

          return false也可以阻止冒泡。

          如何阻止默認(rèn)事件?

          w3c的方法是e.preventDefault(),IE則是使用e.returnValue = false,比如:

          function stopDefault( e ) {  if ( e && e.preventDefault )            e.preventDefault(); //IE中阻止函數(shù)器默認(rèn)動作的方式   else             window.event.returnValue = false; }

          return false也能阻止默認(rèn)行為。 

          DOM 事件有哪些階段?談?wù)剬κ录淼睦斫?/span>

          分為三大階段:捕獲階段--目標(biāo)階段--冒泡階段

          事件代理簡單說就是:事件不直接綁定到某元素上,而是綁定到該元素的父元素上,進(jìn)行觸發(fā)事件操作時(例如'click'),再通過條件判斷,執(zhí)行事件觸發(fā)后的語句(例如'alert(e.target.innerhtml)')

          好處:(1)使代碼更簡潔;(2)節(jié)省內(nèi)存開銷

          如何用原生js給一個按鈕綁定兩個onclick事件?

          使用addEventListener的方式來綁定多個事件。例如

          var btn = document.getElementById('btn')btn.addEventListener('click', fn1)btn.addEventListener('click', fn2)function fn1 () {  console.log('我是方法1')  }function fn2 () {  console.log('我是方法2')  }

          響應(yīng)事件有哪些?

          onclick鼠標(biāo)點(diǎn)擊某個對象;onfocus獲取焦點(diǎn);onblur失去焦點(diǎn);onmousedown鼠標(biāo)被按下等,常用的如下:

          1. 鼠標(biāo)單擊事件( onclick )

          2. 鼠標(biāo)經(jīng)過事件( onmouseover )

          3. 鼠標(biāo)移開事件( onmouseout )

          4. 光標(biāo)聚焦事件( onfocus )

          5. 失焦事件( onblur )

          6. 內(nèi)容選中事件( onselect )

          7. 文本框內(nèi)容改變事件( onchange )

          8. 加載事件( onload )

          9. 卸載事件( onunload )

          閉包的概念?優(yōu)缺點(diǎn)?使用場景?

          閉包的概念:閉包就是能讀取其他函數(shù)內(nèi)部變量的函數(shù)。

          1. 避免全局變量的污染

          2. 希望一個變量長期存儲在內(nèi)存中(緩存變量)

          缺點(diǎn):

          1. 內(nèi)存泄露(消耗)

          2. 常駐內(nèi)存,增加內(nèi)存使用量

          使用場景:封裝功能時(需要使用私有的屬性和方法),函數(shù)防抖、函數(shù)節(jié)流、函數(shù)柯里化、給元素偽數(shù)組添加事件需要使用元素的索引值。

          造成內(nèi)存泄露的原因

          1. 意外的全局變量(在函數(shù)內(nèi)部沒有使用var進(jìn)行聲明的變量)

          2. console.log

          3. 閉包

          4. 對象的循環(huán)引用

          5. 未清除的計時器

          6. DOM泄露(獲取到DOM節(jié)點(diǎn)之后,將DOM節(jié)點(diǎn)刪除,但是沒有手動釋放變量,拿對應(yīng)的DOM節(jié)點(diǎn)在變量中還可以訪問到,就會造成泄露)

          new操作符具體干了什么呢?

          1) 創(chuàng)建一個空對象,并且 this 變量引用該對象,同時還繼承了該函數(shù)的原型。

          2) 屬性和方法被加入到 this 引用的對象中。

          3) 新創(chuàng)建的對象由 this 所引用,并且最后隱式的返回 this 。

          javascript中this的指向

          this永遠(yuǎn)指向函數(shù)運(yùn)行時所在的對象,而不是函數(shù)被創(chuàng)建時所在的對象。

          普通的函數(shù)調(diào)用,函數(shù)被誰調(diào)用,this就是誰。

          構(gòu)造函數(shù)的話,如果不用new操作符而直接調(diào)用,那即this指向window。用new操作符生成對象實例后,this就指向了新生成的對象。

          匿名函數(shù)或不處于任何對象中的函數(shù)指向window 。

          如果是call,apply等,指定的this是誰,就是誰。

          談?wù)則his的理解

          1) this總是指向函數(shù)的直接調(diào)用者(而非間接調(diào)用者)

          2) 如果有new關(guān)鍵字,this指向new出來的那個對象

          3) 在事件中,this指向目標(biāo)元素,特殊的是IE的attachEvent中的this總是指向全局對象window。

          eval是做什么的?

          它的功能是把對應(yīng)的字符串解析成JS代碼并運(yùn)行;應(yīng)該避免使用eval,不安全,非常耗性能(2次,一次解析成js語句,一次執(zhí)行)。

          call,apply,bind的區(qū)別

          call apply bind都可以改變函數(shù)調(diào)用的this指向。

          函數(shù).call(對象,arg1,arg2....)函數(shù).apply(對象,[arg1,arg2,...])var ss=函數(shù).bind(對象,arg1,arg2,....)

          1.第一個參數(shù)都是指定函數(shù)內(nèi)部中this的指向(函數(shù)執(zhí)行時所在的作用域),然后根據(jù)指定的作用域,調(diào)用該函數(shù)。

          2.都可以在函數(shù)調(diào)用時傳遞參數(shù)。call,bind方法需要直接傳入,而apply方法需要以數(shù)組的形式傳入。

          3.call,apply方法是在調(diào)用之后立即執(zhí)行函數(shù),而bind方法沒有立即執(zhí)行,需要將函數(shù)再執(zhí)行一遍。

          4.改變this對象的指向問題不僅有call,apply,bind方法,也可以使用that變量來固定this的指向。

          Javascript作用鏈域

          作用域鏈的原理和原型鏈很類似,如果這個變量在自己的作用域中沒有,那么它會尋找父級的,直到最頂層。

          注意:JS沒有塊級作用域,若要形成塊級作用域,可通過(function(){})();立即執(zhí)行的形式實現(xiàn)。

          Javascript原型鏈

          首先理解三個概念:

          • prototype:原型對象,每個函數(shù)都有一個 prototype 屬性,再通過 new 命令實例對象時,該屬性會成為該實例的原型對象。

          • constructor:構(gòu)造函數(shù)。指向原型對象的 constructor

          • __proto__:實例對象的原型

          在javascript中,實例對象與原型之間的鏈接,叫做原型鏈。Javascript解析引擎在讀取一個Object的屬性的值時,會沿著原型鏈向上尋找,如果最終沒有找到,則該屬性值為undefined;如果最終找到該屬性的值,則返回結(jié)果。

          繼承(6種方式)以及優(yōu)缺點(diǎn)

          1.原型鏈繼承

          2.構(gòu)造函數(shù)繼承

          3.組合繼承(原型鏈繼承+構(gòu)造函數(shù)繼承)

          4.原型式繼承

          5.寄生繼承

          6.組合寄生繼承

          代碼示例:

          //借助構(gòu)造函數(shù)實現(xiàn)繼承:缺點(diǎn)是父構(gòu)造函數(shù)的原型鏈繼承不了,若要全部繼承除非將所有屬性和方法定義在構(gòu)造函數(shù)中function Parent1 () {  this.name = 'parent1';}function Child1 () {  //這么做的好處是定義在父構(gòu)造函數(shù)中的引用類型的屬性,對于子構(gòu)造函數(shù)的每個實例來說是獨(dú)立的  //并且在子構(gòu)造函數(shù)實例化時,可以給父構(gòu)造函數(shù)傳參  Parent.call(this);  this.type = 'child1';}
          //借助原型鏈實現(xiàn)繼承:缺點(diǎn)是繼承的引用類型屬性是共享的,子構(gòu)造函數(shù)的實例更改會影響其他實例上的這個屬性,比如 play 屬性function Parent2 () { this.name = 'parent2'; this.play = [1, 2, 3];}function Child2 () { this.type = 'Child2';}Child2.prototype = new Parent2();
          //組合方式:缺點(diǎn)是會執(zhí)行兩次父構(gòu)造函數(shù)function Child3 () { //執(zhí)行第一次 Parent2.call(this); this.type = 'Child3';}Child3.prototype = new Parent2(); //執(zhí)行第二次
          //組合優(yōu)化1,不需要再將定義在父構(gòu)造函數(shù)中的屬性和方法再繼承一次,只需要繼承原型鏈上的Child3.prototype = Parent2.prototype;//缺點(diǎn)是無法區(qū)分一個實例是子函構(gòu)造函數(shù)實例化的還是父構(gòu)造函數(shù)實例化的let s1 = new Child3();//s1.constructor 指向了 Parent2,而不是 Child3,因為 Child3 原型對象的屬性 constructor 繼承了 Parent2 原型對象上的//如果你強(qiáng)行執(zhí)行 Child3.prototype.constructor = Child3 的話,也會將 Parent2.prototype.constructor 改成 Child3
          //組合優(yōu)化2,通過 Object.create() 創(chuàng)建一個中間對象,將兩個原型對象區(qū)別開來,并且繼承父構(gòu)造函數(shù)的原型鏈Child3.prototype = Object.create(Parent2.prototype);Child3.prototype.constructor = Child3;//即 Child3.prototype.__proto__ === Parent2.prototype 為 true//如此 Child3.prototype 和 Parent2.prototype 被隔離開,是兩個對象,不會相互影響//這種方式為理想繼承方式

          請解釋變量聲明提升

          通過var聲明的變量會被提升至作用域的頂端。不僅僅是變量,函數(shù)聲明也一樣會被提升。

          當(dāng)同一作用域內(nèi)同時出現(xiàn)變量和函數(shù)聲明提升時,變量仍然在函數(shù)前面。

          函數(shù)聲明與函數(shù)表達(dá)式的區(qū)別?

          在Javscript中,解析器在向執(zhí)行環(huán)境中加載數(shù)據(jù)時,對函數(shù)聲明和函數(shù)表達(dá)式并非是一視同仁的,解析器會率先讀取函數(shù)聲明,并使其在執(zhí)行任何代碼之前可用(可以訪問),至于函數(shù)表達(dá)式,則必須等到解析器執(zhí)行到它所在的代碼行,才會真正被解析執(zhí)行。

          什么是window對象? 什么是document對象?

          window對象代表瀏覽器中打開的一個窗口。document對象代表整個html文檔。實際上,document對象是window對象的一個屬性。

          document.onload和document.ready兩個事件的區(qū)別

          頁面加載完成有兩種事件,一是ready,表示文檔結(jié)構(gòu)已經(jīng)加載完成(不包含圖片等非文字媒體文件),二是onload,指示頁面包含圖片等文件在內(nèi)的所有元素都加載完成。

          Js的淺拷貝和深拷貝

          淺拷貝只復(fù)制指向某個對象的指針,而不復(fù)制對象本身,新舊對象還是共享同一塊內(nèi)存。但深拷貝會另外創(chuàng)造一個一模一樣的對象,新對象跟原對象不共享內(nèi)存,修改新對象不會改到原對象。

          淺拷貝

          // 第一層為深拷貝
          Object.assign()
          Array.prototype.slice()
          擴(kuò)展運(yùn)算符 ...

          深拷貝

          JSON.parse(JSON.stringify())

          遞歸函數(shù)

          function cloneObject(obj) {
          var newObj = {} //如果不是引用類型,直接返回
          if (typeof obj !== 'object') {
          return obj
          }
          //如果是引用類型,遍歷屬性
          else {
          for (var attr in obj) {
          //如果某個屬性還是引用類型,遞歸調(diào)用
          newObj[attr] = cloneObject(obj[attr])
          }
          }
          return newObj
          }


          JavaScript里arguments究竟是什么?

          Javascrip中國每個函數(shù)都會有一個Arguments對象實例arguments,它引用著函數(shù)的實參,可以用數(shù)組下標(biāo)的方式"[]"引用arguments的元素。arguments.length為函數(shù)實參個數(shù),arguments.callee引用函數(shù)自身。
          在函數(shù)代碼中,使用特殊對象arguments,開發(fā)者無需明確指出參數(shù)名,通過使用下標(biāo)就可以訪問相應(yīng)的參數(shù)。

          function test() { 
          var s = "";
          for (var i = 0; i < arguments.length; i++) {
          alert(arguments[i]);
          s += arguments[i] + ",";
          }
          return s;
          }
          test("name", "age");//name,age

          arguments雖然有一些數(shù)組的性質(zhì),但其并非真正的數(shù)組,只是一個類數(shù)組對象。其并沒有數(shù)組的很多方法,不能像真正的數(shù)組那樣調(diào)用.jion(),.concat(),.pop()等方法。


          什么是"use strict";?使用它的好處和壞處分別是什么?

          在代碼中出現(xiàn)表達(dá)式-"use strict"; 意味著代碼按照嚴(yán)格模式解析,這種模式使得Javascript在更嚴(yán)格的條件下運(yùn)行。

          好處:

          • 消除Javascript語法的一些不合理、不嚴(yán)謹(jǐn)之處,減少一些怪異行為;

          • 消除代碼運(yùn)行的一些不安全之處,保證代碼運(yùn)行的安全;

          • 提高編譯器效率,增加運(yùn)行速度;

          • 為未來新版本的Javascript做好鋪墊。

          壞處:

          • 同樣的代碼,在"嚴(yán)格模式"中,可能會有不一樣的運(yùn)行結(jié)果;

          • 一些在"正常模式"下可以運(yùn)行的語句,在"嚴(yán)格模式"下將不能運(yùn)行。


          什么是跨域?有什么方法解決跨域帶來的問題?

          跨域需要針對瀏覽器的同源策略來理解,同源策略指的是請求必須是同一個端口,同一個協(xié)議,同一個域名,不同源的客戶端腳本在沒有明確授權(quán)的情況下,不能讀寫對方資源。
          受瀏覽器同源策略的影響,不是同源的腳本不能操作其他源下面的對象。想要操作另一個源下的對象是就需要跨域。

          常用解決方案:

          • 跨域資源共享(CORS)

          • nginx代理跨域

          • nodejs中間件代理跨域

          • jsonp跨域


          解釋什么是Json

          (1)JSON 是一種輕量級的數(shù)據(jù)交換格式。

          (2)JSON 獨(dú)立于語言和平臺,JSON 解析器和 JSON 庫支持許多不同的編程語言。

          (3)JSON的語法表示三種類型值,簡單值(字符串,數(shù)值,布爾值,null),數(shù)組,對象


          解釋jsonp的原理,以及為什么不是真正的ajax

          Json是通過動態(tài)創(chuàng)建script標(biāo)簽,回調(diào)函數(shù)的方式實現(xiàn)的,而Ajax是頁面無刷新請求數(shù)據(jù)操作。

          原生Js的實現(xiàn):

          (function (window,document) {
          "use strict";
          var jsonp = function (url,data,callback) {
          // 1.將傳入的data數(shù)據(jù)轉(zhuǎn)化為url字符串形式
          // {id:1,name:'fly63'} => id=1&name=fly63
          var dataString = url.indexof('?') == -1? '?': '&';
          for(var key in data){
          dataString += key + '=' + data[key] + '&';
          };
          // 2 處理url中的回調(diào)函數(shù)
          // cbFuncName回調(diào)函數(shù)的名字 :my_json_cb_名字的前綴 + 隨機(jī)數(shù)(把小數(shù)點(diǎn)去掉)
          var cbFuncName = 'my_json_cb_' + Math.random().toString().replace('.','');
          dataString += 'callback=' + cbFuncName;
          // 3.創(chuàng)建一個script標(biāo)簽并插入到頁面中
          var scriptEle = document.createElement('script');
          scriptEle.src = url + dataString;
          // 4.掛載回調(diào)函數(shù)
          window[cbFuncName] = function (data) {
          callback(data);
          // 處理完回調(diào)函數(shù)的數(shù)據(jù)之后,刪除jsonp的script標(biāo)簽
          document.body.removeChild(scriptEle);
          }
          // 5.append到頁面中
          document.body.appendChild(scriptEle);
          }
          window.$jsonp = jsonp;// 因為jsonp是一個私有函數(shù)外部不能調(diào)用,所有jsonp函數(shù)作文window對象的一個方法,供外部調(diào)用
          })(window,document)



          談?wù)凜ookie的弊端?

          (1)Cookie數(shù)量和長度的限制。每個domain最多只能有20條cookie,每個cookie長度不能超過4KB,否則會被截掉。

          (2)安全性問題。如果cookie被 人攔截了,那人就可以取得所有的session信息。即使加密也與事無補(bǔ),因為攔截者并不需要知道cookie的意義,他只要原樣轉(zhuǎn)發(fā)cookie就可以達(dá)到目的了。

          (3)有些狀態(tài)不可能保存在客戶端。例如,為了防止重復(fù)提交表單,我們需要在服務(wù)器端保存一個計數(shù)器。如果我們把這個計數(shù)器保存在客戶端,那么它起不到任何作用。


          什么是Cookie 隔離?(或者:請求資源的時候不要帶cookie怎么做)

          通過使用多個非主要域名來請求靜態(tài)文件,如果靜態(tài)文件都放在主域名下,那靜態(tài)文件請求的時候帶有的cookie的數(shù)據(jù)提交給server是非常浪費(fèi)的,還不如隔離開。因為cookie有域的限制,因此不能跨域提交請求,故使用非主要域名的時候,請求頭中就不會帶有cookie數(shù)據(jù),這樣可以降低請求頭的大小,降低請求時間,從而達(dá)到降低整體請求延時的目的。同時這種方式不會將cookie傳入server,也減少了server對cookie的處理分析環(huán)節(jié),提高了server的http請求的解析速度。


          描述一下cookies,sessionStorage和localStorage的區(qū)別?

          sessionStorage和localStorage是HTML5 Web Storage API提供的,可以方便的在web請求之間保存數(shù)據(jù)。有了本地數(shù)據(jù),就可以避免數(shù)據(jù)在瀏覽器和服務(wù)器間不必要地來回傳遞。sessionStorage、localStorage、cookie都是在瀏覽器端存儲的數(shù)據(jù),其中sessionStorage的概念很特別,引入了一個“瀏覽器窗口”的概念。sessionStorage是在同源的同窗口(或tab)中,始終存在的數(shù)據(jù)。也就是說只要這個瀏覽器窗口沒有關(guān)閉,即使刷新頁面或進(jìn)入同源另一頁面,數(shù)據(jù)仍然存在。關(guān)閉窗口后,sessionStorage即被銷毀。同時“獨(dú)立”打開的不同窗口,即使是同一頁面,sessionStorage對象也是不同的cookies會發(fā)送到服務(wù)器端。其余兩個不會。Microsoft指出InternetExplorer8增加cookie限制為每個域名50個,但I(xiàn)E7似乎也允許每個域名50個cookie。

          • Firefox每個域名cookie限制為50個。

          • Opera每個域名cookie限制為30個。

          • Firefox和Safari允許cookie多達(dá)4097個字節(jié),包括名(name)、值(value)和等號。

          • Opera允許cookie多達(dá)4096個字節(jié),包括:名(name)、值(value)和等號。

          • InternetExplorer允許cookie多達(dá)4095個字節(jié),包括:名(name)、值(value)和等號。


          localstorage不能手動刪除的時候,什么時候過期

          除非被清除,否則永久保存 clear()可清楚

          sessionStorage 僅在當(dāng)前會話下有效,關(guān)閉頁面或瀏覽器后被清除


          什么是函數(shù)柯里化

          函數(shù)柯里化是把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。

          柯里化的目的是,減少代碼冗余,以及增加代碼的可讀性。下面看一個與柯里化有關(guān)的經(jīng)典例子:

          // 實現(xiàn)一個add方法,使計算結(jié)果能夠滿足類似如下的預(yù)期:
          // add(1)(2)(3) = 6;
          // add(1, 2, 3)(4) = 10;
          // add(1)(2)(3)(4)(5) = 15;
          var add_currying=function(...rest){
          var sum=0;
          for(let item of rest){
          sum+=item;
          }
          var add_back = (...rest) => {
          for(let item of rest){
          sum+=item;
          }
          return add_back;
          };
          add_back.toString = () => sum;
          return add_back;
          }
          console.log(add_currying(1,2,3)); //6
          console.log(add_currying(1,2)(3,4)); //10
          console.log(add_currying(1,2)(3)(4,5)); //15
          console.log(add_currying(1)(2)(3)(4)(5)(6)); //21
          //打印出來會自動使用toString,即使是寫var a=add_currying(1,2,3)也會自動調(diào)用此方法(默認(rèn)將函數(shù)語句以字符串打出)
          //而為了打印出我們想要的結(jié)果我們就需要自己重寫toString方法
          //如果不用es6的三點(diǎn)運(yùn)算符就只能使用以前的Array.prototype.slice.call(arguments)方法


          js如何處理防抖和節(jié)流?

          在進(jìn)行窗口的resize、scroll,輸入框內(nèi)容校驗等操作時,如果事件處理函數(shù)調(diào)用的頻率無限制,會加重瀏覽器的負(fù)擔(dān),導(dǎo)致用戶體驗非常糟糕。
          此時我們可以采用debounce(防抖)和throttle(節(jié)流)的方式來減少調(diào)用頻率,同時又不影響實際效果。

          函數(shù)防抖(debounce):

          當(dāng)持續(xù)觸發(fā)事件時,一定時間段內(nèi)沒有再觸發(fā)事件,事件處理函數(shù)才會執(zhí)行一次,如果設(shè)定的時間到來之前,又一次觸發(fā)了事件,就重新開始延時。
          如下,持續(xù)觸發(fā)scroll事件時,并不執(zhí)行handle函數(shù),當(dāng)1000毫秒內(nèi)沒有觸發(fā)scroll事件時,才會延時觸發(fā)scroll事件。

          function debounce(fn, wait) { 
          var timeout = null;
          return function() {
          if(timeout !== null) clearTimeout(timeout);
          timeout = setTimeout(fn, wait);
          }
          }
          // 處理函數(shù)
          function handle() {
          console.log(Math.random());
          }
          // 滾動事件
          window.addEventListener('scroll', debounce(handle, 1000)); 函數(shù)節(jié)流

          函數(shù)節(jié)流(throttle):

          當(dāng)持續(xù)觸發(fā)事件時,保證一定時間段內(nèi)只調(diào)用一次事件處理函數(shù)。
          節(jié)流通俗解釋就比如我們水龍頭放水,閥門一打開,水嘩嘩的往下流,秉著勤儉節(jié)約的優(yōu)良傳統(tǒng)美德,我們要把水龍頭關(guān)小點(diǎn),最好是如我們心意按照一定規(guī)律在某個時間間隔內(nèi)一滴一滴的往下滴。
          如下,持續(xù)觸發(fā)scroll事件時,并不立即執(zhí)行handle函數(shù),每隔1000毫秒才會執(zhí)行一次handle函數(shù)。

          var throttle =function(func, delay) {  var prev = Date.now();  return function() {   var context = this;  var args = arguments;   var now = Date.now();   if (now - prev >= delay) {    func.apply(context, args);                              prev = Date.now();  }  } }function handle() {               console.log(Math.random()); }         window.addEventListener('scroll', throttle(handle, 1000)); 

          JS里垃圾回收機(jī)制是什么,常用的是哪種,怎么處理的?

          JS的垃圾回收機(jī)制是為了以防內(nèi)存泄漏,內(nèi)存泄漏的含義就是當(dāng)已經(jīng)不需要某塊內(nèi)存時這塊內(nèi)存還存在著,垃圾回收機(jī)制就是間歇的不定期的尋找到不再使用的變量,并釋放掉它們所指向的內(nèi)存。
          JS中最常見的垃圾回收方式是標(biāo)記清除。
          工作原理:是當(dāng)變量進(jìn)入環(huán)境時,將這個變量標(biāo)記為“進(jìn)入環(huán)境”。當(dāng)變量離開環(huán)境時,則將其標(biāo)記為“離開環(huán)境”。標(biāo)記“離開環(huán)境”的就回收內(nèi)存。

          工作流程:

          1. 垃圾回收器,在運(yùn)行的時候會給存儲在內(nèi)存中的所有變量都加上標(biāo)記。

          2. 去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記。

          3. 再被加上標(biāo)記的會被視為準(zhǔn)備刪除的變量。

          4. 垃圾回收器完成內(nèi)存清除工作,銷毀那些帶標(biāo)記的值并回收他們所占用的內(nèi)存空間。


          那些操作會造成內(nèi)存泄漏

          全局變量、閉包、DOM清空或刪除時,事件未清除、子元素存在引用


          什么是虛擬DOM

          文檔對象模型或 DOM 定義了一個接口,該接口允許 JavaScript 之類的語言訪問和操作 HTML 文檔。元素由樹中的節(jié)點(diǎn)表示,并且接口允許我們操縱它們。但是此接口需要付出代價,大量非常頻繁的 DOM 操作會使頁面速度變慢。

          vue 通過在內(nèi)存中實現(xiàn)文檔結(jié)構(gòu)的虛擬表示來解決此問題,其中虛擬節(jié)點(diǎn)(VNode)表示 DOM 樹中的節(jié)點(diǎn)。當(dāng)需要操縱時,可以在虛擬 DOM的 內(nèi)存中執(zhí)行計算和操作,而不是在真實 DOM 上進(jìn)行操縱。這自然會更快,并且允許虛擬 DOM 算法計算出最優(yōu)化的方式來更新實際 DOM 結(jié)構(gòu)。

          一旦計算出,就將其應(yīng)用于實際的 DOM 樹,這就提高了性能,這就是為什么基于虛擬 DOM 的框架(例如 vue 和 react)如此突出的原因。


          Ajax使用

          所謂異步,就是向服務(wù)器發(fā)送請求的時候,我們不必等待結(jié)果,而是可以同時做其他的事情,等到有了結(jié)果它自己會根據(jù)設(shè)定進(jìn)行后續(xù)操作,與此同時,頁面是不會發(fā)生整頁刷新的,提高了用戶體驗。
          創(chuàng)建Ajax的過程:
          1) 創(chuàng)建XMLHttpRequest對象(異步調(diào)用對象)

          var xhr = new XMLHttpRequest();

          2) 創(chuàng)建新的Http請求(方法、URL、是否異步)

          xhr.open(‘get’,’example.php’,false);

          3) 設(shè)置響應(yīng)HTTP請求狀態(tài)變化的函數(shù)。
          onreadystatechange事件中readyState屬性等于4。響應(yīng)的HTTP狀態(tài)為200(OK)或者304(Not Modified)。
          4) 發(fā)送http請求

          xhr.send(data);

          5) 獲取異步調(diào)用返回的數(shù)據(jù)
          注意:
          1) 頁面初次加載時,盡量在web服務(wù)器一次性輸出所有相關(guān)的數(shù)據(jù),只在頁面加載完成之后,用戶進(jìn)行操作時采用ajax進(jìn)行交互。
          2) 同步ajax在IE上會產(chǎn)生頁面假死的問題。所以建議采用異步ajax。
          3) 盡量減少ajax請求次數(shù)
          4) ajax安全問題,對于敏感數(shù)據(jù)在服務(wù)器端處理,避免在客戶端處理過濾。對于關(guān)鍵業(yè)務(wù)邏輯代碼也必須放在服務(wù)器端處理。


          Ajax的原生寫法

          function ajax() {
          //創(chuàng)建一個 XHR 對象
          let oAjax = window.XMLHttpRequest ? (new XMLHttpRequest()) : (new window.ActiveXobject('Microsoft.XMLHTTP'));
          //返回一個函數(shù),這是函數(shù)柯里化操作,不用每次調(diào)用 ajax 都判斷瀏覽器環(huán)境
          //但是會占用更多的內(nèi)存,因為總是會保存外部函數(shù)的作用域
          return function(url, fnSucc, fnFaild) {
          //只要 XHR 對象的 readyState 屬性的值發(fā)生改變,就觸發(fā)這個事件
          oAjax.onreadystatechange = function() {
          // readyState 屬性是 0-4 的值,當(dāng)為 4 時,表示已經(jīng)接收到全部響應(yīng)數(shù)據(jù),并可以在客戶端使用
          if (oAjax.readyState === 4) {
          //響應(yīng)的 HTTP 狀態(tài)
          let s = oAjax.status;
          if (s === 200 || s === 206 || s === 304) {
          //將響應(yīng)主體被返回的文本作為參數(shù)傳給這個函數(shù),并執(zhí)行這個函數(shù)
          if (fnSucc) fnSucc(oAjax.responseText);
          } else {
          if (fnFaild) fnFaild(oAjax.status);
          }
          }
          };
          //啟動一個請求,準(zhǔn)備發(fā)送
          oAjax.open('GET', url, true);
          //發(fā)送請求
          oAjax.send(null);
          }
          }


          ajax請求的時候get 和post方式的區(qū)別

          最直觀的區(qū)別就是GET把參數(shù)包含在URL中,POST通過request body傳遞參數(shù)。

          • GET在瀏覽器回退時是無害的,而POST會再次提交請求。

          • GET產(chǎn)生的URL地址可以被Bookmark,而POST不可以。

          • GET請求會被瀏覽器主動cache,而POST不會,除非手動設(shè)置。

          • GET請求只能進(jìn)行url編碼,而POST支持多種編碼方式。

          • GET請求參數(shù)會被完整保留在瀏覽器歷史記錄里,而POST中的參數(shù)不會被保留。

          • GET請求在URL中傳送的參數(shù)是有長度限制的,而POST么有。

          • 對參數(shù)的數(shù)據(jù)類型,GET只接受ASCII字符,而POST沒有限制。

          • GET比POST更不安全,因為參數(shù)直接暴露在URL上,所以不能用來傳遞敏感信息。

          • GET參數(shù)通過URL傳遞,POST放在Request body中。

          Javascript中,執(zhí)行時對象查找時,永遠(yuǎn)不會去查找原型的函數(shù)?

          Object.hasOwnProperty(proName):是用來判斷一個對象是否有你給出名稱的屬性。不過需要注意的是,此方法無法檢查該對象的原型鏈中是否具有該屬性,該屬性必須是對象本身的一個成員。

          JS延遲加載的方式有哪些?

          JS的延遲加載有助與提高頁面的加載速度。

          defer和async、動態(tài)創(chuàng)建DOM方式(用得最多)、按需異步載入JS

          defer:延遲腳本。立即下載,但延遲執(zhí)行(延遲到整個頁面都解析完畢后再運(yùn)行),按照腳本出現(xiàn)的先后順序執(zhí)行。
          async:異步腳本。下載完立即執(zhí)行,但不保證按照腳本出現(xiàn)的先后順序執(zhí)行。

          同步和異步的區(qū)別?

          同步的概念在操作系統(tǒng)中:不同進(jìn)程協(xié)同完成某項工作而先后次序調(diào)整(通過阻塞、喚醒等方式),同步強(qiáng)調(diào)的是順序性,誰先誰后。異步不存在順序性。

          同步:瀏覽器訪問服務(wù)器,用戶看到頁面刷新,重新發(fā)請求,等請求完,頁面刷新,新內(nèi)容出現(xiàn),用戶看到新內(nèi)容之后進(jìn)行下一步操作。
          異步:瀏覽器訪問服務(wù)器請求,用戶正常操作,瀏覽器在后端進(jìn)行請求。等請求完,頁面不刷新,新內(nèi)容也會出現(xiàn),用戶看到新內(nèi)容。

          頁面編碼和被請求的資源編碼如果不一致如何處理?

          若請求的資源編碼,如外引js文件編碼與頁面編碼不同??筛鶕?jù)外引資源編碼方式定義為 charset="utf-8"或"gbk"。

          比如:http://www.fly63.com/a.html 中嵌入了一個http://www.fly63.com/test.js

          a.html 的編碼是gbk或gb2312的。 而引入的js編碼為utf-8的 ,那就需要在引入的時候

          <script src="http://www.fly63.com/test.js" charset="utf-8"></script>

          模塊化開發(fā)怎么做?

          模塊化開發(fā)指的是在解決某一個復(fù)雜問題或者一系列問題時,依照一種分類的思維把問題進(jìn)行系統(tǒng)性的分解。

          模塊化是一種將復(fù)雜系統(tǒng)分解為代碼結(jié)構(gòu)更合理,可維護(hù)性更高的可管理的模塊方式。對于軟件行業(yè):系統(tǒng)被分解為一組高內(nèi)聚,低耦合的模塊。

          (1)定義封裝的模塊
          (2)定義新模塊對其他模塊的依賴
          (3)可對其他模塊的引入支持。在JavaScript中出現(xiàn)了一些非傳統(tǒng)模塊開發(fā)方式的規(guī)范。 CommonJS的模塊規(guī)范,AMD(Asynchronous Module Definition),CMD(Common Module Definition)等。AMD是異步模塊定義,所有的模塊將被異步加載,模塊加載不影響后邊語句運(yùn)行。

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

          AMD 是 RequireJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。CMD 是 SeaJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。區(qū)別:

          1. 1) 對于依賴的模塊,AMD 是提前執(zhí)行,CMD 是延遲執(zhí)行。不過 RequireJS 從 2.0 開始,也改成可以延遲執(zhí)行(根據(jù)寫法不同,處理方式不同)。

          2. 2) CMD 推崇依賴就近,AMD 推崇依賴前置。

          3. 3) AMD 的 API 默認(rèn)是一個當(dāng)多個用,CMD 的 API 嚴(yán)格區(qū)分,推崇職責(zé)單一。

          解釋頁面的回流與重繪

          當(dāng)render tree中的一部分(或全部)因為元素的規(guī)模尺寸,布局,隱藏等改變而需要重新構(gòu)建。這就稱為回流(reflow)。

          每個頁面至少需要一次回流,就是在頁面第一次加載的時候,這時候是一定會發(fā)生回流的,因為要構(gòu)建render tree。

          在回流的時候,瀏覽器會使渲染樹中受到影響的部分失效,并重新構(gòu)造這部分渲染樹,完成回流后,瀏覽器會重新繪制受影響的部分到屏幕中,該過程成為重繪。

          當(dāng)render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀,風(fēng)格,而不會影響布局的,比如background-color。則就叫稱為重繪。

          區(qū)別:

          回流必將引起重繪,而重繪不一定會引起回流。比如:只有顏色改變的時候就只會發(fā)生重繪而不會引起回流

          當(dāng)頁面布局和幾何屬性改變時就需要回流,比如:添加或者刪除可見的DOM元素,元素位置改變,元素尺寸改變——邊距、填充、邊框、寬度和高度,內(nèi)容改變

          瀏覽器的滾動距離

          可視區(qū)域距離頁面頂部的距離

          var scrollTop=document.documentElement.scrollTop||document.body.scrollTop

          可視區(qū)的大小

          (1)innerXXX(不兼容ie)

          window.innerHeight 可視區(qū)高度,包含滾動條寬度
          window.innerWidth 可視區(qū)寬度,包含滾動條寬度

          (2)document.documentElement.clientXXX(兼容ie)

          document.documentElement.clientWidth可視區(qū)寬度,不包含滾動條寬度
          document.documentElement.clientHeight可視區(qū)高度,不包含滾動條寬度

          節(jié)點(diǎn)的種類有幾種,分別是什么?

          (1)元素節(jié)點(diǎn):nodeType ===1;

          (2)文本節(jié)點(diǎn):nodeType ===3;

          (3)屬性節(jié)點(diǎn):nodeType ===2;

          innerHTML和outerHTML的區(qū)別

          innerHTML(元素內(nèi)包含的內(nèi)容)

          outerHTML(自己以及元素內(nèi)的內(nèi)容)

          document.write和innerHTML的區(qū)別

          document.write 將內(nèi)容寫入頁面,清空替換掉原來的內(nèi)容,會導(dǎo)致重繪

          document.innerHTML 將內(nèi)容寫入某個Dom節(jié)點(diǎn),不會重繪

          offsetWidth offsetHeight和clientWidth clientHeight的區(qū)別

          (1)offsetWidth (content寬度+padding寬度+border寬度)
          (2)offsetHeight(content高度+padding高度+border高度)
          (3)clientWidth(content寬度+padding寬度)
          (4)clientHeight(content高度+padding高度)

          DOM操作

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

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

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

          appendChild()
          removeChild()
          replaceChild()
          insertBefore() //在已有的子節(jié)點(diǎn)前插入一個新的子節(jié)點(diǎn)

          (3)查找

          getElementsByTagName() //通過標(biāo)簽名稱
          getElementsByName() //通過元素的Name屬性的值(IE容錯能力較強(qiáng),會得到一個數(shù)組,其中包括id等于name值的)
          getElementById() //通過元素Id,唯一性

          BOM 和 DOM 的關(guān)系

          BOM全稱Browser Object Model,即瀏覽器對象模型,主要處理瀏覽器窗口和框架。

          DOM全稱Document Object Model,即文檔對象模型,是 HTML 和XML 的應(yīng)用程序接口(API),遵循W3C 的標(biāo)準(zhǔn),所有瀏覽器公共遵守的標(biāo)準(zhǔn)。

          JS是通過訪問BOM(Browser Object Model)對象來訪問、控制、修改客戶端(瀏覽器),由于BOM的window包含了document,window對象的屬性和方法是直接可以使用而且被感知的,因此可以直接使用window對象的document屬性,通過document屬性就可以訪問、檢索、修改XHTML文檔內(nèi)容與結(jié)構(gòu)。因為document對象又是DOM的根節(jié)點(diǎn)。

          可以說,BOM包含了DOM(對象),瀏覽器提供出來給予訪問的是BOM對象,從BOM對象再訪問到DOM對象,從而js可以操作瀏覽器以及瀏覽器讀取到的文檔。

          數(shù)組對象有哪些原生方法,列舉一下

          pop、push、shift、unshift、splice、reverse、sort、concat、join、slice、toString、indexOf、lastIndexOf、reduce、reduceRight、forEach、map、filter、every、some

          pop:刪除并返回數(shù)組最后一個元素(改變原數(shù)組);
          push:返回添加完成后的數(shù)組的長度(改變原數(shù)組);
          shift:移除并返回數(shù)組的第一個元素(改變原數(shù)組);
          unshift:在數(shù)組頭部插入一個元素
          slice:slice(下標(biāo),個數(shù))返回裁剪后的數(shù)組(不改變原數(shù)組);
          splice:插入,刪除或替換數(shù)組的元素
          concat:合并數(shù)組返回組合數(shù)組(不改變原數(shù)組);
          join:將數(shù)組用標(biāo)識符鏈接成字符串返回拼接好的字符串(不改變原數(shù)組);
          reverse:翻轉(zhuǎn)數(shù)組(改變原數(shù)組);
          toString:將數(shù)組轉(zhuǎn)換成一個字符串;
          split:把字符串分割開,以數(shù)組方式儲存;
          forEach:主要用于遍歷數(shù)組;
          every:主要用于檢查數(shù)組中每個元素是否符合函數(shù)的條件,如果其中有一個不符合,則返回false;
          indexOf:主要用于在數(shù)組中查找元素,并把元素的位置返回來。

          字符串方法

          charAt():根據(jù)下標(biāo)找到對應(yīng)值

          charCodeAt():通過下標(biāo)值找到對應(yīng)字符Unicode編碼

          indexOf():通過字符查找對應(yīng)下標(biāo)(首次出現(xiàn))

          lastIndexOf():通過字符找最后一次出現(xiàn)的下標(biāo)值

          slice():截取字符串,2個參數(shù),(起始位置,結(jié)束位置)

          split():把字符串按分隔符分割成數(shù)組

          substring():截取字符串,(起始位置,結(jié)束位置)

          substr():截取指定位置和長度的字符串,(起始位置,長度)

          toLowerCase():字符串轉(zhuǎn)為小寫

          toUpperCase():字符串轉(zhuǎn)成大寫

          trim():去掉字符串前后所有空格

          JS中的Array.splice()和Array.slice()方法有什么區(qū)別

          話不多說,來看第一個例子:

          var arr=[0,1,2,3,4,5,6,7,8,9];//設(shè)置一個數(shù)組console.log(arr.slice(2,7));//2,3,4,5,6console.log(arr.splice(2,7));//2,3,4,5,6,7,8//由此我們簡單推測數(shù)量兩個函數(shù)參數(shù)的意義,slice(start,end)第一個參數(shù)表示開始位置,第二個表示截取到的位置(不包含該位置)splice(start,length)第一個參數(shù)開始位置,第二個參數(shù)截取長度

          接著看第二個:

          var x=y=[0,1,2,3,4,5,6,7,8,9]console.log(x.slice(2,5));//2,3,4console.log(x);[0,1,2,3,4,5,6,7,8,9]原數(shù)組并未改變//接下來用同樣方式測試spliceconsole.log(y.splice(2,5));//2,3,4,5,6console.log(y);//[0,1,7,8,9]顯示原數(shù)組中的數(shù)值被剔除掉了

          slice和splice雖然都是對于數(shù)組對象進(jìn)行截取,但是二者還是存在明顯區(qū)別,函數(shù)參數(shù)上slice和splice第一個參數(shù)都是截取開始位置,slice第二個參數(shù)是截取的結(jié)束位置(不包含),而splice第二個參數(shù)(表示這個從開始位置截取的長度),slice不會對原數(shù)組產(chǎn)生變化,而splice會直接剔除原數(shù)組中的截取數(shù)據(jù)!

          如何在JS中動態(tài)添加/刪除對象的屬性?

          咱們可以使用object.property_name = value向?qū)ο筇砑訉傩?,delete object.property_name 用于刪除屬性。

          例如:

          let user = new Object();// adding a propertyuser.name='Anil';user.age  =25;console.log(user);delete user.age;console.log(user);

          圖片的懶加載和預(yù)加載

          預(yù)加載:常用的是new Image();,設(shè)置其src來實現(xiàn)預(yù)載,再使用onload方法回調(diào)預(yù)載完成事件。

          function loadImage(url, callback){    var img = new Image(); //創(chuàng)建一個Image對象,實現(xiàn)圖片預(yù)下載    img.src = url;    if (img.complete){         // 如果圖片已經(jīng)存在于瀏覽器緩存,直接調(diào)用回調(diào)函數(shù)        callback.call(img);        return; // 直接返回,不用再處理onload事件    }    img.onload = function (){    //圖片下載完畢時異步調(diào)用callback函數(shù)。    callback.call(img);//將回調(diào)函數(shù)的this替換為Image對象 ,如果你直接用img.width的時候,圖片還沒有完全下載下來    };}

          懶加載:主要目的是作為服務(wù)器前端的優(yōu)化,緩解服務(wù)器前端壓力,一次性請求次數(shù)減少或延遲請求。實現(xiàn)方式:
          1.第一種是純粹的延遲加載,使用setTimeOut、setInterval進(jìn)行加載延遲.
          2.第二種是條件加載,符合某些條件,或觸發(fā)了某些事件才開始異步下載。
          3.第三種是可視區(qū)加載,即僅加載用戶可以看到的區(qū)域,這個主要由監(jiān)控滾動條來實現(xiàn),一般會在距用戶看到某圖片前一定距離遍開始加載,這樣能保證用戶拉下時正好能看到圖片。

          什么是回調(diào)

          回調(diào)函數(shù)是作為參數(shù)或選項傳遞給某個方法的普通JS函數(shù)。它是一個函數(shù),在另一個函數(shù)完成執(zhí)行后執(zhí)行,因此稱為回調(diào)。

          在JS中,函數(shù)是對象,因此,函數(shù)可以接受函數(shù)作為參數(shù),并且可以由其他函數(shù)返回。

          JS中的宿主對象與原生對象有何不同?

          宿主對象:這些是運(yùn)行環(huán)境提供的對象。這意味著它們在不同的環(huán)境下是不同的。例如,瀏覽器包含像windows這樣的對象,但是Node.js環(huán)境提供像Node List這樣的對象。

          原生對象:這些是JS中的內(nèi)置對象。它們也被稱為全局對象,因為如果使用JS,內(nèi)置對象不受是運(yùn)行環(huán)境影響。

          漸進(jìn)增強(qiáng)與優(yōu)雅降級

          漸進(jìn)增強(qiáng):針對低版本瀏覽器進(jìn)行構(gòu)建頁面,保證最基本的功能,然后再針對高級瀏覽器進(jìn)行效果、交互等改進(jìn),達(dá)到更好的用戶體驗。

          優(yōu)雅降級:一開始就構(gòu)建完整的功能,然后再針對低版本瀏覽器進(jìn)行兼容。

          Web Worker和Web Socket?

          web socket:在一個單獨(dú)的持久連接上提供全雙工、雙向的通信。使用自定義的協(xié)議(ws://、wss://),同源策略對web socket不適用。
          web worker:運(yùn)行在后臺的JavaScript,不影響頁面的性能。

          創(chuàng)建worker:var worker = new Worker(url);
          向worker發(fā)送數(shù)據(jù):worker.postMessage(data);
          接收worker返回的數(shù)據(jù):worker.onmessage
          終止一個worker的執(zhí)行:worker.terminate();

          瀏覽器的線程

          JS引擎線程:解釋執(zhí)行JS代碼、用戶輸入、網(wǎng)絡(luò)請求等

          GUI線程(渲染線程):繪制用戶界面、與JS主線程互斥

          HTTP網(wǎng)絡(luò)請求線程:處理用戶的GET、POST等請求,等拿到返回結(jié)果后,將回調(diào)函數(shù)推入事件隊列

          定時器觸發(fā)線程:setTimeout、setInterval等待時間結(jié)束后,將執(zhí)行函數(shù)推入事件隊列中

          事件處理線程:將click、mouse、input等交互事件發(fā)生后,將事件處理函數(shù)推入事件隊列中

          js 執(zhí)行機(jī)制、事件循環(huán)

          JavaScript 語言的一大特點(diǎn)就是單線程,同一個時間只能做一件事。單線程就意味著,所有任務(wù)需要排隊,前一個任務(wù)結(jié)束,才會執(zhí)行后一個任務(wù)。如果前一個任務(wù)耗時很長,后一個任務(wù)就不得不一直等著。

          JavaScript 語言的設(shè)計者意識到這個問題,將所有任務(wù)分成兩種,一種是同步任務(wù)(synchronous),另一種是異步任務(wù)(asynchronous),在所有同步任務(wù)執(zhí)行完之前,任何的異步任務(wù)是不會執(zhí)行的。

          當(dāng)我們打開網(wǎng)站時,網(wǎng)頁的渲染過程就是一大堆同步任務(wù),比如頁面骨架和頁面元素的渲染。而像加載圖片音樂之類占用資源大耗時久的任務(wù),就是異步任務(wù)。

          JS 異步有一個機(jī)制,就是遇到宏任務(wù),先執(zhí)行宏任務(wù),將宏任務(wù)放入 Event Queue,然后再執(zhí)行微任務(wù),將微任務(wù)放入 Event Queue,但是,這兩個 Queue 不是一個 Queue。

          當(dāng)你往外拿的時候先從微任務(wù)里拿這個回調(diào)函數(shù),然后再從宏任務(wù)的 Queue 拿宏任務(wù)的回調(diào)函數(shù)。

          宏任務(wù):整體代碼 script,setTimeout,setInterval
          微任務(wù):Promise,process.nextTick

          JS為什么要區(qū)分微任務(wù)和宏任務(wù)

          (1)js是單線程的,但是分同步異步
          (2)微任務(wù)和宏任務(wù)皆為異步任務(wù),它們都屬于一個隊列
          (3)宏任務(wù)一般是:script,setTimeout,setInterval、setImmediate
          (4)微任務(wù):原生Promise
          (5)遇到微任務(wù),先執(zhí)行微任務(wù),執(zhí)行完后如果沒有微任務(wù),就執(zhí)行下一個宏任務(wù),如果有微任務(wù),就按順序一個一個執(zhí)行微任務(wù)

          減少頁面加載時間的方法

          • 優(yōu)化圖片

          • 圖像格式的選擇(GIF:提供的顏色較少,可用在一些對顏色要求不高的地方)

          • 優(yōu)化css(壓縮合并css,如margin-top,margin-left...)

          • 網(wǎng)址后加斜杠(如www.campr.com/目錄,會判斷這個“目錄是什么文件類型,或者是目錄。)

          • 標(biāo)明高度和寬度(如果瀏覽器沒有找到這兩個參數(shù),它需要一邊下載圖片一邊計算大小,如果圖片很多,瀏覽器需要不斷地調(diào)整頁面。這不但影響速度,也影響瀏覽體驗。當(dāng)瀏覽器知道了高度和寬度參數(shù)后,即使圖片暫時無法顯示,頁面上也會騰出圖片的空位,然后繼續(xù)加載后面的內(nèi)容。從而加載時間快了,瀏覽體驗也更好了。)

          • 減少http請求(合并文件,合并圖片)。

          線程與進(jìn)程的區(qū)別

          一個程序至少有一個進(jìn)程,一個進(jìn)程至少有一個線程。線程的劃分尺度小于進(jìn)程,使得多線程程序的并發(fā)性高。

          另外,進(jìn)程在執(zhí)行過程中擁有獨(dú)立的內(nèi)存單元,而多個線程共享內(nèi)存,從而極大地提高了程序的運(yùn)行效率。

          線程在執(zhí)行過程中與進(jìn)程還是有區(qū)別的。每個獨(dú)立的線程有一個程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口。

          但是線程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個線程執(zhí)行控制。

          從邏輯角度來看,多線程的意義在于一個應(yīng)用程序中,有多個執(zhí)行部分可以同時執(zhí)行。但操作系統(tǒng)并沒有將多個線程看做多個獨(dú)立的應(yīng)用,來實現(xiàn)進(jìn)程的調(diào)度和管理以及資源分配。這就是進(jìn)程和線程的重要區(qū)別。

          說說你對語義化的理解?

          1.去掉或樣式丟失的時候能讓頁面呈現(xiàn)清晰的結(jié)構(gòu):html本身是沒有表現(xiàn)的,我們看到例如<h1>是粗體,字體大小2em,加粗;<strong>是加粗的,不要認(rèn)為這是html的表現(xiàn),這些其實html默認(rèn)的css樣式在起作用。

          所以去掉或樣式丟失的時候能讓頁面呈現(xiàn)清晰的結(jié)構(gòu)不是語義化的HTML結(jié)構(gòu)的優(yōu)點(diǎn),但是瀏覽器都有有默認(rèn)樣式,默認(rèn)樣式的目的也是為了更好的表達(dá)html的語義,可以說瀏覽器的默認(rèn)樣式和語義化的HTML結(jié)構(gòu)是不可分割的。

          2.屏幕閱讀器(如果訪客有視障)會完全根據(jù)你的標(biāo)記來“讀”你的網(wǎng)頁。

          3.PDA、手機(jī)等設(shè)備可能無法像普通電腦的瀏覽器一樣來渲染網(wǎng)頁(通常是因為這些設(shè)備對CSS的支持較弱)。

          4.有利于SEO:和搜索引擎建立良好溝通,有助于爬蟲抓取更多的有效信息:爬蟲依賴于標(biāo)簽來確定上下文和各個關(guān)鍵字的權(quán)重。

          5.便于團(tuán)隊開發(fā)和維護(hù),語義化更具可讀性,是下一步吧網(wǎng)頁的重要動向,遵循W3C標(biāo)準(zhǔn)的團(tuán)隊都遵循這個標(biāo)準(zhǔn),可以減少差異化。

          為什么利用多個域名來提供網(wǎng)站資源會更有效?

          1.CDN緩存更方便

          2.突破瀏覽器并發(fā)限制(一般每個域名建立的鏈接不超過6個)

          3.Cookieless,節(jié)省帶寬,尤其是上行帶寬一般比下行要慢

          4.對于UGC的內(nèi)容和主站隔離,防止不必要的安全問題(上傳js竊取主站cookie之類的)。正是這個原因要求用戶內(nèi)容的域名必須不是自己主站的子域名,而是一個完全獨(dú)立的第三方域名。

          5.數(shù)據(jù)做了劃分,甚至切到了不同的物理集群,通過子域名來分流比較省事。這個可能被用的不多。

          PS:關(guān)于Cookie的問題,帶寬是次要的,安全隔離才是主要的。關(guān)于多域名,也不是越多越好,雖然服務(wù)器端可以做泛解釋,瀏覽器做dns解釋也是耗時間的,而且太多域名,如果要走h(yuǎn)ttps的話,還有要多買證書和部署的問題。

          你如何組織,優(yōu)化自己的代碼?

          對內(nèi):模塊模式;對外:繼承

          代碼重用

          避免全局變量(命名空間,封閉空間,模塊化mvc…)

          拆分函數(shù)避免函數(shù)過于臃腫

          注釋

          前端模塊化和組件化

          模塊化:可復(fù)用,側(cè)重的功能的封裝,主要是針對Javascript代碼,隔離、組織復(fù)制的javascript代碼,將它封裝成一個個具有特定功能的的模塊。

          組件化:可復(fù)用,更多關(guān)注的UI部分,頁面的每個部件,比如頭部,彈出框甚至確認(rèn)按鈕都可以成為一個組件,每個組件有獨(dú)立的HTML、css、js代碼。

          http的cache機(jī)制及作用?

          定義:瀏覽器緩存(Browser Caching)是為了加速瀏覽,瀏覽器在用戶磁盤上對最近請求過的文檔進(jìn)行存儲,當(dāng)訪問者再次請求這個頁面時,瀏覽器就可以從本地磁盤顯示文檔,這樣就可以加速頁面的閱覽。
          cache的作用:
          1、減少延遲,讓你的網(wǎng)站更快,提高用戶體驗。
          2、避免網(wǎng)絡(luò)擁塞,減少請求量,減少輸出帶寬。
          實現(xiàn)手段:
          Cache-Control中的max-age是實現(xiàn)內(nèi)容cache的主要手段,共有3種常用策略:max-age和Last-Modified(If-Modified-Since)的組合、僅max-age、max-age和ETag的組合。

          對于強(qiáng)制緩存,服務(wù)器通知瀏覽器一個緩存時間,在緩存時間內(nèi),下次請求,直接用緩存,不在時間內(nèi),執(zhí)行比較緩存策略。
          對于比較緩存,將緩存信息中的Etag和Last-Modified通過請求發(fā)送給服務(wù)器,由服務(wù)器校驗,返回304狀態(tài)碼時,瀏覽器直接使用緩存。

          HTTP 常見的狀態(tài)碼

          400 客戶端請求有語法錯誤,不能被服務(wù)器所理解
          403 服務(wù)器收到請求但是拒絕提供服務(wù)|
          200 客戶端請求成功|
          404 請求資源不存在 eg:輸入錯誤的URL |
          500 服務(wù)器發(fā)生不可預(yù)期的錯誤 |
          503 服務(wù)器當(dāng)前不能處理客戶端請求,一段時間后可能恢復(fù)正常

          JS 中的主要有哪幾類錯誤

          JS有三類的錯誤:

          加載時錯誤:加載web頁面時出現(xiàn)的錯誤(如語法錯誤)稱為加載時錯誤,它會動態(tài)生成錯誤。

          運(yùn)行時錯誤:由于濫用html語言中的命令而導(dǎo)致的錯誤。

          邏輯錯誤:這些錯誤是由于對具有不同操作的函數(shù)執(zhí)行了錯誤的邏輯而導(dǎo)致的

          列出JS中的一些設(shè)計模式

          設(shè)計模式是軟件設(shè)計中常見問題的通用可重用解決方案,以下是一些設(shè)計模式是:

          創(chuàng)建模式:該模式抽象了對象實例化過程。

          結(jié)構(gòu)型模式:這些模式處理不同的類和對象以提供新功能。

          行為模式:也稱發(fā)布-訂閱模式,定義了一個被觀察者和多個觀察者的、一對多的對象關(guān)系。

          并行設(shè)計模式:這些模式處理多線程編程范例。

          架構(gòu)設(shè)計模式:這些模式用于處理架構(gòu)設(shè)計。

          es6的新特性

          1. const let

          2. 模板字符串

          3. 箭頭函數(shù)

          4. 函數(shù)的參數(shù)默認(rèn)值

          5. 對象和數(shù)組解構(gòu)

          6. for...of 和 for...in

          解釋一下什么是箭頭函數(shù)?

          箭頭函數(shù)是在es6或更高版本中編寫函數(shù)表達(dá)式的簡明方法。箭頭函數(shù)不能用作構(gòu)造函數(shù),也不支持this,arguments,super或new.target關(guān)鍵字,它最適合非方法函數(shù)。 

          通常,箭頭函數(shù)看起來像 const function_name =()=> {}。

          const greet=()=>{console.log('hello');}greet();

          普通函數(shù)和箭頭函數(shù)的區(qū)別

          1、普通函數(shù)
          可以通過bind、call、apply改變this指向
          可以使用new
          2、箭頭函數(shù)
          本身沒有this指向,
          它的this在定義的時候繼承自外層第一個普通函數(shù)的this
          被繼承的普通函數(shù)的this指向改變,箭頭函數(shù)的this指向會跟著改變
          箭頭函數(shù)外層沒有普通函數(shù)時,this指向window
          不能通過bind、call、apply改變this指向
          使用new調(diào)用箭頭函數(shù)會報錯,因為箭頭函數(shù)沒有constructor

          解釋一下什么是 promise ?

          promise是js中的一個對象,用于生成可能在將來產(chǎn)生結(jié)果的值。 值可以是已解析的值,也可以是說明為什么未解析該值的原因。

          promise 可以有三種狀態(tài):

          pending:初始狀態(tài),既不是成功也不是失敗
          fulfilled:意味著操作完全成功
          rejected:意味著操作失敗

          一個等待狀態(tài)的promise對象能夠成功后返回一個值,也能失敗后帶回一個錯誤,當(dāng)這兩種情況發(fā)生的時候,處理函數(shù)會排隊執(zhí)行通過then方法會被調(diào)用。

          resolve和reject分別是兩個函數(shù),當(dāng)在回調(diào)中調(diào)用時,會改變promise實例的狀態(tài),resolve改變狀態(tài)為成功,reject為失敗。

          手寫promise 

          function Promise(exector) {  let _this = this;  //status表示一種狀態(tài)  let status = “pending“;  let value = undefined;  let reason = undefined;  //成功  function resolve(value) {    if (status == “pending“) {      _this.value = value;      _this.status = “resolve“;    }  }  //執(zhí)行失敗  function reject(value) {    if (status == “pending“) {      _this.value = value;      _this.status = “reject“    }  }  //異步操作  try {    exector(resolve, reject)  } catch (e) {    reject(e)  }  //測試then  Promise.prototype.then = function(reject, resolve) {    let _this = this;    if (this.status == “resolve“) {      reject(_this.value)    }    if (this.status == “reject“) {      resolve(_this.reason)    }  }} //new Promise測試let promise = new Promise((reject,resolve)=>{    resolve(“return resolve“);});promise.then(data => {  console.log(`success${data}`);}, err => {  console.log(`err${data}`);})

          promise、async有什么區(qū)別

          1、什么是Async/Await
          async/await是寫異步代碼的新方式,使用的方式看起來像同步
          async/await是基于Promise實現(xiàn)的,它不能用于普通的回調(diào)函數(shù)。
          2、什么是promise
          為了解決異步嵌套而產(chǎn)生,讓代碼更易于理解
          區(qū)別:async/await讓代碼更像同步,進(jìn)一步優(yōu)化了代碼

          介紹各種異步方案

          (1)回調(diào)函數(shù)(異步回調(diào))

          回調(diào)是一個函數(shù)被作為一個參數(shù)傳遞到另一個函數(shù)里,在那個函數(shù)執(zhí)行完后再執(zhí)行。

          (2)promise 

          promise對象是commonJS工作組提出的一種規(guī)范,一種模式,目的是為了異步編程提供統(tǒng)一接口。

          (3)async/await

          (4)事件監(jiān)聽

          采用事件驅(qū)動模式。任務(wù)的執(zhí)行不取決于代碼的順序,而取決于某個事件是否發(fā)生。

          (5)發(fā)布/訂閱

          module.exports 和 exports 之間有什么區(qū)別?

          module和exports是Node.js給每個js文件內(nèi)置的兩個對象。可以通過console.log(module)和console.log(exports)打印出來。如果你在main.js中寫入下面兩行,然后運(yùn)行$ node main.js:

          console.log(exports);//輸出:{}console.log(module);//輸出:Module {..., exports: {}, ...} (注:...代表省略了其他一些屬性)

          從打印咱們可以看出,module.exports和exports一開始都是一個空對象{},實際上,這兩個對象指向同一塊內(nèi)存。這也就是說module.exports和exports是等價的(有個前提:不去改變它們指向的內(nèi)存地址)。

          例如:exports.age = 18和module.export.age = 18,這兩種寫法是一致的(都相當(dāng)于給最初的空對象{}添加了一個屬性,通過require得到的就是{age: 18})。

          import 和 exports 是什么?

          import和exports 幫助咱們編寫模塊化的JS代碼。使用import和exports,咱們可以將代碼分割成多個文件。import只允許獲取文件的某些特定變量或方法??梢詫?dǎo)入模塊導(dǎo)出的方法或變量。

           //index.js import name,age from './person';  console.log(name); console.log(age);
          //person.js let name ='Sharad', occupation='developer', age =26; export { name, age};

          學(xué)習(xí)更多技能

          請點(diǎn)擊下方公眾號


          瀏覽 43
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  wwwww黄 | 色一区二区三区 | 日韩一级毛方 | 日本黄色三级视频 | 国产精品——色哟哟 |