前端Js筆試題面試題

前言

爲了方便如今和之後前端學習和麪試,在此收集和整理了Js相關的筆試面試題,供本身查閱的同時,但願也會對你們有所幫助。javascript

目錄:

前端HTML+CSS筆試題面試題
前端JS筆試題面試題
前端Vue筆試題面試題
前端小程序筆試題面試題html

數據類型

JS的基本數據類型

Undefined、Null、Boolean、Number、String新增:Symbol前端

JS有哪些內置對象?

ObjectJavaScript 中全部對象的父對象
數據封裝類對象:Object、Array、Boolean、NumberString
其餘對象:Function、Arguments、Math、Date、RegExp、Errorjava

JS中使用typeof能獲得的哪些類型?(考點:JS變量類型)

typeof undefinded //undefined
typeof null // object
typeof'abc' // string
typeof 123 // number
typeof [] // object
typeof {} // object
typeof true //boolean
typeof b // b 沒有聲明,可是還會顯示 undefined
複製代碼

什麼時候使用===什麼時候使用==?(考點:強制類型轉換)

== 比較兩個值相等返回ture
=== 比較二者的值和類型都相等才返回true 嚴格相等
0,NAN,null,undefinded,'',falseif語句中會強制轉化爲 falsenode

if (obj.a == null) {
    // 這裏至關於 obj.a === null || obj.a ===undefinded, 簡寫形式
    // 這裏jquery 源碼中推薦的寫法
}
複製代碼

null,undefined 的區別?

一、nullNull類型,表明「空值」,表明一個空對象指針,使用typeof運算獲得 「object」,因此你能夠認爲它是一個特殊的對象值。
二、undefinedUndefined類型,當一個聲明瞭一個變量未初始化時,獲得的就是undefinedjquery

ps:一句話簡單來講nullundefine值比較是相等,但類型不一樣。nginx

Javascript建立對象的幾種方式?

一、對象字面量的方式面試

var = {}
複製代碼

二、經過構造函數方式建立。ajax

var obj = new Object(); 
複製代碼

三、經過Object.create()方式建立。算法

var obj = Object.create(Object.prototype);
複製代碼

翻轉一個字符串

能夠先將字符串轉成一個數組,而後用數組的reverse()+join()方法。

let str="hello word";
let b=[...str].reverse().join("");//drow olleh
複製代碼

描述new一個對象的過程

一、建立一個新對象
二、this指向這個對象
三、執行代碼,即對this賦值
四、隱式返回this

JS按存儲方式區分變量類型

// 值類型
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
複製代碼

如何判斷一個變量是對象仍是數組?

一、instanceof方法 instanceof運算符是用來測試一個對象是否在其原型鏈原型構造函數的屬性

var arr = []; 
arr instanceof Array; // true
複製代碼

二、constructor方法 constructor屬性返回對建立此對象的數組函數的引用,就是返回對象相對應的構造函數

var arr = []; 
arr.constructor == Array; //true
複製代碼

三、最簡單的方法,這種寫法兼容性最好使用Object.prototype.toString.call()

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不是數組也不是對象')
      }
}
複製代碼

四、ES5新增方法isArray()

var a = new Array(123);
var b = new Date();
console.log(Array.isArray(a)); //true
console.log(Array.isArray(b)); //false
複製代碼

ps:千萬不能使用typeof來判斷對象和數組,由於這兩種類型都會返回"object"

如何對一個數組去重?

一、Set結構去重(ES6用法)。
ES6提供了新的數據結構Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。

[...new Set(array)];
複製代碼

二、遍歷,將值添加到新數組,用indexOf()判斷值是否存在,已存在就不添加,達到去重效果。

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。是有問題的,下面那個方法好一點。
複製代碼

三、利用for嵌套for,而後splice去重(ES5中最經常使用)。

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直接消失了
複製代碼

四、forEach遍歷,而後利用Object.keys(對象)返回這個對象可枚舉屬性組成的數組,這個數組就是去重後的數組。

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"]
    
複製代碼

做用域和閉包

var、let、const之間的區別

var聲明變量能夠重複聲明,而let不能夠重複聲明
var是不受限於塊級的,而let是受限於塊級
var會與window相映射(會掛一個屬性),而let不與window相映射
var能夠在聲明的上面訪問變量,而let有暫存死區,在聲明的上面訪問變量會報錯
const聲明以後必須賦值,不然會報錯
const定義不可變的量,改變了就會報錯
constlet同樣不會與window相映射、支持塊級做用域、在聲明的上面訪問變量會報錯

