<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # Js常見面試題 ## 數據類型 ### JS的基本數據類型 `Undefined、Null、Boolean、Number、String`新增:`Symbol` ### JS有哪些內置對象? `Object`?是?`JavaScript`?中所有對象的父對象 數據封裝類對象:`Object、Array、Boolean、Number`?和?`String` 其他對象:`Function、Arguments、Math、Date、RegExp、Error` ### JS中使用typeof能得到的哪些類型?(考點:JS變量類型) ~~~javascript typeof undefinded //undefined typeof null // object typeof'abc' // string typeof 123 // number typeof [] // object typeof {} // object typeof true //boolean typeof b // b 沒有聲明,但是還會顯示 undefined ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 何時使用`===`何時使用`==`?(考點:強制類型轉換) `==`?比較兩個值相等返回`ture` `===`?比較兩者的值和類型都相等才返回`true`?嚴格相等 `0,NAN,null,undefinded,'',false`?在`if`語句中會強制轉化為?`false` ~~~javascript if (obj.a == null) { // 這里相當于 obj.a === null || obj.a ===undefinded, 簡寫形式 // 這里jquery 源碼中推薦的寫法 } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### `null,undefined`?的區別? 1、`null`:?`Null`類型,代表“空值”,代表一個空對象指針,使用`typeof`運算得到?`“object”`,所以你可以認為它是一個特殊的對象值。 2、`undefined`:?`Undefined`類型,當一個聲明了一個變量未初始化時,得到的就是`undefined`。 ps:一句話簡單來說`null`和`undefine`值比較是相等,但類型不同。 ### Javascript創建對象的幾種方式? 1、對象字面量的方式 ~~~javascript var = {} ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") 2、通過構造函數方式創建。 ~~~javascript var obj = new Object(); ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") 3、通過`Object.create()`方式創建。 ~~~javascript var obj = Object.create(Object.prototype); ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 翻轉一個字符串 可以先將字符串轉成一個數組,然后用數組的`reverse()+join()`方法。 ~~~javascript let str="hello word"; let b=[...str].reverse().join("");//drow olleh ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 描述`new`一個對象的過程 1、創建一個新對象 2、`this`指向這個對象 3、執行代碼,即對`this`賦值 4、隱式返回`this` ### JS按存儲方式區分變量類型 ~~~javascript // 值類型 var a = 10 var b = a a = 11 console.log(b) // 10 // 引用類型 var obj1 = {x : 100} var obj2 = obj1 obj1.x = 200 console.log(obj2) // 200 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 如何判斷一個變量是對象還是數組? 1、`instanceof`方法?`instanceof`運算符是用來測試一個對象是否在其原型鏈原型構造函數的屬性 ~~~javascript var arr = []; arr instanceof Array; // true ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") 2、`constructor`方法?`constructor`屬性返回對創建此對象的數組函數的引用,就是返回對象相對應的構造函數 ~~~javascript var arr = []; arr.constructor == Array; //true ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") 3、最簡單的方法,這種寫法兼容性最好使用`Object.prototype.toString.call()` ~~~javascript function isObjArr(value){ if (Object.prototype.toString.call(value) === "[object Array]") { console.log('value是數組'); }else if(Object.prototype.toString.call(value)==='[object Object]'){//這個方法兼容性好一點 console.log('value是對象'); }else{ console.log('value不是數組也不是對象') } } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") 4、`ES5`新增方法`isArray()` ~~~javascript var a = new Array(123); var b = new Date(); console.log(Array.isArray(a)); //true console.log(Array.isArray(b)); //false ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ps:千萬不能使用`typeof`來判斷對象和數組,因為這兩種類型都會返回`"object"`。 ### 如何對一個數組去重? 1、`Set`結構去重(`ES6`用法)。 `ES6`提供了新的數據結構`Set`。它類似于數組,但是成員的值都是唯一的,沒有重復的值。 ~~~javascript [...new Set(array)]; ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") 2、遍歷,將值添加到新數組,用`indexOf()`判斷值是否存在,已存在就不添加,達到去重效果。 ~~~javascript let a = ['1','2','3',1,NaN,NaN,undefined,undefined,null,null, 'a','b','b']; let unique= arr =>{ let newA=[]; arr.forEach(key => { if( newA.indexOf(key)<0 ){ //遍歷newA是否存在key,如果存在key會大于0就跳過push的那一步 newA.push(key); } }); return newA; } console.log(unique(a)) ;//["1", "2", "3", 1, NaN, NaN, undefined, null, "a", "b"] // 這個方法不能分辨NaN,會出現兩個NaN。是有問題的,下面那個方法好一點。 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") 3、利用`for`嵌套`for`,然后`splice`去重(`ES5`中最常用)。 ~~~javascript function unique(arr){ for(var i=0; i<arr.length; i++){ for(var j=i+1; j<arr.length; j++){ if(arr[i]==arr[j]){ //第一個等同于第二個,splice方法刪除第二個 arr.splice(j,1); j--; } } } return arr; } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) //[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}] //NaN和{}沒有去重,兩個null直接消失了 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") 4、`forEach`遍歷,然后利用`Object.keys(對象)`返回這個對象可枚舉屬性組成的數組,這個數組就是去重后的數組。 ~~~javascript let a = ['1', '2', '3', 1,NaN,NaN,undefined,undefined,null,null, 'a', 'b', 'b']; const unique = arr => { var obj = {} arr.forEach(value => { obj[value] = 0;//這步新添加一個屬性,并賦值,如果不賦值的話,屬性會添加不上去 }) return Object.keys(obj);//`Object.keys(對象)`返回這個對象可枚舉屬性組成的數組,這個數組就是去重后的數組 } console.log(unique(a));//["1", "2", "3", "NaN", "undefined", "null", "a", "b"] ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ## 作用域和閉包 ### `var、let、const`之間的區別 `var`聲明變量可以重復聲明,而`let`不可以重復聲明 `var`是不受限于塊級的,而`let`是受限于塊級 `var`會與`window`相映射(會掛一個屬性),而`let`不與`window`相映射 `var`可以在聲明的上面訪問變量,而`let`有暫存死區,在聲明的上面訪問變量會報錯 `const`聲明之后必須賦值,否則會報錯 `const`定義不可變的量,改變了就會報錯 `const`和`let`一樣不會與`window`相映射、支持塊級作用域、在聲明的上面訪問變量會報錯 ### 說明`This`幾種不同的使用場景 1、作為構造函數執行 2、作為對象屬性執行 3、作為普通函數執行 4、`call apply bind` ### 談談`This`對象的理解 1、`this`總是指向函數的直接調用者(而非間接調用者) 2、如果有`new`關鍵字,`this`指向`new`出來的那個對象 3、在事件中,`this`指向觸發這個事件的對象,特殊的是,`IE`中的`attachEvent`中的`this`總是指向全局對象`Window` ### 作用域 `ES5`作用域分為 全局作用域 和 函數作用域。 `ES6`新增塊級作用域,塊作用域由?`{ }`包括,`if`語句和?`for`語句里面的`{ }`也屬于塊作用域。 ~~~javascript // 塊級作用域 if (true) { var name = 'zhangsan' } console.log(name) // 函數和全局作用域 var a = 100 function fn () { var a = 200 console.log('fn', a) } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 用`JS`創建10個`<a>`標簽,點擊的時候彈出來對應的序號?(考點:作用域) ~~~javascript var i for (i = 0; i < 10; i++) { (function (i) { var a = document.createElement('a') a.innerHTML = i + '<br>' a.addEventListener('click', function (e) { e.preventDefault() alert(i) }) document.body.appendChild(a) })(i) } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 說說你對作用域鏈的理解 1、作用域鏈的作用是保證執行環境里有權訪問的變量和函數是有序的,作用域鏈的變量只能向上訪問,變量訪問到`window`對象即被終止,作用域鏈向下訪問變量是不被允許的 2、簡單的說,作用域就是變量與函數的可訪問范圍,即作用域控制著變量與函數的可見性和生命周期 3、通俗來說,一般情況下,變量取值到 創建 這個變量 的函數的作用域中取值。 但是如果在當前作用域中沒有查到值,就會向上級作用域去查,直到查到全局作用域,這么一個查找過程形成的鏈條就叫做作用域鏈 ~~~javascript var a = 100 function fn () { var b = 200 // 當前作用域沒有定于的變量,即‘自由變量’ console.log(a) console.log(b) } fn() console.log(a) 去父級作用域找a自由變量 作用域鏈 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 閉包 閉包就是能夠讀取其他函數內部變量的函數 閉包是指有權訪問另一個函數作用域中變量的函數,創建閉包的最常見的方式就是在一個函數內創建另一個函數,通過另一個函數訪問這個函數的局部變量,利用閉包可以突破作用鏈域 ~~~javascript function F1 () { var a = 100 // 返回一個函數,函數作為返回值 return function () { console.log(a) } } // f1 得到一個函數 var f1 = F1() var a = 200 f1() // 100 定義的時候父級作用域 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 閉包的特性 函數內再嵌套函數 內部函數可以引用外層的參數和變量 參數和變量不會被垃圾回收機制回收 ### 說說你對閉包的理解 使用閉包主要是為了設計私有的方法和變量。閉包的優點是可以避免全局變量的污染,缺點是閉包會常駐內存,會增大內存使用量,使用不當很容易造成內存泄露。在js中,函數即閉包,只有函數才會產生作用域的概念 閉包的最大用處有兩個,一個是可以讀取函數內部的變量,另一個就是讓這些變量始終保持在內存中 閉包的另一個用處,是封裝對象的私有屬性和私有方法 好處:能夠實現封裝和緩存等; 壞處:就是消耗內存、不正當使用會造成內存溢出的問題 ### 使用閉包的注意點 由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露 解決方法是,在退出函數之前,將不使用的局部變量全部刪除 ### 閉包的使用場景 閉包的兩個場景:函數作為返回值 和 函數作為參數傳遞 ~~~javascript function F1 () { var a = 100 // 返回一個函數,函數作為返回值 return function () { console.log(a) } } // f1 得到一個函數 var f1 = F1() var a = 200 f1() // 100 定義的時候父級作用域 function F2 (fn) { var a = 200 fn() } F2(f1) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 實際開發中閉包的應用 ~~~javascript // 閉包實際應用中主要用于封裝變量 收斂權限 function isFirstLoad() { var _list = [] return function (id) { if (_list.indexOf(id) >= 0) { return false } else { _list.push(id) return true } } } // 使用 var firstLoad = new isFirstLoad() firstLoad(10) //true firstLoad(10) //false firstLoad(30) //true ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ## 原型和原型鏈 ### JavaScript原型,原型鏈 ? 有什么特點? 每個對象都會在其內部初始化一個屬性,就是`prototype`(原型), 當我們訪問一個對象的屬性時,如果這個對象內部不存在這個屬性,那么他就會去`prototype`里找這個屬性,這個`prototype`又會有自己的`prototype`,于是就這樣一直找下去,也就是我們平時所說的原型鏈的概念 原型和原型鏈關系 **關系:**`instance.constructor.prototype = instance.__proto__` 原型和原型鏈特點 `JavaScript`對象是通過引用來傳遞的,我們創建的每個新對象實體中并沒有一份屬于自己的原型副本。當我們修改原型時,與之相關的對象也會繼承這一改變 當我們需要一個屬性的時,`Javascript`引擎會先看當前對象中是否有這個屬性, 如果沒有的 就會查找他的Prototype對象是否有這個屬性,如此遞推下去,一直檢索到?`Object`?內建對象 PS: 1.所有的引用類型(數組,對象,函數)都具有對象的特性,即可自由擴展屬性(除了‘`null`’)除外 2.所有的引用類型(數組,對象,函數)都有一個`_proto_`(隱式原型)屬性,屬性值是一個普通對象 3.所有的函數,都有一個`prototype`(顯示原型)的屬性,也是一個普通對象 4.所有的引用類型(數組,對象,函數)`_proto_`屬性值指向他的構造的‘`prototype`’ 屬性值 ### 當試圖得到一個對象的某個屬性時,如果這個對象本身沒有這個屬性,那么會去它的`_proto_`(即它的構造函數的`prototype`) 中尋找 ~~~javascript // 構造函數 function Foo(name, age){ this.name = name } Foo.prototype.alertName = function () { alert(this.name) } //創建實例 var f = new Foo('zhangsan') f.printName = function () { console.log(this.name) } //測試 f.printName() f.alertName() ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 循環對象自身的屬性 ~~~javascript var item for (item in f) { // 高級瀏覽器已經在 for in 中屏蔽了來自原型的屬性 // 但是這里建議大家還是加上這個判斷,保證程序的健壯性 if (f.hasOwnProperty(item)) { console.log(item) } } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 原型鏈 ~~~javascript f.toString() //到f._proto_._proto_ 中去找 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ## 異步和單線程 ### 同步和異步的區別是什么?分別舉一個同步和異步的例子 `同步交互`:指發送一個請求,需要等待返回,然后才能夠發送下一個請求,有個等待過程。 `異步交互`:指發送一個請求,不需要等待返回,隨時可以再發送下一個請求,即不需要等待。 `相同的地方`:都屬于交互方式,都是發送請求。 `不同的地方`:一個需要等待,一個不需要等待。 **簡單而言**,同步就是必須一件一件的做事,等前一件事做完后才能做下一件事。而異步這是把事情指派給別人后,接著繼續做下一件事,不必等別人返回的結果。 **舉例:** 1、廣播,就是一個異步例子。發起者不關心接收者的狀態。不需要等待接收者的返回信息; 在部分情況下,我們的項目開發中都會優先選擇不需要等待的異步交互方式。 2、電話,就是一個同步例子。發起者需要等待接收者,接通電話后,通信才開始。需要等待接收者的返回信息 比如銀行的轉賬系統,對數據庫的保存操作等等,都會使用同步交互操作。 ps:?`alert`是同步,`setTimeout`和`setInterval`是異步,同步會阻塞代碼執行,而異步不會 ### 異步編程的實現方式? 1、`回調函數` 優點:簡單、容易理解 缺點:不利于維護,代碼耦合高 2、`事件監聽(采用時間驅動模式,取決于某個事件是否發生):` 優點:容易理解,可以綁定多個事件,每個事件可以指定多個回調函數 缺點:事件驅動型,流程不夠清晰 3、`發布/訂閱(觀察者模式)` 類似于事件監聽,但是可以通過‘消息中心’,了解現在有多少發布者,多少訂閱者 4、`Promise對象` 優點:可以利用`then`方法,進行鏈式寫法;可以書寫錯誤時的回調函數; 缺點:編寫和理解,相對比較難 5、`Generator函數` 優點:函數體內外的數據交換、錯誤處理機制 缺點:流程管理不方便 6、`async函數` 優點:內置執行器、更好的語義、更廣的適用性、返回的是`Promise`、結構清晰。 缺點:錯誤處理機制 ### 定時器的執行順序或機制。 簡單來說:**因為`js`是單線程的,瀏覽器遇到`setTimeout`或者`setInterval`會先執行完當前的代碼塊,在此之前會把定時器推入瀏覽器的待執行事件隊列里面,等到瀏覽器執行完當前代碼之后會看一下事件隊列里面有沒有任務,有的話才執行定時器的代碼。**?所以即使把定時器的時間設置為`0`還是會先執行當前的一些代碼。 ### 一個關于`setTimeout`的筆試題 ~~~javascript console.log(1) setTimeout(function () { console.log(2) },0) console.log(3) setTimeout(function () { console.log(4) },1000) console.log(5) //結果為 1 3 5 2 4 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 前段使用異步的場景 1、定時任務:`setTimeout,setInverval` 2、網絡請求:`ajax`請求,動態`<img>`加載 3、事件綁定 ## 數組和對象`API` ### map與forEach的區別? 1、`forEach`方法,是最基本的方法,就是遍歷與循環,默認有3個傳參:分別是遍歷的數組內容`item`、數組索引`index`、和當前遍歷數組`Array` 2、`map`方法,基本用法與`forEach`一致,但是不同的,它會返回一個新的數組,所以在`callback`需要有`return`值,如果沒有,會返回`undefined` ### JS 數組和對象的遍歷方式,以及幾種方式的比較 通常我們會用循環的方式來遍歷數組。但是循環是 導致js 性能問題的原因之一。一般我們會采用下幾種方式來進行數組的遍歷 `for in循環` `for循環` `forEach` 1、這里的?`forEach`回調中兩個參數分別為?`value,index` 2、`forEach`?無法遍歷對象 3、`IE`不支持該方法;`Firefox`?和?`chrome`?支持 4、`forEach`?無法使用?`break,continue`?跳出循環,且使用?`return`?是跳過本次循環 5、可以添加第二個參數,為一個數組,回調中的this會指向這個數組,若沒有添加,則是指向`window`; 在方式一中,`for-in`需要分析出`array`的每個屬性,這個操作性能開銷很大。用在?`key`?已知的數組上是非常不劃算的。所以盡量不要用`for-in`,除非你不清楚要處理哪些屬性,例如`JSON`對象這樣的情況 在方式2中,循環每進行一次,就要檢查一下數組長度。讀取屬性(數組長度)要比讀局部變量慢,尤其是當`array`里存放的都是?`DOM`元素,因為每次讀取都會掃描一遍頁面上的選擇器相關元素,速度會大大降低 ### 寫一個能遍歷對象和數組的通用`forEach`函數 ~~~javascript function forEach(obj, fn) { var key // 判斷類型 if (obj instanceof Array) { obj.forEach(function (item, index) { fn(index, item) }) } else { for (key in obj) { fn ( key, obj[key]) } } } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 數組`API` ~~~javascript map: 遍歷數組,返回回調返回值組成的新數組 forEach: 無法break,可以用try/catch中throw new Error來停止 filter: 過濾 some: 有一項返回true,則整體為true every: 有一項返回false,則整體為false join: 通過指定連接符生成字符串 push / pop: 末尾推入和彈出,改變原數組, 返回推入/彈出項【有誤】 unshift / shift: 頭部推入和彈出,改變原數組,返回操作項【有誤】 sort(fn) / reverse: 排序與反轉,改變原數組 concat: 連接數組,不影響原數組, 淺拷貝 slice(start, end): 返回截斷后的新數組,不改變原數組 splice(start, number, value...): 返回刪除元素組成的數組,value 為插入項,改變原數組 indexOf / lastIndexOf(value, fromIndex): 查找數組項,返回對應的下標 reduce / reduceRight(fn(prev, cur), defaultPrev): 兩兩執行,prev 為上次化簡函數的return值,cur 為當前值(從第二項開始) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 對象`API` ~~~javascript var obj = { x: 100, y: 200, z: 300 } var key for (key in obj) { // 注意這里的 hasOwnProperty,再講原型鏈的時候講過 if (obj.hasOwnProperty(key)) { console.log(key, obj[key]) } } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ## 日期和隨機數 ### 獲取`2020-06-10`格式的日期 ~~~javascript Date.now() // 獲取當前時間毫秒數 var dt = new Date() dt.getTime() //獲取毫秒數 dt.getFullYear() // 年 dt.getMonth() // 月 (0-11) dt.getDate() // 日 (0-31) dt.getHours() // 小時 (0-23) dt.getMinutes() //分鐘 (0-59) dt.getSeconds() //秒 (0-59) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ps:?`ES6`?新增`padStart(),padEnd()`方法可用來返回`06`格式日期 ### 獲取隨機數,要求是長度一致的字符串格式 ~~~javascript var random = Math.random() var random = random + '0000000000' console.log(random) var random = random.slice(0, 10) console.log(random) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ## `DOM`和`BOM`操作 ### `DOM`是哪種基本的數據結構 `DOM`是一種樹形結構的數據結構 ### `DOM`操作的常用`API`有哪些 1、獲取`DOM`節點,以及節點的`property`和`Attribute` 2、獲取父節點,獲取子節點 3、新增節點,刪除節點 ### `DOM`節點的`Attribute`和`property`有何區別 `property`只是一個`JS`對象的屬性修改和獲取 `Attribute`是對`html`標簽屬性的修改和獲取 ### `DOM`的節點操作 **創建新節點** ~~~javascript createDocumentFragment() //創建一個DOM片段 createElement() //創建一個具體的元素 createTextNode() //創建一個文本節點 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") **添加、移除、替換、插入** ~~~ appendChild() //添加 removeChild() //移除 replaceChild() //替換 insertBefore() //插入 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") **查找** ~~~javascript getElementsByTagName() //通過標簽名稱 getElementsByName() //通過元素的Name屬性的值 getElementById() //通過元素Id,唯一性 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") **如何檢測瀏覽器的類型** 可以通過檢測`navigator.userAgent`?在通過不通瀏覽器的不通來檢測 ~~~javascript var ua = navigator.userAgent var isChrome = ua.indexOf('Chrome') console.log(isChrome) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") **拆解`url`的各部分** 使用`location`里面的`location.href location.protocol location.pathname location.search location.hash`來獲取各種參數 ~~~javascript console.log(location.href) console.log(location.host) //域名 console.log(location.protocol) console.log(location.pathname) //路徑 console.log(location.search) // 參數 console.log(location.hash) // history history.back() history.forward() ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ## 事件機制 ### 請解釋什么是事件代理 事件代理(`Event Delegation`),又稱之為事件委托。是?`JavaScript`?中常用綁定事件的常用技巧。顧名思義,“事件代理”即是把原本需要綁定的事件委托給父元素,讓父元素擔當事件監聽的職務。事件代理的原理是`DOM`元素的事件冒泡。 **使用事件代理的好處是:** 1、可以提高性能 2、可以大量節省內存占用,減少事件注冊,比如在`table`上代理所有`td`的`click`事件就非常棒 3、可以實現當新增子對象時無需再次對其綁定 ### 事件模型 `W3C`中定義事件的發生經歷三個階段:`捕獲階段(capturing)、目標階段(targetin)、冒泡階段(bubbling)` `冒泡型事件:`當你使用事件冒泡時,子級元素先觸發,父級元素后觸發 `捕獲型事件:`當你使用事件捕獲時,父級元素先觸發,子級元素后觸發 `DOM事件流:`<時支持兩種事件模型:捕獲型事件和冒泡型事件 `阻止冒泡:`在`W3c`中,使用`stopPropagation()`方法;在`IE`下設置`cancelBubble = true` `阻止捕獲:`阻止事件的默認行為,例如`click - <a>后`的跳轉。在`W3c`中,使用`preventDefault()`方法,在`IE`下設置`window.event.returnValue = false` ### 編寫一個通用的事件監聽函數 ~~~javascript function bindEvent(elem,type,selector,fn){ if(fn==null){ fn = selector; selector = null } elem.addEventListener(type,function(e){ var target; if(selector){ target = e.target; if(target.matches(selector)){ fn.call(target,e) } }else{ fn(e) } }) } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 描述事件冒泡流程 當給某元素綁定一個事件的時候,首先會觸發自己綁定的,然后會逐層向上級查找事件,這就是事件冒泡 ~~~javascript var p1 = document.getElementById('p1') var body = document.body function bindEvent(elem, type, fn) { elem.addEventListener(type, fn) } bindEvent(p1, 'click', function (e) { e.stopPropagation() var target = e.target alert("激活") }) bindEvent(body, 'click', function (e) { var target = e.target alert("取消") }) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 對于一個無限下拉加載圖片的頁面,如何給每個圖片綁定事件 可以使用代理,通過對父級元素綁定一個事件,通過判斷事件的`target`屬性來進行判斷,添加行為 ~~~javascript var div1 = document.getElementById('div1') var div2 = document.getElementById('div2') function bindEvent(elem, type, fn) { elem.addEventListener(type, fn) } bindEvent(div1, 'click', function (e) { var target = e.target alert(target.innerHTML) }) bindEvent(div2, 'click', function (e) { var target = e.target alert(target.innerHTML) }) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ## AJAX ### `Ajax`原理 `Ajax`的原理簡單來說是在用戶和服務器之間加了—個中間層(`AJAX`引擎),通過`XmlHttpRequest`對象來向服務器發異步請求,從服務器獲得數據,然后用`javascript`來操作`DOM`而更新頁面。使用戶操作與服務器響應異步化。這其中最關鍵的一步就是從服務器獲得請求數據 `Ajax`的過程只涉及`JavaScript、XMLHttpRequest和DOM`。`XMLHttpRequest`是ajax的核心機制 ### 手動編寫一個`ajax`,不依賴第三方庫 ~~~javascript // 1. 創建連接 var xhr = null; xhr = new XMLHttpRequest() // 2. 連接服務器 xhr.open('get', url, true) // 3. 發送請求 xhr.send(null); // 4. 接受請求 xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if(xhr.status == 200){ success(xhr.responseText); } else { // fail fail && fail(xhr.status); } } } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") `ajax`的優點和缺點 `ajax的優點` 1、無刷新更新數據(在不刷新整個頁面的情況下維持與服務器通信) 2、異步與服務器通信(使用異步的方式與服務器通信,不打斷用戶的操作) 3、前端和后端負載均衡(將一些后端的工作交給前端,減少服務器與寬度的負擔) 4、界面和應用相分離(`ajax`將界面和應用分離也就是數據與呈現相分離) `ajax的缺點` 1、ajax不支持瀏覽器back按鈕 2、安全問題?`Aajax`暴露了與服務器交互的細節 3、對搜索引擎的支持比較弱 4、破壞了`Back`與`History`后退按鈕的正常行為等瀏覽器機制。 ### 什么情況下會碰到跨域問題?有哪些解決方法? 跨域問題是這是瀏覽器為了安全實施的同源策略導致的,同源策略限制了來自不同源的`document、腳本`,同源的意思就是兩個`URL`的域名、協議、端口要完全相同。 `script`標簽`jsonp`跨域、`nginx`反向代理、`node.js`中間件代理跨域、后端在頭部信息設置安全域名、后端在服務器上設置`cors`。 ps:有三個標簽允許跨域加載資源:?`<img src=xxx> <link href = xxx> <script src=xxx>` 三個標簽場景: `<img>`?用于打點統計,統計網站可能是其他域 `<link> <script>`?可以使用`CDN`,`CDN`的也是其他域 `<script>`可以用于JSONP **??跨域注意事項** 所有的跨域請求都必須經過信息提供方允許 如果未經允許即可獲取,那么瀏覽器同源策略出現漏洞 ### `XML`和`JSON`的區別? **數據體積方面** `JSON`相對于`XML`來講,數據的體積小,傳遞的速度更快些。 **數據交互方面** `JSON`與`JavaScript`的交互更加方便,更容易解析處理,更好的數據交互 **數據描述方面** `JSON`對數據的描述性比`XML`較差 **傳輸速度方面** `JSON`的速度要遠遠快于`XML` ### `get`和`post`的區別 1、`get`和`post`在`HTTP`中都代表著請求數據,其中get請求相對來說更簡單、快速,效率高些 2、`get`相對`post`安全性低 3、`get`有緩存,`post`沒有 4、`get`體積小,`post`可以無限大 5、`get`的`url`參數可見,`post`不可見 6、`get`只接受`ASCII`字符的參數數據類型,`post`沒有限制 7、`get`請求參數會保留歷史記錄,`post`中參數不會保留 8、`get`會被瀏覽器主動`catch,post`不會,需要手動設置 9、`get`在瀏覽器回退時無害,`post`會再次提交請求 什么時候使用`post`? **`post`一般用于修改服務器上的資源,對所發送的信息沒有限制。比如** 1、無法使用緩存文件(更新服務器上的文件或數據庫) 2、向服務器發送大量數據(`POST`?沒有數據量限制) 3、發送包含未知字符的用戶輸入時,`POST`?比?`GET`?更穩定也更可靠 ### `TCP`建立連接為什么需要三次? * 為了實現可靠數據傳輸,?`TCP`?協議的通信雙方, 都必須維護一個序列號, 以標識發送出去的數據包中, 哪些是已經被對方收到的。 三次握手的過程即是通信雙方相互告知序列號起始值, 并確認對方已經收到了序列號起始值的必經步驟 * 如果只是兩次握手, 至多只有連接發起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認 ## ES6 ### `ES5`的繼承和`ES6`的繼承有什么區別? `ES5`的繼承時通過`prototype`或構造函數機制來實現。**`ES5`的繼承實質上是先創建子類的實例對象,然后再將父類的方法添加到`this`上**(`Parent.apply(this)`)。 `ES6`的繼承機制完全不同,**實質上是先創建父類的實例對象`this`(所以必須先調用父類的`super()`方法),然后再用子類的構造函數修改`this`**。 具體的:`ES6`通過`class`關鍵字定義類,里面有構造方法,類之間通過`extends`關鍵字實現繼承。子類必須在`constructor`方法中調用`super`方法,否則新建實例報錯。因為子類沒有自己的`this`對象,而是繼承了父類的`this`對象,然后對其進行加工。如果不調用`super`方法,子類得不到`this`對象。 ps:`super`關鍵字指代父類的實例,即父類的`this`對象。在子類構造函數中,調用`super`后,才可使用`this`關鍵字,否則報錯 ### `javascript`如何實現繼承? 1、`構造繼承` 2、`原型繼承` 3、`實例繼承` 4、`拷貝繼承` `原型prototype`機制或`apply和call`方法去實現較簡單,建議使用構造函數與原型混合方式 ~~~javascript function Parent(){ this.name = 'wang'; } function Child(){ this.age = 28; } Child.prototype = new Parent();//繼承了Parent,通過原型 var demo = new Child(); alert(demo.age); alert(demo.name);//得到被繼承的屬性 } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 談談你對`ES6`的理解 新增模板字符串(為`JavaScript`提供了簡單的字符串插值功能) 箭頭函數 `for-of`(用來遍歷數據—例如數組中的值。) `arguments`對象可被不定參數和默認參數完美代替。 `ES6`將`promise`對象納入規范,提供了原生的`Promise`對象。 增加了`let和const`命令,用來聲明變量。 增加了塊級作用域。 `let`命令實際上就增加了塊級作用域。 還有就是引入`module`模塊的概念 ### 談一談箭頭函數與普通函數的區別? * 函數體內的`this`對象,就是定義時所在的對象,而不是使用時所在的對象 * 不可以當作構造函數,也就是說,不可以使用`new`命令,否則會拋出一個錯誤 * 不可以使用`arguments`對象,該對象在函數體內不存在。如果要用,可以用`Rest`參數代替 * 不可以使用`yield`命令,因此箭頭函數不能用作`Generator`函數 ### `forEach、for in、for of`三者區別 `forEach`更多的用來遍歷數 `for in`一般常用來遍歷對象或`json` `for of`數組對象都可以遍歷,遍歷對象需要通過和`Object.keys()` `for in`循環出的是`key,for of`循環出的是`value` ### `Set、Map`的區別 應用場景`Set`用于數據重組,Map用于數據儲存 **`Set:`** 1,成員不能重復 2,只有鍵值沒有鍵名,類似數組 3,可以遍歷,方法有`add, delete,has` **`Map:`** 1,本質上是健值對的集合,類似集合 2,可以遍歷,可以跟各種數據格式轉換 ### `promise`對象的用法,手寫一個`promise` `promise`是一個構造函數,下面是一個簡單實例 ~~~javascript var promise = new Promise((resolve,reject) => { if (操作成功) { resolve(value) } else { reject(error) } }) promise.then(function (value) { // success },function (value) { // failure }) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 請描述一下`Promise`的使用場景,'`Promise`'它所解決的問題以及現在對于異步操作的解決方案。 `Promise`的使用場景:`ajax`請求,回調函數,復雜操作判斷。 `Promise`是`ES6`為了解決異步編程所誕生的。 異步操作解決方案:`Promise、Generator`、定時器(不知道算不算)、還有`ES7`的`async` ### `ECMAScript6`?怎么寫`class`,為什么會出現`class`這種東西? 這個語法糖可以讓有`OOP`基礎的人更快上手`js`,至少是一個官方的實現了 但對熟悉`js`的人來說,這個東西沒啥大影響;一個`Object.creat()`搞定繼承,比`class`簡潔清晰的多 ## 算法和其他 ### 冒泡排序 每次比較相鄰的兩個數,如果后一個比前一個小,換位置 ~~~javascript var arr = [3, 1, 4, 6, 5, 7, 2]; function bubbleSort(arr) { for (var i = 0; i < arr.length - 1; i++) { for(var j = 0; j < arr.length - i - 1; j++) { if(arr[j + 1] < arr[j]) { var temp; temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } return arr; } console.log(bubbleSort(arr)); ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 快速排序 采用二分法,取出中間數,數組每次和中間數比較,小的放到左邊,大的放到右邊 ~~~javascript var arr = [3, 1, 4, 6, 5, 7, 2]; function quickSort(arr) { if(arr.length == 0) { return []; // 返回空數組 } var cIndex = Math.floor(arr.length / 2); var c = arr.splice(cIndex, 1); var l = []; var r = []; for (var i = 0; i < arr.length; i++) { if(arr[i] < c) { l.push(arr[i]); } else { r.push(arr[i]); } } return quickSort(l).concat(c, quickSort(r)); } console.log(quickSort(arr)); ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 懶加載 ~~~javascript <img id="img1" src="preview.png" data-realsrc = "abc.png"/> <script type = "text/javascript"> var img1 = document.getElementById("img1") img1.src = img1.getAttribute('data-realsrc') </script> ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 緩存`DOM`查詢 ~~~javascript // 未緩存 DOM查詢 var i for (i = 0; i < document.getElementByTagName('p').length; i++) { //todo } //緩存了DOM查詢 var pList = document.getElementByTagName('p') var i for (i= 0; i < pList.length; i++) { // todo } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 合并`DOM`插入 ~~~javascript var listNode = document.getElementById('list') //要插入10個li標簽 var frag = document.createDocumentFragment(); var x,li for (x = 0; x < 10; x++) { li = document.createElement('li') li.innerHTML = "List item" + x frag.appendChild(li) } listNode.appendChild(frag) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 事件節流 ~~~javascript var textarea = document.getElementById('text') var timeoutId textarea.addEventListener('keyup', function () { if (timeoutId) { clearTimeout(timeoutId) } timeoutId = setTimeout(function () { //觸發事件 },100) }) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 盡早操作 ~~~javascript window.addEventListener('load', function () { //頁面的全部資源加載完才會去執行,包括圖片,視頻 }) document.addEventListener('DOMContentLoaded', function() { //DOM 渲染完即可執行,此時圖片,視頻還可能沒有加載完成 }) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 淺拷貝 首先可以通過?`Object.assign`?來解決這個問題 ~~~ let a = { age: 1 } let b = Object.assign({}, a) a.age = 2 console.log(b.age) // 1 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 深拷貝 這個問題通常可以通過`JSON.parse(JSON.stringify(object))`?來解決 ~~~javascript let a = { age: 1, jobs: { first: 'FE' } } let b = JSON.parse(JSON.stringify(a)) a.jobs.first = 'native' console.log(b.jobs.first) // FE ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 數組降維 ~~~javascript [1, [2], 3].flatMap(v => v) // -> [1, 2, 3] ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") 如果想將一個多維數組徹底的降維,可以這樣實現 ~~~javascript const flattenDeep = (arr) => Array.isArray(arr) ? arr.reduce( (a, b) => [...a, ...flattenDeep(b)] , []) : [arr] flattenDeep([1, [[2], [3, [4]], 5]]) ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### 預加載 在開發中,可能會遇到這樣的情況。有些資源不需要馬上用到,但是希望盡早獲取,這時候就可以使用預加載 預加載其實是聲明式的?`fetch`,強制瀏覽器請求資源,并且不會阻塞?`onload`?事件,可以使用以下代碼開啟預加載 ~~~ <link rel="preload" href="http://example.com"> 復制代碼 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") 預加載可以一定程度上降低首屏的加載時間,因為可以將一些不影響首屏但重要的文件延后加載,唯一缺點就是兼容性不好 ### 預渲染 可以通過預渲染將下載的文件預先在后臺渲染,可以使用以下代碼開啟預渲染 ~~~ <link rel="prerender" href="http://poetries.com"> ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ## 性能優化 ### JavaScript性能優化 ~~~ 1、盡可能把 <script> 標簽放在 body 之后,避免 JS 的執行卡住 DOM 的渲染,最大程度保證頁面盡快地展示出來 2、盡可能合并 JS 代碼:提取公共方法,進行面向對象設計等…… 3、CSS 能做的事情,盡量不用 JS 來做,畢竟 JS 的解析執行比較粗暴,而 CSS 效率更高。 4、盡可能逐條操作 DOM,并預定好 CSs 樣式,從而減少 reflow 或者 repaint 的次數。 5、盡可能少地創建 DOM,而是在 HTML 和 CSS 中使用 display: none 來隱藏,按需顯示。 6、壓縮文件大小,減少資源下載負擔。 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動") ### JavaScript幾條基本規范 ~~~ 1、不要在同一行聲明多個變量 2、請使用===/!==來比較true/false或者數值 3、使用對象字面量替代new Array這種形式 4、不要使用全局變量 5、Switch語句必須帶有default分支 6、函數不應該有時候有返回值,有時候沒有返回值 7、For循環必須使用大括號 8、IF語句必須使用大括號 9、for-in循環中的變量 應該使用var關鍵字明確限定作用域,從而避免作用域污染 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "點擊并拖拽以移動")
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看