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