說明This幾種不一樣的使用場景

一、做爲構造函數執行
二、做爲對象屬性執行
三、做爲普通函數執行
四、call apply bind

談談This對象的理解

一、this老是指向函數的直接調用者(而非間接調用者)
二、若是有new關鍵字,this指向new出來的那個對象
三、在事件中,this指向觸發這個事件的對象,特殊的是,IE中的attachEvent中的this老是指向全局對象Window

做用域

ES5做用域分爲 全局做用域 和 函數做用域。
ES6新增塊級做用域,塊做用域由 { }包括,if語句和 for語句裏面的{ }也屬於塊做用域。

// 塊級做用域
if (true) {
    var name = 'zhangsan'
}
console.log(name)
// 函數和全局做用域
var a = 100
function fn () {
    var a = 200
    console.log('fn', a)
}
複製代碼

JS建立10個<a>標籤,點擊的時候彈出來對應的序號?(考點:做用域)

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)
}
複製代碼

說說你對做用域鏈的理解

一、做用域鏈的做用是保證執行環境裏有權訪問的變量和函數是有序的,做用域鏈的變量只能向上訪問,變量訪問到window對象即被終止,做用域鏈向下訪問變量是不被容許的
二、簡單的說,做用域就是變量與函數的可訪問範圍,即做用域控制着變量與函數的可見性和生命週期
三、通俗來講,通常狀況下,變量取值到 建立 這個變量 的函數的做用域中取值。 可是若是在當前做用域中沒有查到值,就會向上級做用域去查,直到查到全局做用域,這麼一個查找過程造成的鏈條就叫作做用域鏈

var a = 100
function fn () {
    var b = 200
    // 當前做用域沒有定於的變量,即‘自由變量’
    console.log(a)
    console.log(b)
}
fn()
console.log(a) 去父級做用域找a自由變量 做用域鏈
複製代碼

閉包

閉包就是可以讀取其餘函數內部變量的函數
閉包是指有權訪問另外一個函數做用域中變量的函數,建立閉包的最多見的方式就是在一個函數內建立另外一個函數,經過另外一個函數訪問這個函數的局部變量,利用閉包能夠突破做用鏈域

function F1 () {
    var a = 100
    // 返回一個函數,函數做爲返回值
    return function () {
        console.log(a)
    }
}
// f1 獲得一個函數
var f1 = F1()
var a = 200
f1()  // 100 定義的時候父級做用域
複製代碼

閉包的特性

函數內再嵌套函數
內部函數能夠引用外層的參數和變量
參數和變量不會被垃圾回收機制回收

說說你對閉包的理解

使用閉包主要是爲了設計私有的方法和變量。閉包的優勢是能夠避免全局變量的污染,缺點是閉包會常駐內存,會增大內存使用量,使用不當很容易形成內存泄露。在js中,函數即閉包,只有函數纔會產生做用域的概念

閉包的最大用處有兩個,一個是能夠讀取函數內部的變量,另外一個就是讓這些變量始終保持在內存中
閉包的另外一個用處,是封裝對象的私有屬性和私有方法
好處:可以實現封裝和緩存等;
壞處:就是消耗內存、不正當使用會形成內存溢出的問題

使用閉包的注意點

因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露 解決方法是,在退出函數以前,將不使用的局部變量所有刪除

閉包的使用場景

閉包的兩個場景:函數做爲返回值 和 函數做爲參數傳遞

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)
複製代碼

實際開發中閉包的應用

// 閉包實際應用中主要用於封裝變量 收斂權限
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
複製代碼

原型和原型鏈

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) 中尋找

// 構造函數
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()
複製代碼

循環對象自身的屬性

var item
for (item in f) {
    // 高級瀏覽器已經在 for in 中屏蔽了來自原型的屬性
    // 可是這裏建議你們仍是加上這個判斷,保證程序的健壯性
    if (f.hasOwnProperty(item)) {
        console.log(item)
    }
}
複製代碼

原型鏈

f.toString() //到f._proto_._proto_ 中去找
複製代碼

異步和單線程

同步和異步的區別是什麼?分別舉一個同步和異步的例子

