1. get請求傳參長度的誤區(qū)
誤區(qū):我們經(jīng)常說get請求參數(shù)的大小存在限制,而post請求的參數(shù)大小是無限制的顿锰。
實際上HTTP 協(xié)議從未規(guī)定 GET/POST 的請求長度限制是多少器赞。對get請求參數(shù)的限制是來源與瀏覽器或web服務(wù)器垢袱,瀏覽器或web服務(wù)器限制了url的長度。為了明確這個概念港柜,我們必須再次強(qiáng)調(diào)下面幾點:
- HTTP 協(xié)議 未規(guī)定 GET 和POST的長度限制
- GET的最大長度顯示是因為 瀏覽器和 web服務(wù)器限制了 URI的長度
- 不同的瀏覽器和WEB服務(wù)器请契,限制的最大長度不一樣
- 要支持IE,則最大長度為2083byte潘懊,若只支持Chrome姚糊,則最大長度 8182byte
2. 補(bǔ)充get和post請求在緩存方面的區(qū)別
post/get的請求區(qū)別,具體不再贅述授舟。
補(bǔ)充補(bǔ)充一個get和post在緩存方面的區(qū)別:
- get請求類似于查找的過程救恨,用戶獲取數(shù)據(jù),可以不用每次都與數(shù)據(jù)庫連接释树,所以可以使用緩存肠槽。
- post不同,post做的一般是修改和刪除的工作奢啥,所以必須與數(shù)據(jù)庫交互秸仙,所以不能使用緩存。因此get請求適合于請求緩存桩盲。
3. 閉包
一句話可以概括:閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)寂纪,或者子函數(shù)在外調(diào)用,子函數(shù)所在的父函數(shù)的作用域不會被釋放赌结。
4. 類的創(chuàng)建和繼承
(1)類的創(chuàng)建(es5):new一個function捞蛋,在這個function的prototype里面增加屬性和方法。
下面來創(chuàng)建一個Animal類:
// 定義一個動物類
function Animal (name) {
// 屬性
this.name = name || 'Animal';
// 實例方法
this.sleep = function(){
console.log(this.name + '正在睡覺柬姚!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
這樣就生成了一個Animal類拟杉,實力化生成對象后,有方法和屬性量承。
(2)類的繼承——原型鏈繼承
--原型鏈繼承
function Cat(){ }
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
- 介紹:在這里我們可以看到new了一個空對象,這個空對象指向Animal并且Cat.prototype指向了這個空對象搬设,這種就是基于原型鏈的繼承穴店。
- 特點:基于原型鏈,既是父類的實例拿穴,也是子類的實例
- 缺點:無法實現(xiàn)多繼承
(3)構(gòu)造繼承:使用父類的構(gòu)造函數(shù)來增強(qiáng)子類實例泣洞,等于是復(fù)制父類的實例屬性給子類(沒用到原型)
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
- 特點:可以實現(xiàn)多繼承
- 缺點:只能繼承父類實例的屬性和方法,不能繼承原型上的屬性和方法贞言。
(4)實例繼承和拷貝繼承
實例繼承:為父類實例添加新特性斜棚,作為子類實例返回
拷貝繼承:拷貝父類元素上的屬性和方法
上述兩個實用性不強(qiáng),不一一舉例该窗。
(5)組合繼承:相當(dāng)于構(gòu)造繼承和原型鏈繼承的組合體。通過調(diào)用父類構(gòu)造蚤霞,繼承父類的屬性并保留傳參的優(yōu)點酗失,然后通過將父類實例作為子類原型,實現(xiàn)函數(shù)復(fù)用
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
- 特點:可以繼承實例屬性/方法昧绣,也可以繼承原型屬性/方法
- 缺點:調(diào)用了兩次父類構(gòu)造函數(shù)规肴,生成了兩份實例
(6)寄生組合繼承:通過寄生方式,砍掉父類的實例屬性夜畴,這樣拖刃,在調(diào)用兩次父類的構(gòu)造的時候,就不會初始化兩次實例方法/屬性
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 創(chuàng)建一個沒有實例方法的類
var Super = function(){};
Super.prototype = Animal.prototype;
//將實例作為子類的原型
Cat.prototype = new Super();
})();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
- 較為推薦
5. 如何解決異步回調(diào)地獄
promise贪绘、generator兑牡、async/await
6. 說說前端中的事件流
HTML中與javascript交互是通過事件驅(qū)動來實現(xiàn)的,例如鼠標(biāo)點擊事件onclick税灌、頁面的滾動事件onscroll等等均函,可以向文檔或者文檔中的元素添加事件偵聽器來預(yù)訂事件。想要知道這些事件是在什么時候進(jìn)行調(diào)用的菱涤,就需要了解一下“事件流”的概念苞也。
什么是事件流:事件流描述的是從頁面中接收事件的順序,DOM2級事件流包括下面幾個階段。
- 事件捕獲階段
- 處于目標(biāo)階段
- 事件冒泡階段
addEventListener:addEventListener 是DOM2 級事件新增的指定事件處理程序的操作粘秆,這個方法接收3個參數(shù):要處理的事件名如迟、作為事件處理程序的函數(shù)和一個布爾值。最后這個布爾值參數(shù)如果是true攻走,表示在捕獲階段調(diào)用事件處理程序殷勘;如果是false,表示在冒泡階段調(diào)用事件處理程序陋气。
IE只支持事件冒泡劳吠。
7. 如何讓事件先冒泡后捕獲
在DOM標(biāo)準(zhǔn)事件模型中,是先捕獲后冒泡巩趁。但是如果要實現(xiàn)先冒泡后捕獲的效果痒玩,對于同一個事件淳附,監(jiān)聽捕獲和冒泡,分別對應(yīng)相應(yīng)的處理函數(shù)蠢古,監(jiān)聽到捕獲事件奴曙,先暫緩執(zhí)行,直到冒泡事件被捕獲后再執(zhí)行捕獲之間草讶。
8. 事件委托
- 簡介:事件委托指的是洽糟,不在事件的發(fā)生地(直接dom)上設(shè)置監(jiān)聽函數(shù),而是在其父元素上設(shè)置監(jiān)聽函數(shù)堕战,通過事件冒泡坤溃,父元素可以監(jiān)聽到子元素上事件的觸發(fā),通過判斷事件發(fā)生元素DOM的類型嘱丢,來做出不同的響應(yīng)薪介。
- 舉例:最經(jīng)典的就是ul和li標(biāo)簽的事件監(jiān)聽,比如我們在添加事件時候越驻,采用事件委托機(jī)制汁政,不會在li標(biāo)簽上直接添加,而是在ul父元素上添加缀旁。
- 好處:比較合適動態(tài)元素的綁定记劈,新添加的子元素也會有監(jiān)聽函數(shù),也可以有事件觸發(fā)機(jī)制并巍。
9. 圖片的懶加載和預(yù)加載
- 預(yù)加載:提前加載圖片目木,當(dāng)用戶需要查看時可直接從本地緩存中渲染。
- 懶加載:懶加載的主要目的是作為服務(wù)器前端的優(yōu)化履澳,減少請求數(shù)或延遲請求數(shù)嘶窄。
兩種技術(shù)的本質(zhì):兩者的行為是相反的,一個是提前加載距贷,一個是遲緩甚至不加載柄冲。
懶加載對服務(wù)器前端有一定的緩解壓力作用,預(yù)加載則會增加服務(wù)器前端壓力忠蝗。
10. mouseover和mouseenter的區(qū)別
- mouseover:當(dāng)鼠標(biāo)移入元素或其子元素都會觸發(fā)事件现横,所以有一個重復(fù)觸發(fā),冒泡的過程阁最。對應(yīng)的移除事件是mouseout
- mouseenter:當(dāng)鼠標(biāo)移除元素本身(不包含元素的子元素)會觸發(fā)事件戒祠,也就是不會冒泡,對應(yīng)的移除事件是mouseleave
11. js的new操作符做了哪些事情
new 操作符新建了一個空對象速种,這個對象原型指向構(gòu)造函數(shù)的prototype姜盈,執(zhí)行構(gòu)造函數(shù)后返回這個對象。
12.改變函數(shù)內(nèi)部this指針的指向函數(shù)(bind配阵,apply馏颂,call的區(qū)別)
- 通過apply和call改變函數(shù)的this指向示血,他們兩個函數(shù)的第一個參數(shù)都是一樣的表示要改變指向的那個對象,第二個參數(shù)救拉,apply是數(shù)組难审,而call則是arg1,arg2...這種形式。
- 通過bind改變this作用域會返回一個新的函數(shù)亿絮,這個函數(shù)不會馬上執(zhí)行告喊。
13. js的各種位置,比如clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop的區(qū)別派昧?
- clientHeight:表示的是可視區(qū)域的高度黔姜,不包含border和滾動條
- offsetHeight:表示可視區(qū)域的高度,包含了border和滾動條
- scrollHeight:表示了所有區(qū)域的高度蒂萎,包含了因為滾動被隱藏的部分地淀。
- clientTop:表示邊框border的厚度,在未指定的情況下一般為0
- scrollTop:滾動后被隱藏的高度岖是,獲取對象相對于由offsetParent屬性指定的父坐標(biāo)(css定位的元素或body元素)距離頂端的高度。
14. js拖拽功能的實現(xiàn)
- 首先是三個事件实苞,分別是mousedown豺撑,mousemove,mouseup
當(dāng)鼠標(biāo)點擊按下的時候黔牵,需要一個tag標(biāo)識此時已經(jīng)按下聪轿,可以執(zhí)行mousemove里面的具體方法。
-
clientX猾浦,clientY標(biāo)識的是鼠標(biāo)的坐標(biāo)陆错,分別標(biāo)識橫坐標(biāo)和縱坐標(biāo),并且我們用offsetX和offsetY來表示元素的元素的初始坐標(biāo)金赦,移動的舉例應(yīng)該是:
鼠標(biāo)移動時候的坐標(biāo)-鼠標(biāo)按下去時候的坐標(biāo)音瓷。
也就是說定位信息為:
鼠標(biāo)移動時候的坐標(biāo)-鼠標(biāo)按下去時候的坐標(biāo)+元素初始情況下的offetLeft.
還有一點也是原理性的東西,也就是拖拽的同時是絕對定位夹抗,我們改變的是絕對定位條件下的left
以及top等等值绳慎。
補(bǔ)充:也可以通過html5的拖放(Drag 和 drop)來實現(xiàn)
二、進(jìn)階javascript篇
1.自己實現(xiàn)一個bind函數(shù)
原理:通過apply或者call方法來實現(xiàn)漠烧。
(1)初始版本
Function.prototype.bind=function(obj,arg){
var arg=Array.prototype.slice.call(arguments,1);
var context=this;
return function(newArg){
arg=arg.concat(Array.prototype.slice.call(newArg));
return context.apply(obj,arg);
}
}
(2) 考慮到原型鏈
為什么要考慮杏愤?因為在new 一個bind過生成的新函數(shù)的時候,必須的條件是要繼承原函數(shù)的原型
Function.prototype.bind=function(obj,arg){
var arg=Array.prototype.slice.call(arguments,1);
var context=this;
var bound=function(newArg){
arg=arg.concat(Array.prototype.slice.call(newArg));
return context.apply(obj,arg);
}
var F=function(){}
//這里需要一個寄生組合繼承
F.prototype=context.prototype;
bound.prototype=new F();
return bound;
}
2.用setTimeout來實現(xiàn)setInterval
(1)用setTimeout()方法來模擬setInterval()與setInterval()之間的什么區(qū)別已脓?
首先來看setInterval的缺陷珊楼,使用setInterval()創(chuàng)建的定時器確保了定時器代碼規(guī)則地插入隊列中。這個問題在于:如果定時器代碼在代碼再次添加到隊列之前還沒完成執(zhí)行度液,結(jié)果就會導(dǎo)致定時器代碼連續(xù)運行好幾次厕宗。而之間沒有間隔画舌。不過幸運的是:javascript引擎足夠聰明,能夠避免這個問題媳瞪。當(dāng)且僅當(dāng)沒有該定時器的如何代碼實例時骗炉,才會將定時器代碼添加到隊列中。這確保了定時器代碼加入隊列中最小的時間間隔為指定時間蛇受。
這種重復(fù)定時器的規(guī)則有兩個問題:1.某些間隔會被跳過 2.多個定時器的代碼執(zhí)行時間可能會比預(yù)期小句葵。
下面舉例子說明:
假設(shè),某個onclick事件處理程序使用啦setInterval()來設(shè)置了一個200ms的重復(fù)定時器兢仰。如果事件處理程序花了300ms多一點的時間完成乍丈。
<img width="626" alt="2018-07-10 11 36 43" src="https://user-gold-cdn.xitu.io...;h=498&f=png&s=326047">
這個例子中的第一個定時器是在205ms處添加到隊列中,但是要過300ms才能執(zhí)行把将。在405ms又添加了一個副本轻专。在一個間隔,605ms處察蹲,第一個定時器代碼還在執(zhí)行中请垛,而且隊列中已經(jīng)有了一個定時器實例,結(jié)果是605ms的定時器代碼不會添加到隊列中洽议。結(jié)果是在5ms處添加的定時器代碼執(zhí)行結(jié)束后宗收,405處的代碼立即執(zhí)行。
function say(){
//something
setTimeout(say,200);
}
setTimeout(say,200)
或者
setTimeout(function(){
//do something
setTimeout(arguments.callee,200);
},200);
3.js怎么控制一次加載一張圖片亚兄,加載完后再加載下一張
(1)方法1
<script type="text/javascript">
var obj=new Image();
obj.src="http://www.phpernote.com/uploadfiles/editor/201107240502201179.jpg";
obj.onload=function(){
alert('圖片的寬度為:'+obj.width+'混稽;圖片的高度為:'+obj.height);
document.getElementById("mypic").innnerHTML="<img src='"+this.src+"' />";
}
</script>
<div id="mypic">onloading……</div>
(2)方法2
<script type="text/javascript">
var obj=new Image();
obj.src="http://www.phpernote.com/uploadfiles/editor/201107240502201179.jpg";
obj.onreadystatechange=function(){
if(this.readyState=="complete"){
alert('圖片的寬度為:'+obj.width+';圖片的高度為:'+obj.height);
document.getElementById("mypic").innnerHTML="<img src='"+this.src+"' />";
}
}
</script>
<div id="mypic">onloading……</div>
3.代碼的執(zhí)行順序
setTimeout(function(){console.log(1)},0);
new Promise(function(resolve,reject){
console.log(2);
resolve();
}).then(function(){console.log(3)
}).then(function(){console.log(4)});
process.nextTick(function(){console.log(5)});
console.log(6);
//輸出2,6,5,3,4,1
為什么呢审胚?具體請參考我的文章:
從promise匈勋、process.nextTick、setTimeout出發(fā)膳叨,談?wù)凟vent Loop中的Job queue
4.如何實現(xiàn)sleep的效果(es5或者es6)
(1)while循環(huán)的方式
function sleep(ms){
var start=Date.now(),expire=start+ms;
while(Date.now()<expire);
console.log('1111');
return;
}
執(zhí)行sleep(1000)之后洽洁,休眠了1000ms之后輸出了1111。上述循環(huán)的方式缺點很明顯懒鉴,容易造成死循環(huán)诡挂。
(2)通過promise來實現(xiàn)
function sleep(ms){
var temple=new Promise(
(resolve)=>{
console.log(111);setTimeout(resolve,ms)
});
return temple
}
sleep(500).then(function(){
//console.log(222)
})
//先輸出了111,延遲500ms后輸出222
(3)通過async封裝
function sleep(ms){
return new Promise((resolve)=>setTimeout(resolve,ms));
}
async function test(){
var temple=await sleep(1000);
console.log(1111)
return temple
}
test();
//延遲1000ms輸出了1111
(4).通過generate來實現(xiàn)
function* sleep(ms){
yield new Promise(function(resolve,reject){
console.log(111);
setTimeout(resolve,ms);
})
}
sleep(500).next().value.then(function(){console.log(2222)})
5.簡單的實現(xiàn)一個promise
首先明確什么是promiseA+規(guī)范,參考規(guī)范的地址:
如何實現(xiàn)一個promise,參考我的文章:
實現(xiàn)一個完美符合Promise/A+規(guī)范的Promise
一般不會問的很詳細(xì)仅讽,只要能寫出上述文章中的v1.0版本的簡單promise即可挣郭。
6.Function.proto(getPrototypeOf)是什么?
獲取一個對象的原型,在chrome中可以通過proto的形式,或者在ES6中可以通過Object.getPrototypeOf的形式稿黄。
那么Function.proto是什么么唱星?也就是說Function由什么對象繼承而來雳旅,我們來做如下判別。
Function.__proto__==Object.prototype //false
Function.__proto__==Function.prototype//true
我們發(fā)現(xiàn)Function的原型也是Function间聊。
我們用圖可以來明確這個關(guān)系:
<img width="646" alt="2018-07-10 2 38 27" src="https://user-gold-cdn.xitu.io...;h=1028&f=png&s=183106">
7.實現(xiàn)js中所有對象的深度克略苡(包裝對象,Date對象哎榴,正則對象)
通過遞歸可以簡單實現(xiàn)對象的深度克隆型豁,但是這種方法不管是ES6還是ES5實現(xiàn),都有同樣的缺陷尚蝌,就是只能實現(xiàn)特定的object的深度復(fù)制(比如數(shù)組和函數(shù))迎变,不能實現(xiàn)包裝對象Number,String 飘言, Boolean衣形,以及Date對象,RegExp對象的復(fù)制姿鸿。
(1)前文的方法
function deepClone(obj){
var newObj= obj instanceof Array?[]:{};
for(var i in obj){
newObj[i]=typeof obj[i]=='object'?
deepClone(obj[i]):obj[i];
}
return newObj;
}
這種方法可以實現(xiàn)一般對象和數(shù)組對象的克隆谆吴,比如:
var arr=[1,2,3];
var newArr=deepClone(arr);
// newArr->[1,2,3]
var obj={
x:1,
y:2
}
var newObj=deepClone(obj);
// newObj={x:1,y:2}
但是不能實現(xiàn)例如包裝對象Number,String,Boolean,以及正則對象RegExp和Date對象的克隆,比如:
//Number包裝對象
var num=new Number(1);
typeof num // "object"
var newNum=deepClone(num);
//newNum -> {} 空對象
//String包裝對象
var str=new String("hello");
typeof str //"object"
var newStr=deepClone(str);
//newStr-> {0:'h',1:'e',2:'l',3:'l',4:'o'};
//Boolean包裝對象
var bol=new Boolean(true);
typeof bol //"object"
var newBol=deepClone(bol);
// newBol ->{} 空對象
....
(2)valueof()函數(shù)
所有對象都有valueOf方法苛预,valueOf方法對于:如果存在任意原始值纪铺,它就默認(rèn)將對象轉(zhuǎn)換為表示它的原始值。對象是復(fù)合值碟渺,而且大多數(shù)對象無法真正表示為一個原始值,因此默認(rèn)的valueOf()方法簡單地返回對象本身突诬,而不是返回一個原始值苫拍。數(shù)組、函數(shù)和正則表達(dá)式簡單地繼承了這個默認(rèn)方法旺隙,調(diào)用這些類型的實例的valueOf()方法只是簡單返回這個對象本身绒极。
對于原始值或者包裝類:
function baseClone(base){
return base.valueOf();
}
//Number
var num=new Number(1);
var newNum=baseClone(num);
//newNum->1
//String
var str=new String('hello');
var newStr=baseClone(str);
// newStr->"hello"
//Boolean
var bol=new Boolean(true);
var newBol=baseClone(bol);
//newBol-> true
其實對于包裝類,完全可以用=號來進(jìn)行克隆蔬捷,其實沒有深度克隆一說垄提,
這里用valueOf實現(xiàn),語法上比較符合規(guī)范周拐。
對于Date類型:
因為valueOf方法铡俐,日期類定義的valueOf()方法會返回它的一個內(nèi)部表示:1970年1月1日以來的毫秒數(shù).因此我們可以在Date的原型上定義克隆的方法:
Date.prototype.clone=function(){
return new Date(this.valueOf());
}
var date=new Date('2010');
var newDate=date.clone();
// newDate-> Fri Jan 01 2010 08:00:00 GMT+0800
對于正則對象RegExp:
RegExp.prototype.clone = function() {
var pattern = this.valueOf();
var flags = '';
flags += pattern.global ? 'g' : '';
flags += pattern.ignoreCase ? 'i' : '';
flags += pattern.multiline ? 'm' : '';
return new RegExp(pattern.source, flags);
};
var reg=new RegExp('/111/');
var newReg=reg.clone();
//newReg-> /\/111\//
8.簡單實現(xiàn)Node的Events模塊
簡介:觀察者模式或者說訂閱模式,它定義了對象間的一種一對多的關(guān)系妥粟,讓多個觀察者對象同時監(jiān)聽某一個主題對象审丘,當(dāng)一個對象發(fā)生改變時,所有依賴于它的對象都將得到通知勾给。
node中的Events模塊就是通過觀察者模式來實現(xiàn)的:
var events=require('events');
var eventEmitter=new events.EventEmitter();
eventEmitter.on('say',function(name){
console.log('Hello',name);
})
eventEmitter.emit('say','Jony yu');
這樣滩报,eventEmitter發(fā)出say事件锅知,通過On接收,并且輸出結(jié)果脓钾,這就是一個訂閱模式的實現(xiàn)售睹,下面我們來簡單的實現(xiàn)一個Events模塊的EventEmitter。
(1)實現(xiàn)簡單的Event模塊的emit和on方法
function Events(){
this.on=function(eventName,callBack){
if(!this.handles){
this.handles={};
}
if(!this.handles[eventName]){
this.handles[eventName]=[];
}
this.handles[eventName].push(callBack);
}
this.emit=function(eventName,obj){
if(this.handles[eventName]){
for(var i=0;o<this.handles[eventName].length;i++){
this.handles[eventName][i](obj);
}
}
}
return this;
}
這樣我們就定義了Events可训,現(xiàn)在我們可以開始來調(diào)用:
var events=new Events();
events.on('say',function(name){
console.log('Hello',nama)
});
events.emit('say','Jony yu');
//結(jié)果就是通過emit調(diào)用之后昌妹,輸出了Jony yu
(2)每個對象是獨立的
因為是通過new的方式,每次生成的對象都是不相同的沉噩,因此:
var event1=new Events();
var event2=new Events();
event1.on('say',function(){
console.log('Jony event1');
});
event2.on('say',function(){
console.log('Jony event2');
})
event1.emit('say');
event2.emit('say');
//event1捺宗、event2之間的事件監(jiān)聽互相不影響
//輸出結(jié)果為'Jony event1' 'Jony event2'
9.箭頭函數(shù)中this指向舉例
var a=11;
function test2(){
this.a=22;
let b=()=>{console.log(this.a)}
b();
}
var x=new test2();
//輸出22
定義時綁定。