1: 下面的代碼輸出多少学赛?修改代碼讓 fnArr[i]() 輸出 i年堆。使用 兩種以上的方法
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i; // 后面調(diào)用函數(shù)去執(zhí)行的時(shí)候,這里的 i 實(shí)際上已經(jīng)是全局的i盏浇,已經(jīng)經(jīng)過(guò)了10次循環(huán)
};
}
console.log( fnArr[3]() ); //輸出10
方法1:聲明個(gè)函數(shù)嘀韧,創(chuàng)建新的作用域并立即執(zhí)行,從而每次給數(shù)組fnArr賦不同的函數(shù)
var fnArr = []
for (var i = 0; i < 10; i ++) {
!function(i){ // 這里的 i 只是形參缠捌,可以是任何其他名字锄贷,函數(shù)內(nèi)部使用相同名字即可
fnArr[i] = function(){
return i // 后面調(diào)用函數(shù)去執(zhí)行的時(shí)候,這里的 i 實(shí)際上已經(jīng)是全局的i曼月,已經(jīng)經(jīng)過(guò)了10次循環(huán)
}
}(i) // 每一次循環(huán)谊却,都會(huì)執(zhí)行函數(shù),傳入的不同的 i
}
console.log( fnArr[3]() ); //輸出10
方法2:
//方法2 使用自動(dòng)執(zhí)行函數(shù)哑芹,返回的是原來(lái)的函數(shù)炎辨,但自動(dòng)執(zhí)行函數(shù)每一次循環(huán)時(shí)傳入的 i 都不一樣
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(i){
return function(){
return i; //所以這里的i,在循環(huán)時(shí)傳入的是什么值聪姿,五百年后調(diào)用出來(lái)還是什么值碴萧,不會(huì)自增
}
}(i)
}
console.log( fnArr[3]() );
以上兩種方法都可以,造成問(wèn)題的原因就是閉包末购,函數(shù)內(nèi)訪(fǎng)問(wèn)了外部的變量破喻,而外部變量的值在函數(shù)執(zhí)行的時(shí)候早已塵埃落地。
所以解決問(wèn)題的關(guān)鍵就是使用立即執(zhí)行函數(shù):在循環(huán)的時(shí)候盟榴,就做點(diǎn)手腳曹质,通過(guò)再‘包’一層函數(shù),隔斷閉包擎场,構(gòu)建新的函數(shù)作用域羽德,并立即執(zhí)行,把每次自增的 i 都挨個(gè)傳入函數(shù)的局部作用域內(nèi)迅办,賦給新的變量宅静。
這樣最后調(diào)用函數(shù)的時(shí)候,能看似得到fnArr[i]() 輸出 i , 但實(shí)際上這里輸出的 i 早已經(jīng)不是當(dāng)年的 i 站欺,當(dāng)年的 i 循環(huán)完了就已經(jīng)變成10了姨夹,不信的話(huà)究驴, console.log(i) 便知。
方法3:
var fnArr = [];
for (let i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i;
};
}
console.log( fnArr[3]() ); //輸出 3
只修改一個(gè)地方匀伏,把for循環(huán)中的var i 換成 let i 構(gòu)成塊級(jí)變量洒忧,從而在一開(kāi)始,就避免了i 是全局變量所帶來(lái)的問(wèn)題——return i 這里的 i 每次調(diào)用時(shí)够颠,都指向循環(huán)結(jié)束后的 i 熙侍,也就是 10 。
let 使得 for每次循環(huán)履磨,其塊內(nèi)都會(huì)生成一個(gè)塊級(jí)作用域蛉抓,所以4個(gè)事件處理函數(shù),也就是console.log(i) 就綁定指向了不同的 i 剃诅。
2: 封裝一個(gè)汽車(chē)對(duì)象巷送,可以通過(guò)如下方式獲取汽車(chē)狀態(tài)
var Car = (function(){
var speed = 0;
function setSpeed(s){
speed = s
}
...
return {
setSpeed: setSpeed,
...
}
})()
Car.setSpeed(30);
Car.getSpeed(); //30
Car.accelerate();
Car.getSpeed(); //40;
Car.decelerate();
Car.decelerate();
Car.getSpeed(); //20
Car.getStatus(); // 'running';
Car.decelerate();
Car.decelerate();
Car.getStatus(); //'stop';
//Car.speed; //error
做法如下:
var Car = (function(){
var speed = 0;
function setSpeed(s){
speed = s
}
function getSpeed(){
return speed
}
function accelerate(){
speed += 10
}
function decelerate(){
if(speed !== 0){
speed -= 10
}
}
function getStatus(){
return (speed === 0)?'stop':'running'
}
return {
// 返回對(duì)象的屬性是方法(如果一個(gè)屬性的值為函數(shù),通常把這個(gè)屬性稱(chēng)為“方法”)
setSpeed: setSpeed, // 這里屬性名和值不一定要一樣矛辕,也可以把上面的方法函數(shù)匿名寫(xiě)在這里
getSpeed: getSpeed,
accelerate: accelerate,
decelerate: decelerate,
getStatus: getStatus
}
})() //Car立即執(zhí)行后返回的是對(duì)象笑跛,對(duì)象里存的方法
//對(duì)象.方法()
Car.setSpeed(30);
Car.getSpeed(); //30
Car.accelerate();
Car.getSpeed(); //40;
Car.decelerate();
Car.decelerate();
Car.getSpeed(); //20
Car.getStatus(); // 'running';
Car.decelerate();
Car.decelerate();
Car.getStatus(); //'stop';
//Car.speed; //error 這樣是get不到speed的,speed是被封裝在函數(shù)里的變量聊品,只能用提供的方法間接訪(fǎng)問(wèn)
題目3:下面這段代碼輸出結(jié)果是? 為什么?
var a = 1;
setTimeout(function(){
a = 2;
console.log(a);
}, 0);
var a ;
console.log(a);
a = 3;
console.log(a);
// 分別輸出 1 3 2
因?yàn)槎〞r(shí)器會(huì)被異步執(zhí)行飞蹂,0代表 立刻進(jìn)入任務(wù)隊(duì)列中,但是具體執(zhí)行翻屈,要先看主進(jìn)程是否執(zhí)行完畢陈哑,再看任務(wù)隊(duì)列前面是否還有其他任務(wù)。 所以 0 只是代表盡可能快地的被執(zhí)行
4:下面這段代碼輸出結(jié)果是? 為什么?
var flag = true;
setTimeout(function(){
flag = false;
},0)
while(flag){}
console.log(flag);
注定沒(méi)結(jié)果伸眶。 無(wú)限死循環(huán)惊窖,CPU瞬間100%峰值。
因?yàn)槎〞r(shí)器永遠(yuǎn)不會(huì)被執(zhí)行厘贼,會(huì)被放入任務(wù)隊(duì)列中界酒,先去執(zhí)行的是while,flag 永遠(yuǎn)都是true, 后面的console.log(flag)也不會(huì)輸出
5: 下面這段代碼輸出涂臣?如何輸出delayer: 0, delayer:1...(使用閉包來(lái)實(shí)現(xiàn))
for(var i=0;i<5;i++){
setTimeout(function(){
console.log('delayer:' + i );
}, 0);
console.log(i);
}
輸出 0 1 2 3 4 delayer: 5 delayer: 5 delayer: 5 delayer: 5 delayer: 5
做法參考第一題:
for(var i=0;i<5;i++){
//每次循環(huán)盾计,分別傳入 0 1 2 3 4
setTimeout(function(i){
//這里的參數(shù) i 作用域只在當(dāng)前函數(shù),不再是之前的i赁遗,參數(shù)傳進(jìn)來(lái)的時(shí)候,相當(dāng)于內(nèi)部的 i 分別賦值
return function(){
console.log('delayer:' + i );
}
}(i), 0);
console.log(i);
}
6: 如何獲取元素的真實(shí)寬高
第一種情況就是寬高都寫(xiě)在樣式表里:
就比如#div1{width:120px;}族铆。這種情況通過(guò)#div1.style.width拿不到寬度岩四,而通過(guò)#div1.offsetWidth或者#div1.clientWidth才可以獲取到寬度(前者包含padding、border和滾動(dòng)條哥攘,后者包含padding不包含border和滾動(dòng)條)剖煌。
或者使用 getComputedStyle($('div')).width (這里的寬度材鹦,如果沒(méi)設(shè)置盒模型,那么就是content寬度耕姊,如果有box-sizing: border-box; 那么width就包含padding和border),在低版本IE中對(duì)應(yīng)的實(shí)現(xiàn)方法是:element.currentStyle
第二種情況就是寬和高是寫(xiě)在行內(nèi)中:
比如style="width:120px;"桶唐,這中情況通過(guò)上述3個(gè)方法都能拿到寬度。
注意如果不是寫(xiě)在行內(nèi)style中的屬性都不能通過(guò)id.style.atrr來(lái)獲取茉兰。
7: URL 如何編碼解碼尤泽?為什么要編碼?
JavaScript提供四個(gè)URL的編碼/解碼方法:
decodeURI()
decodeURIComponent()
encodeURI()
encodeURIComponent()
區(qū)別:
- encodeURI方法不會(huì)對(duì)下列字符編碼:
- ASCII字母
- 數(shù)字
- ~!@#$&*()=:/,;?+'
- encodeURIComponent方法不會(huì)對(duì)下列字符編碼:
- ASCII字母
- 數(shù)字
- ~!*()'
所以encodeURIComponent比encodeURI編碼的范圍更大规脸。
encodeURI和encodeURIComponent的區(qū)別在于前者被設(shè)計(jì)來(lái)用于對(duì)完整URL進(jìn)行URL Encode坯约,于是URL中的功能字符,比如&, ?, /, =等等這些并不會(huì)被轉(zhuǎn)義莫鸭;而后者被設(shè)計(jì)來(lái)對(duì)一個(gè)URL中的值進(jìn)行轉(zhuǎn)義嘁傀,會(huì)把這些功能字符也進(jìn)行轉(zhuǎn)義锐锣。應(yīng)用場(chǎng)景最常見(jiàn)的一個(gè)是手工拼URL的時(shí)候,對(duì)每對(duì)KV(鍵值)用encodeURIComponent進(jìn)行轉(zhuǎn)義。
一個(gè)直觀的例子坚踩,你隨便找個(gè)URL,用encodeUR轉(zhuǎn)換一下捡硅,然后paste到瀏覽器地址欄荐捻,回車(chē)發(fā)現(xiàn)依然可以訪(fǎng)問(wèn),而用encodeURIComponent轉(zhuǎn)換后蛋欣,則會(huì)被瀏覽器認(rèn)為你是要搜索航徙。
為什么需要編碼呢?
之所以要進(jìn)行編碼陷虎,是因?yàn)閁RL中有些字符會(huì)引起歧義到踏,比如URL中的參數(shù)字符串,如果包含了=或者&尚猿,就會(huì)造成服務(wù)器解析錯(cuò)誤窝稿,因此要進(jìn)行轉(zhuǎn)義,也就是編碼凿掂。
再比如 URL格式采用的ASCII碼伴榔,而不是Unicode,如果URL中有非ASCII字符庄萎,比如漢字踪少,不轉(zhuǎn)碼也會(huì)出現(xiàn)問(wèn)題。
當(dāng)然在瀏覽器的地址欄你所看到的中文糠涛,實(shí)際上傳輸時(shí)經(jīng)過(guò)了轉(zhuǎn)換援奢,只是瀏覽器用了UTF-8的字符集展示給你,或者在當(dāng)前頁(yè)面F12打開(kāi)控制臺(tái)輸入 location.href 看一下當(dāng)前URL忍捡,就是經(jīng)過(guò)編碼后的集漾。
8: 補(bǔ)全如下函數(shù)切黔,判斷用戶(hù)的瀏覽器類(lèi)型
function isAndroid(){
}
funcnction isIphone(){
}
function isIpad(){
}
function isIOS(){
}
主要的思路就是 navigator.userAgent 方法的使用:
function isAndroid() {
if(navigator.userAgent.indexOf('Android') > -1){ //如果匹配到就返回index索引值,否則返回 -1
return '這是Android瀏覽器'
}
}
function isIphone(){
if(/iPhone/.test(navigator.userAgent)){ //使用正則 reg.test(str) ,如果匹配就返回true具篇,否則返回false
return '這是Iphone瀏覽器'
}
}
function isIpad(){
if(/iPad/.test(navigator.userAgent)){
return '這是Ipad瀏覽器'
}
}
function isIOS(){
if(/iPhone|iPad/.test(navigator.userAgent)){
return '這是IOS瀏覽器'
}
}
// 可以使用chrome 的device 設(shè)備模擬纬霞,來(lái)試驗(yàn)