同步交互:指發送一個請求,須要等待返回,而後纔可以發送下一個請求,有個等待過程。
異步交互:指發送一個請求,不須要等待返回,隨時能夠再發送下一個請求,即不須要等待。
相同的地方:都屬於交互方式,都是發送請求。
不一樣的地方:一個須要等待,一個不須要等待。
簡單而言,同步就是必須一件一件的作事,等前一件事作完後才能作下一件事。而異步這是把事情指派給別人後,接着繼續作下一件事,沒必要等別人返回的結果。
舉例:
一、廣播,就是一個異步例子。發起者不關心接收者的狀態。不須要等待接收者的返回信息; 在部分狀況下,咱們的項目開發中都會優先選擇不須要等待的異步交互方式。
二、電話,就是一個同步例子。發起者須要等待接收者,接通電話後,通訊纔開始。須要等待接收者的返回信息 好比銀行的轉帳系統,對數據庫的保存操做等等,都會使用同步交互操做。
ps: alert是同步,setTimeoutsetInterval是異步,同步會阻塞代碼執行,而異步不會

異步編程的實現方式?

一、回調函數
優勢:簡單、容易理解
缺點:不利於維護,代碼耦合高

二、事件監聽(採用時間驅動模式,取決於某個事件是否發生):
優勢:容易理解,能夠綁定多個事件,每一個事件能夠指定多個回調函數
缺點:事件驅動型,流程不夠清晰

三、發佈/訂閱(觀察者模式)
相似於事件監聽,可是能夠經過‘消息中心’,瞭解如今有多少發佈者,多少訂閱者

四、Promise對象
優勢:能夠利用then方法,進行鏈式寫法;能夠書寫錯誤時的回調函數;
缺點:編寫和理解,相對比較難

五、Generator函數
優勢:函數體內外的數據交換、錯誤處理機制
缺點:流程管理不方便

六、async函數
優勢:內置執行器、更好的語義、更廣的適用性、返回的是Promise、結構清晰。
缺點:錯誤處理機制

定時器的執行順序或機制。

簡單來講:由於js是單線程的,瀏覽器遇到setTimeout或者setInterval會先執行完當前的代碼塊,在此以前會把定時器推入瀏覽器的待執行事件隊列裏面,等到瀏覽器執行完當前代碼以後會看一下事件隊列裏面有沒有任務,有的話才執行定時器的代碼。 因此即便把定時器的時間設置爲0仍是會先執行當前的一些代碼。

一個關於setTimeout的筆試題

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
複製代碼

前段使用異步的場景

一、定時任務:setTimeout,setInverval
二、網絡請求:ajax請求,動態<img>加載
三、事件綁定

數組和對象API

map與forEach的區別?

一、forEach方法,是最基本的方法,就是遍歷與循環,默認有3個傳參:分別是遍歷的數組內容item、數組索引index、和當前遍歷數組Array
二、map方法,基本用法與forEach一致,可是不一樣的,它會返回一個新的數組,因此在callback須要有return值,若是沒有,會返回undefined

JS 數組和對象的遍歷方式,以及幾種方式的比較

一般咱們會用循環的方式來遍歷數組。可是循環是 致使js 性能問題的緣由之一。通常咱們會採用下幾種方式來進行數組的遍歷
for in循環
for循環
forEach
一、這裏的 forEach回調中兩個參數分別爲 value,index
二、forEach 沒法遍歷對象
三、IE不支持該方法;Firefoxchrome 支持
四、forEach 沒法使用 break,continue 跳出循環,且使用 return 是跳過本次循環
五、能夠添加第二個參數,爲一個數組,回調中的this會指向這個數組,若沒有添加,則是指向window
在方式一中for-in須要分析出array的每一個屬性,這個操做性能開銷很大。用在 key 已知的數組上是很是不划算的。因此儘可能不要用for-in,除非你不清楚要處理哪些屬性,例如JSON對象這樣的狀況
在方式2中,循環每進行一次,就要檢查一下數組長度。讀取屬性(數組長度)要比讀局部變量慢,尤爲是當array裏存放的都是 DOM元素,由於每次讀取都會掃描一遍頁面上的選擇器相關元素,速度會大大下降

寫一個能遍歷對象和數組的通用forEach函數

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])
        }
    }
}
複製代碼

數組API

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 爲當前值(從第二項開始)

複製代碼

對象API

var obj = {
    x: 100,
    y: 200,
    z: 300
} 
var key
for (key in obj) {
    // 注意這裏的 hasOwnProperty,再講原型鏈的時候講過
    if (obj.hasOwnProperty(key)) {
        console.log(key, obj[key])
    }
}
複製代碼

日期和隨機數

獲取2020-06-10格式的日期

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)
複製代碼

ps: ES6 新增padStart(),padEnd()方法可用來返回06格式日期

獲取隨機數,要求是長度一致的字符串格式

var random = Math.random()
var random = random + '0000000000'
console.log(random)
var random = random.slice(0, 10)
console.log(random)
複製代碼

DOMBOM操做

DOM是哪一種基本的數據結構

DOM是一種樹形結構的數據結構

DOM操做的經常使用API有哪些

一、獲取DOM節點,以及節點的propertyAttribute
二、獲取父節點,獲取子節點
三、新增節點,刪除節點

DOM節點的Attributeproperty有何區別

property只是一個JS對象的屬性修改和獲取
Attribute是對html標籤屬性的修改和獲取

DOM的節點操做

建立新節點

createDocumentFragment()    //建立一個DOM片斷
createElement()   //建立一個具體的元素
createTextNode()   //建立一個文本節點
複製代碼

添加、移除、替換、插入

appendChild()      //添加
removeChild()      //移除
replaceChild()      //替換
insertBefore()      //插入
複製代碼

查找

getElementsByTagName()    //經過標籤名稱
getElementsByName()     //經過元素的Name屬性的值
getElementById()        //經過元素Id,惟一性
複製代碼

如何檢測瀏覽器的類型
能夠經過檢測navigator.userAgent 在經過不通瀏覽器的不通來檢測

var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)
複製代碼

拆解url的各部分
使用location裏面的location.href location.protocol location.pathname location.search location.hash來獲取各類參數

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()
複製代碼

事件機制

請解釋什麼是事件代理

事件代理(Event Delegation),又稱之爲事件委託。是 JavaScript 中經常使用綁定事件的經常使用技巧。顧名思義,「事件代理」便是把本來須要綁定的事件委託給父元素,讓父元素擔當事件監聽的職務。事件代理的原理是DOM元素的事件冒泡。

使用事件代理的好處是:

一、能夠提升性能
二、能夠大量節省內存佔用,減小事件註冊,好比在table上代理全部tdclick事件就很是棒
三、能夠實現當新增子對象時無需再次對其綁定

事件模型

W3C中定義事件的發生經歷三個階段:捕獲階段(capturing)、目標階段(targetin)、冒泡階段(bubbling)

冒泡型事件:當你使用事件冒泡時,子級元素先觸發,父級元素後觸發
捕獲型事件:當你使用事件捕獲時,父級元素先觸發,子級元素後觸發
DOM事件流:<時支持兩種事件模型:捕獲型事件和冒泡型事件
阻止冒泡:W3c中,使用stopPropagation()方法;在IE下設置cancelBubble = true
阻止捕獲:阻止事件的默認行爲,例如click - <a>後的跳轉。在W3c中,使用preventDefault()方法,在IE下設置window.event.returnValue = false

編寫一個通用的事件監聽函數

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)
        }
    })
}
複製代碼

描述事件冒泡流程

當給某元素綁定一個事件的時候,首先會觸發本身綁定的,而後會逐層向上級查找事件,這就是事件冒泡

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("取消")
            })
複製代碼

對於一個無限下拉加載圖片的頁面,如何給每一個圖片綁定事件

可使用代理,經過對父級元素綁定一個事件,經過判斷事件的target屬性來進行判斷,添加行爲

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)
        })
複製代碼

AJAX

Ajax原理

Ajax的原理簡單來講是在用戶和服務器之間加了—箇中間層(AJAX引擎),經過XmlHttpRequest對象來向服務器發異步請求,從服務器得到數據,而後用javascript來操做DOM而更新頁面。使用戶操做與服務器響應異步化。這其中最關鍵的一步就是從服務器得到請求數據
Ajax的過程只涉及JavaScript、XMLHttpRequest和DOMXMLHttpRequest是ajax的核心機制

手動編寫一個ajax,不依賴第三方庫

// 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);
            }
        }
    }
複製代碼

ajax的優勢和缺點

ajax的優勢

一、無刷新更新數據(在不刷新整個頁面的狀況下維持與服務器通訊)
二、異步與服務器通訊(使用異步的方式與服務器通訊,不打斷用戶的操做)
三、前端和後端負載均衡(將一些後端的工做交給前端,減小服務器與寬度的負擔)
四、界面和應用相分離(ajax將界面和應用分離也就是數據與呈現相分離)

ajax的缺點

一、ajax不支持瀏覽器back按鈕
二、安全問題 Aajax暴露了與服務器交互的細節
三、對搜索引擎的支持比較弱
四、破壞了BackHistory後退按鈕的正常行爲等瀏覽器機制。

什麼狀況下會碰到跨域問題?有哪些解決方法?

跨域問題是這是瀏覽器爲了安全實施的同源策略致使的,同源策略限制了來自不一樣源的document、腳本,同源的意思就是兩個URL的域名、協議、端口要徹底相同。
script標籤jsonp跨域、nginx反向代理、node.js中間件代理跨域、後端在頭部信息設置安全域名、後端在服務器上設置cors
ps:有三個標籤容許跨域加載資源: <img src=xxx> <link href = xxx> <script src=xxx>
三個標籤場景:
<img> 用於打點統計,統計網站多是其餘域
<link> <script> 可使用CDNCDN的也是其餘域
<script>能夠用於JSONP

⚠️跨域注意事項

全部的跨域請求都必須通過信息提供方容許
若是未經容許便可獲取,那麼瀏覽器同源策略出現漏洞

XMLJSON的區別?

數據體積方面
JSON相對於XML來說,數據的體積小,傳遞的速度更快些。

數據交互方面
JSONJavaScript的交互更加方便,更容易解析處理,更好的數據交互

數據描述方面
JSON對數據的描述性比XML較差

傳輸速度方面
JSON的速度要遠遠快於XML

getpost的區別

一、getpostHTTP中都表明着請求數據,其中get請求相對來講更簡單、快速,效率高些
二、get相對post安全性低
三、get有緩存,post沒有
四、get體積小,post能夠無限大
五、geturl參數可見,post不可見
六、get只接受ASCII字符的參數數據類型,post沒有限制
七、get請求參數會保留歷史記錄,post中參數不會保留
八、get會被瀏覽器主動catch,post不會,須要手動設置
九、get在瀏覽器回退時無害,post會再次提交請求

何時使用post

post通常用於修改服務器上的資源,對所發送的信息沒有限制。好比
一、沒法使用緩存文件(更新服務器上的文件或數據庫)
二、向服務器發送大量數據(POST 沒有數據量限制)
三、發送包含未知字符的用戶輸入時,POSTGET 更穩定也更可靠

TCP創建鏈接爲何須要三次?

  • 爲了實現可靠數據傳輸, TCP 協議的通訊雙方, 都必須維護一個序列號, 以標識發送出去的數據包中, 哪些是已經被對方收到的。 三次握手的過程便是通訊雙方相互告知序列號起始值, 並確認對方已經收到了序列號起始值的必經步驟
  • 若是隻是兩次握手, 至多隻有鏈接發起方的起始序列號能被確認, 另外一方選擇的序列號則得不到確認

ES6

ES5的繼承和ES6的繼承有什麼區別?

ES5的繼承時經過prototype或構造函數機制來實現。ES5的繼承實質上是先建立子類的實例對象,而後再將父類的方法添加到thisParent.apply(this))。

ES6的繼承機制徹底不一樣,實質上是先建立父類的實例對象this(因此必須先調用父類的super()方法),而後再用子類的構造函數修改this

具體的:ES6經過class關鍵字定義類,裏面有構造方法,類之間經過extends關鍵字實現繼承。子類必須在constructor方法中調用super方法,不然新建實例報錯。由於子類沒有本身的this對象,而是繼承了父類的this對象,而後對其進行加工。若是不調用super方法,子類得不到this對象。

ps:super關鍵字指代父類的實例,即父類的this對象。在子類構造函數中,調用super後,纔可以使用this關鍵字,不然報錯

javascript如何實現繼承?

一、構造繼承
二、原型繼承
三、實例繼承
四、拷貝繼承
原型prototype機制或apply和call方法去實現較簡單,建議使用構造函數與原型混合方式

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);//獲得被繼承的屬性
  }
複製代碼

談談你對ES6的理解

新增模板字符串(爲JavaScript提供了簡單的字符串插值功能)
箭頭函數
for-of(用來遍歷數據—例如數組中的值。)
arguments對象可被不定參數和默認參數完美代替。
ES6promise對象歸入規範,提供了原生的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是一個構造函數,下面是一個簡單實例

var promise = new Promise((resolve,reject) => {
    if (操做成功) {
        resolve(value)
    } else {
        reject(error)
    }
})
promise.then(function (value) {
    // success
},function (value) {
    // failure
})
複製代碼

請描述一下Promise的使用場景,'Promise'它所解決的問題以及如今對於異步操做的解決方案。

Promise的使用場景:ajax請求,回調函數,複雜操做判斷。
PromiseES6爲了解決異步編程所誕生的。
異步操做解決方案:Promise、Generator、定時器(不知道算不算)、還有ES7async

ECMAScript6 怎麼寫class,爲何會出現class這種東西?

這個語法糖可讓有OOP基礎的人更快上手js,至少是一個官方的實現了 但對熟悉js的人來講,這個東西沒啥大影響;一個Object.creat()搞定繼承,比class簡潔清晰的多

算法和其餘

冒泡排序

每次比較相鄰的兩個數,若是後一個比前一個小,換位置

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));
複製代碼

快速排序

採用二分法,取出中間數,數組每次和中間數比較,小的放到左邊,大的放到右邊

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));
複製代碼

懶加載

<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>
複製代碼

緩存DOM查詢

// 未緩存 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
}
複製代碼

合併DOM插入

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)
複製代碼

事件節流

var textarea = document.getElementById('text')
var timeoutId
textarea.addEventListener('keyup', function () {
    if (timeoutId) {
        clearTimeout(timeoutId)
    }
    timeoutId = setTimeout(function () {
        //觸發事件    
    },100)
})
複製代碼

儘早操做

window.addEventListener('load', function () {
    //頁面的所有資源加載完纔會去執行,包括圖片,視頻
})
document.addEventListener('DOMContentLoaded', function() {
    //DOM 渲染完便可執行,此時圖片,視頻還可能沒有加載完成
})
複製代碼

淺拷貝

首先能夠經過 Object.assign 來解決這個問題

let a = {
    age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
複製代碼

深拷貝

這個問題一般能夠經過JSON.parse(JSON.stringify(object)) 來解決

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

數組降維

[1, [2], 3].flatMap(v => v)
// -> [1, 2, 3]
複製代碼

若是想將一個多維數組完全的降維,能夠這樣實現

const flattenDeep = (arr) => Array.isArray(arr)
  ? arr.reduce( (a, b) => [...a, ...flattenDeep(b)] , [])
  : [arr]

flattenDeep([1, [[2], [3, [4]], 5]])
複製代碼

預加載

在開發中,可能會遇到這樣的狀況。有些資源不須要立刻用到,可是但願儘早獲取,這時候就可使用預加載 預加載實際上是聲明式的 fetch,強制瀏覽器請求資源,而且不會阻塞 onload 事件,可使用如下代碼開啓預加載

<link rel="preload" href="http://example.com">
複製代碼

預加載能夠必定程度上下降首屏的加載時間,由於能夠將一些不影響首屏但重要的文件延後加載,惟一缺點就是兼容性很差

預渲染

能夠經過預渲染將下載的文件預先在後臺渲染,可使用如下代碼開啓預渲染

<link rel="prerender" href="http://poetries.com">
複製代碼

性能優化

JavaScript性能優化

一、儘量把 <script> 標籤放在 body 以後,避免 JS 的執行卡住 DOM 的渲染,最大程度保證頁面儘快地展現出來
二、儘量合併 JS 代碼:提取公共方法,進行面向對象設計等……
三、CSS 能作的事情,儘可能不用 JS 來作,畢竟 JS 的解析執行比較粗暴,而 CSS 效率更高。
四、儘量逐條操做 DOM,並預約好 CSs 樣式,從而減小 reflow 或者 repaint 的次數。
五、儘量少地建立 DOM,而是在 HTML 和 CSS 中使用 display: none 來隱藏,按需顯示。
六、壓縮文件大小,減小資源下載負擔。
複製代碼

JavaScript幾條基本規範

一、不要在同一行聲明多個變量
二、請使用===/!==來比較true/false或者數值
三、使用對象字面量替代new Array這種形式
四、不要使用全局變量
五、Switch語句必須帶有default分支
六、函數不該該有時候有返回值,有時候沒有返回值
七、For循環必須使用大括號
八、IF語句必須使用大括號
九、for-in循環中的變量 應該使用var關鍵字明確限定做用域,從而避免做用域污染
複製代碼
相關文章
相關標籤/搜索