1. setTimeout輸出值的時候刘绣,如何實現(xiàn)i按序輸出权烧?
for(var i=0;i<10;i++){
setTimeout(function ten(){
console.log(i);
},10);
}//輸出結(jié)果是10個10
問:為什么輸出的是10個10赖钞?
答:JS是一個單線程的解釋器需纳,setTimeout本質(zhì)是間隔一定時間將任務(wù)添加到任務(wù)隊列中。輸出的時候for循環(huán)作為主線程已經(jīng)執(zhí)行完畢岳瞭,此時作用域中的i=5
拥娄;按序執(zhí)行10次輸出i
蚊锹,就會輸出10個10瞳筏;
問:如何輸出成按序輸出?
答:方法一:
生成一個立即執(zhí)行的函數(shù)牡昆,將i
作為參數(shù)輸入(閉包)
for(var i=0;i<10;i++){
(function(i){
setTimeout(function ten(){
console.log(i);
},10);
})(i)
}
方法二:用es6中的let來聲明變量姚炕,相當(dāng)于let在每個塊級作用域里面都聲明了一個變量i
for(let i=0;i<10;i++){
setTimeout(function ten(){
console.log(i);
},10);
}
方法三:使用setTimeout
的第三個參數(shù)
for(var i=0;i<10;i++){
setTimeout(function ten(){
console.log(i);
},10,i);
}
2. 繼承prototype大概是怎么實現(xiàn)的?
實例對象per的構(gòu)造器constructor是指構(gòu)造函數(shù)Person丢烘;
console.log(per.constructor==Person);//true
實例對象的_proto_
和構(gòu)造函數(shù)中的prototype相等柱宦;
console.log(per._proto_.constructor==Person.prototype.constructor);//true
原型鏈:實例對象使用的屬性或者方法,先在實例中查找播瞳,找到了則直接使用掸刊,找不到則去實例對象的__proto__
指向的原型對象prototype中找,找到了則使用,找不到則報錯赢乓。
實例對象中有_proto_
這個屬性忧侧,叫原型石窑,也是一個對象,這個屬性是給瀏覽器使用蚓炬,
不是標準的屬性—–>proto—–>可以叫原型對象松逊;
構(gòu)造函數(shù)中有prototype這個屬性,叫原型肯夏,也是一個對象经宏,這個屬性是給程序員使用,
是標準的屬性——>prototype—>可以叫原型對象驯击;
- 原型繼承核心就是讓自定義的構(gòu)造器的prototype對象指向父類構(gòu)造器生成的對象:
//Person是個構(gòu)造函數(shù)烁兰,Per是自定義的構(gòu)造器
function Per(){}
Per.prototype = new Person('Alice');
const person = new Per()//繼承父類實例定義的所有屬性以及父類構(gòu)造器原型上的屬性
-
借用函數(shù)繼承:通過函數(shù)對象本身的
call
和apply
來顯示的指定函數(shù)調(diào)用時必備的參數(shù);
function Per(name){
Person.call(this,name)//當(dāng)成了普通函數(shù)來使用
}
const person = new Per('Alice')
//只能調(diào)用Person中定義的屬性和函數(shù)徊都,無法調(diào)用Person定義在prototype上的屬性和方法
- 組合繼承(原型鏈+借用函數(shù)):[為了解決借用函數(shù)無法使用函數(shù)原型上的屬性和方法]
function Per(name){
Person.call(this,name)//當(dāng)成了普通函數(shù)來使用
}
Per.prototype = new Person('Alice')//這樣寫缚柏,會造成Animal實例化兩次,沒有自己的原型
//或者
Per.prototype = Person.prototype//這樣寫碟贾,就不會造成多次實例化
const person = new Per('Alice')
- 原型式繼承:提供一個被繼承的對象币喧,把這個對象掛在到某個構(gòu)造函數(shù)的prototype上,再利用new袱耽;
function inherit (object) {
function fn () {} // 提供一個函數(shù)
fn.prototype = object ; // 設(shè)置函數(shù)的prototype
return new fn() // 返回這個函數(shù)實例化出來的對象
}
const person = Person('Alice');
const me=inherit(person);//可以繼承peroson對象上所有的方法和屬性
- 寄生式繼承
-
寄生組合式繼承:利用
Object.create()
方法
3. js的垃圾回收機制杀餐?(摘自紅寶書)
- 離開作用域的值將被自動標記為可以回收,因此將在垃圾收集期間被刪除朱巨;
- “ 標記清除 ” 是目前最主流的垃圾收集算法史翘,這種算法的思想是給當(dāng)前不使用的值加上標記,然后再回收其內(nèi)存(當(dāng)變量進入環(huán)境時冀续,將變量標記為“進入環(huán)境”琼讽。當(dāng)變量離開環(huán)境時,將其標記為“離開環(huán)境”洪唐,標記“離開環(huán)境”的就回收內(nèi)存)钻蹬;
- 另一種垃圾收集算法是 “ 引用計數(shù) ” ,這種算法的思想是跟蹤記錄所有值被引用的次數(shù)凭需。
JavaScript
引擎目前都不再使用這種算法问欠;但在 IE 中訪問非原生JavaScript
對象(如DOM
元素)時,這種算法仍然可能會導(dǎo)致問題粒蜈; - 當(dāng)代碼中存在循環(huán)引用現(xiàn)象時顺献,” 引用計數(shù) “算法就會導(dǎo)致問題;
- 解除變量的引用不僅有助于消除循環(huán)引用現(xiàn)象枯怖,而且對垃圾收集也有好處注整。為了確保有效的回收內(nèi)存,應(yīng)該及時解除不再使用的全局對象、全局對象屬性以及循環(huán)引用變量的引用肿轨。
代碼回收規(guī)則如下:
1.全局變量不會被回收借浊。
2.局部變量會被回收,也就是函數(shù)一旦運行完以后萝招,函數(shù)內(nèi)部的東西都會被銷毀蚂斤。
3.只要被另外一個作用域所引用就不會被回收
4. 閉包是什么?用let怎么實現(xiàn)閉包槐沼?有什么優(yōu)點和缺點曙蒸?
閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)。
一個閉包就是一個沒有釋放資源的棧區(qū)岗钩,棧區(qū)內(nèi)的變量處于激活狀態(tài)纽窟。
閉包:當(dāng)外部函數(shù)返回之后,內(nèi)部函數(shù)可以訪問外部函數(shù)的屬性或者方法兼吓。(站友提供的說法)
在ES6中l(wèi)et實際是為js新增了塊級作用域臂港。
let聲明的變量可以綁定在作用域中。
優(yōu)點:① 能夠讀取函數(shù)內(nèi)部的變量视搏;②讓這些變量一直存在于內(nèi)存中审孽,不會在調(diào)用結(jié)束后被垃圾回收機制回收;
缺點:由于閉包會使用函數(shù)中的變量存在在內(nèi)存中浑娜,內(nèi)存消耗很大佑力,所以不能濫用閉包;解決的辦法是退出函數(shù)之前筋遭,將不使用的局部變量刪除打颤;
5. 怎么調(diào)程序中出現(xiàn)的代碼?
alert('方法一')
console.log('方法二')
Chrome中的開發(fā)者工具:斷點設(shè)置漓滔、調(diào)試功能
6.this的指向問題编饺?
this的最終指向的是那個調(diào)用它的對象。
改變this指向的方法:
- 使用箭頭函數(shù)响驴;
- 在函數(shù)內(nèi)部使用
_this=this;
- 使用apply透且、call、bind
- new實例化一個對象踏施;
在非嚴格模式下石蔗,如果如果函數(shù)沒有用作構(gòu)造函數(shù),而是僅僅作為普通函數(shù)使用的話畅形,那么函數(shù)中的this是指向window的。在嚴格模式下诉探,this的值就是undefined日熬。
7.js的執(zhí)行機制:Event loop
- 同步和異步任務(wù)分別進入不同的執(zhí)行”場所”,同步的進入主線程肾胯,異步的進入Event Table并注冊函數(shù)竖席。
- 當(dāng)指定的事情完成時耘纱,Event Table會將這個函數(shù)移入Event Queue。
- 主線程內(nèi)的任務(wù)執(zhí)行完畢為空毕荐,會去Event Queue讀取對應(yīng)的函數(shù)束析,進入主線程執(zhí)行。
- 上述過程會不斷重復(fù)憎亚,也就是常說的Event Loop(事件循環(huán))员寇。
? setTimeout
這個函數(shù),是經(jīng)過指定時間后第美,把要執(zhí)行的任務(wù)加入到Event Queue中蝶锋,又因為是單線程任務(wù)要一個一個執(zhí)行,如果前面的任務(wù)需要的時間太久什往,那么只能等著扳缕,導(dǎo)致真正的延遲時間遠遠大于設(shè)置的時間長度。
? setInterval
會每隔指定的時間將注冊的函數(shù)置入Event Queue别威,如果前面的任務(wù)耗時太久躯舔,那么同樣需要等待。唯一需要注意的一點是省古,對于setInterval(fn,ms)
來說庸毫,我們已經(jīng)知道不是每過ms
秒會執(zhí)行一次fn
,而是每過ms
秒衫樊,會有fn
進入Event Queue飒赃。一旦setInterval的回調(diào)函數(shù)fn執(zhí)行時間超過了延遲時間ms,那么就完全看不出來有時間間隔了科侈。
除了廣義的同步任務(wù)和異步任務(wù)载佳,我們對任務(wù)有更精細的定義:
- macro-task(宏任務(wù)):包括整體代碼script,setTimeout臀栈,setInterval
- micro-task(微任務(wù)):Promise蔫慧,process.nextTick
不同類型的任務(wù)會進入對應(yīng)的Event Queue,比如setTimeout
和setInterval
會進入相同的Event Queue权薯。
進入整體代碼(宏任務(wù))后姑躲,開始第一次循環(huán)。接著執(zhí)行所有的微任務(wù)盟蚣。然后再次從宏任務(wù)開始黍析,找到其中一個任務(wù)隊列執(zhí)行完畢,再執(zhí)行所有的微任務(wù)屎开。
8.給DOM元素綁定事件有哪幾種方法阐枣?
1.使用內(nèi)聯(lián);2.使用.onclick
的方式;3.使用事件監(jiān)聽addEventListener
的方式蔼两;(IE使用attachEvent)
1甩鳄、2兩種方式都是存在一個弊端的,就是一個元素只能添加一個事件额划。
<!--1.內(nèi)聯(lián)-->
<input type="button" value="btn" onclick="alert('內(nèi)聯(lián)');">
? 這種方式就是在一個元素上面直接綁定了一個點擊onclick
事件妙啃。同時,這個事件的優(yōu)先級是最高的俊戳。
<!--2\. .onclick的方式-->
<input type="button" value="btn">
<script>
var btn = document.getElementsBytagname("input")[0];
btn.onclick = function(){
alert('.onclick')
}
</script>
使用這種形式也是可以給一個DOM元素添加上一個事件揖赴。
3.給一個元素(DOM對象)添加兩個甚至是多個事件
<!--3.事件監(jiān)聽addEventListener-->
<input type="button" value="按鈕">
<script>
var btn = document.getElementsBytagname("input")[0];
btn.addEventListener("click", function(){
alert(1)
})
btn.addEventListener("click", function(){
alert(2)
})
</script>
使用addEventListener
的方式還可以擁有第三個參數(shù):
- 事件類型,不需要添加上
on
- 事件函數(shù)
- 是否捕獲(布爾值)品抽,默認是
false
储笑,即不捕獲,那就是冒泡圆恤。
attachEvent
綁定的事件突倍,如果需要注銷,應(yīng)該使用detachEvent
9.三大事件冒泡盆昙、捕獲是怎么執(zhí)行的羽历?
先捕獲再冒泡
事件冒泡的概念下在p元素上發(fā)生click事件的順序應(yīng)該是p -> div -> body -> html -> document;
事件捕獲的概念下在p元素上發(fā)生click事件的順序應(yīng)該是document -> html -> body -> div -> p淡喜;
接下來是摘自《紅寶書》的總結(jié):
事件流描述的是從頁面中接收事件的順序秕磷。P348
IE的事件流叫做事件冒泡:就是指事件開始時由最具體的元素(文檔中嵌套層次最深的的那個節(jié)點)接收,然后逐級向上傳播到較為不具體的節(jié)點(文檔)炼团。
div–>body–>html–>document
事件捕獲:不太具體的節(jié)點應(yīng)該更早接收到事件澎嚣,而最具體的節(jié)點應(yīng)該最后接收到事件。(用意在于在事件到達預(yù)定目標之前捕獲它)
document–>html–>body–>div
10.js處理異步的幾種方式
一瘟芝、回調(diào)函數(shù)(callback)
概念:回調(diào)是一個函數(shù)被作為一個參數(shù)傳遞到另一個函數(shù)里易桃,在那個函數(shù)執(zhí)行完后再執(zhí)行。
優(yōu)點:回調(diào)函數(shù)是異步編程最基本的方法锌俱,簡單晤郑、容易理解和部署
缺點:不利于代碼的閱讀和維護,各個部分之間高度耦合贸宏,流程會很混亂造寝,而且每個任務(wù)只能指定一個回調(diào)函數(shù)。
注意 區(qū)分:回調(diào)函數(shù)和異步 回調(diào)并不一定就是異步吭练。他們自己并沒有直接關(guān)系诫龙。
二、事件監(jiān)聽
采用事件驅(qū)動模式线脚。任務(wù)的執(zhí)行不取決代碼的順序赐稽,而取決于某一個事件是否發(fā)生叫榕。
監(jiān)聽函數(shù)有:on浑侥,bind姊舵,listen,addEventListener寓落,observe
//on
f1.on('done',f2);
//或者
function f1(){
settimeout(function(){
//...f1的任務(wù)代碼
f1.trigger('done'); //執(zhí)行完成后括丁,立即觸發(fā)done事件,從而開始執(zhí)行f2.
},1000);
}
優(yōu)點:容易理解伶选,可以綁定多個事件史飞,每一個事件可以指定多個回調(diào)函數(shù),而且可以去耦合仰税,有利于實現(xiàn)模塊化构资。
缺點:整個程序都要變成事件驅(qū)動型,運行流程會變得不清晰陨簇。
element.onclick=function(){
//處理函數(shù)
}
優(yōu)點:寫法兼容到主流瀏覽器
缺點:當(dāng)同一個element元素綁定多個事件時吐绵,只有最后一個事件會被添加,相當(dāng)于一次只能添加一個事件河绽;
//IE:attachEvent;三個方法執(zhí)行順序:3-2-1己单;
elment.attachEvent("onclick",handler1);
elment.attachEvent("onclick",handler2);
elment.attachEvent("onclick",handler3);
//標準addEventListener;執(zhí)行順序:1-2-3
elment.addEvenListener("click",handler1,false);
elment.addEvenListener("click",handler2,false);
elment.addEvenListener("click",handler3,false);
注意:該方法的第三個參數(shù)是冒泡獲取,是一個布爾值:當(dāng)為false時表示由里向外(冒泡)耙饰,true表示由外向里(捕獲)纹笼。
三、發(fā)布/訂閱(又稱觀察者模式)
//jQuery的一個插件 Ben Alman的Tiny Pub/Sub
jQuery.subscribe("done", f2);//f2向"信號中心"jQuery訂閱"done"信號苟跪。
jQuery.unsubscribe("done", f2);//取消訂閱(unsubscribe)
//或者
function f1(){
setTimeout(function () {
// ...f1的任務(wù)代碼
jQuery.publish("done");//f1執(zhí)行完成后廷痘,向"信號中心"jQuery發(fā)布"done"信號,從而引發(fā)f2的執(zhí)行
}, 1000);
}
四件已、promise對象(promise 模式)
promise是一種模式笋额,promise可以幫忙管理異步方式返回的代碼。將代碼進行封裝并添加一個類似于事件處理的管理層拨齐。我們可以使用promise來注冊代碼鳞陨,這些代碼會在在promise成功或者失敗后運行。
我們可以注冊任意數(shù)量的函數(shù)再成功或者失敗后運行瞻惋,也可以在任何時候注冊事件處理程序厦滤。
promise有兩種狀態(tài):1、等待(pending)歼狼;2掏导、完成(settled)。promise會一直處于等待狀態(tài)羽峰,直到它所包裝的異步調(diào)用返回/超時/結(jié)束趟咆。這時候promise狀態(tài)變成完成添瓷。完成狀態(tài)分成兩類:解決(resolved);拒絕(rejected)值纱。
resolve()函數(shù)告訴promise用戶promise已解決鳞贷;reject()函數(shù)告訴promise用戶promise未能順利完成。注意then和catch用法虐唠,可以將他們想象成onsucess和onfailure事件的處理程序搀愧。
f1().then(f2).then(f3);
f1().then(f2).fail(f3);
優(yōu)點:回調(diào)函數(shù)寫成了鏈式寫法,程序的流程可以看得很清楚疆偿,而且有一整套的配套方法咱筛,可以實現(xiàn)很多強大的功能。如果一個任務(wù)已經(jīng)完成杆故,再添加回調(diào)函數(shù)迅箩,該回調(diào)函數(shù)會立即執(zhí)行。
缺點:編寫和理解都相對比較難处铛。
五饲趋、優(yōu)雅的async/await
async 函數(shù)書寫的方式跟我們普通的函數(shù)書寫方式一樣,只不過是前面多了一個 async
關(guān)鍵字罢缸,并且函數(shù)返回的是一個 Promise 對象篙贸,所接收的值就是函數(shù) return 的值。
在 async 函數(shù)內(nèi)部可以使用 await 命令枫疆,表示等待一個異步函數(shù)的返回爵川。await 后面跟著的是一個 Promise 對象,如果不是的話息楔,系統(tǒng)會調(diào)用 Promise.resolve()
方法寝贡,將其轉(zhuǎn)為一個 resolve 的 Promise 的對象。
let bar = async function(){
try{
await Promise.reject('error')
}catch(e){
console.log(e)
}
}
11.JS設(shè)置CSS樣式的幾種方式
? 1. 直接設(shè)置style的屬性 某些情況用這個設(shè)置 !important值無效
element.style.height = '100px';
/* 如果屬性有'-'號值依,就寫成駝峰的形式(如textAlign)
如果想保留 - 號圃泡,就中括號的形式 element.style['text-align'] = '100px';*/
? 2. 直接設(shè)置屬性(只能用于某些屬性,相關(guān)樣式會自動識別)
element.setAttribute('height', 100);
element.setAttribute('height', '100px');
? 3. 設(shè)置style的屬性
element.setAttribute('style', 'height: 100px !important');
? 4. 使用setProperty 如果要設(shè)置!important愿险,推薦用這種方法設(shè)置第三個參數(shù)
element.style.setProperty('height', '300px', 'important');
? 5. 改變class 比如JQ的更改class相關(guān)方法
element.className = 'blue';
element.className += 'blue fb';
/*因JS獲取不到css的偽元素颇蜡,所以可以通過改變偽元素父級的class來動態(tài)更改偽元素的樣式*/
? 6. 設(shè)置cssText
element.style.cssText = 'height: 100px !important';
element.style.cssText += 'height: 100px !important';
? 7. 創(chuàng)建引入新的css樣式文件
function addNewStyle(newStyle) {
var styleElement = document.getElementById('styles_js');
if (!styleElement) {
styleElement = document.createElement('style');
styleElement.type = 'text/css';
styleElement.id = 'styles_js';
document.getElementsByTagName('head')[0].appendChild(styleElement);
}
styleElement.appendChild(document.createTextNode(newStyle));
}
addNewStyle('.box {height: 100px !important;}');
? 8. 使用addRule、insertRule
// 在原有樣式操作
document.styleSheets[0].addRule('.box', 'height: 100px');
document.styleSheets[0].insertRule('.box {height: 100px}', 0);
// 或者插入新樣式時操作
var styleEl = document.createElement('style'),
styleSheet = styleEl.sheet;
styleSheet.addRule('.box', 'height: 100px');
styleSheet.insertRule('.box {height: 100px}', 0);
document.head.appendChild(styleEl);
12.點擊網(wǎng)頁任意空白區(qū)域隱藏div
$('.phone_cell').on('click',function(event){
//取消事件冒泡
event.stopPropagation();
$('.ulfix').slideToggle();
});
//點擊空白處隱藏彈出層辆亏,下面為消失效果
$(window).click(function(){
if ($('.ulfix').css('display')!='none') {
$('.ulfix').slideToggle();
}
});
需要注意的是如果單層里面本身還有點擊事件风秤,如果點擊自身也會出現(xiàn)消失的情況 ,解決辦法給點擊事件增加
13.js的基本數(shù)據(jù)類型及其判斷方法
值類型(6個基本類型):字符串(String)扮叨、數(shù)字(Number)缤弦、布爾(Boolean)、空(Null)彻磁、未定義(Undefined)碍沐、Symbol(ES6引入狸捅,表示獨一無二的值)。
引用數(shù)據(jù)類型(又稱Object類型):對象(Object)累提、數(shù)組(Array)尘喝、函數(shù)(Function)、Date 類型刻恭、RegExp 類型(正則)瞧省。
注意:基本數(shù)據(jù)類型值不可變扯夭,基本類型的比較是值的比較鳍贾;引用類型值可變,引用類型的比較是引用的比較交洗;
檢測類型常用的有typeof和instanceof:
①typeof:經(jīng)常用來檢測一個變量是不是最基本的數(shù)據(jù)類型
var a;typeof a; // undefined骑科,聲明未賦值
a = true;typeof a; // boolean
a = 666;typeof a; // number
a = "hello";typeof a; // string
a = Symbol();typeof a; // symbol
a = [];typeof a; // object
a = {};typeof a; // object
a = /aaa/g;typeof a; // object
a = null;typeof a; // object,注意null判斷出來的類型是object
a = function(){};typeof a; // function构拳,注意function判斷出來的類型是function而不是object
②instanceof:判斷一個引用類型的變量具體是不是某種類型的對象
({}) instanceof Object // true
([]) instanceof Array // true
(/aa/g) instanceof RegExp // true
(function(){}) instanceof Function // true
14.怎么判斷數(shù)組類型咆爽?5種方法
typeof運算符,constructor法置森,instanceof運算符斗埂,Object.prototype.toString方法以及Array.isArray法
①typeof:javascript原生提供的判斷數(shù)據(jù)類型的運算符,它會返回一個表示參數(shù)的數(shù)據(jù)類型的字符串凫海;但是數(shù)組呛凶,對象,正則行贪,null經(jīng)過這個判斷得到的都是object漾稀,并不能識別出數(shù)組
const s = [];
console.log(typeof(s))//object
②instanceof運算符:判斷某個構(gòu)造函數(shù)的prototype屬性所指向的對象是否存在于另外一個要檢測對象的原型鏈上。如果這個Object的原型鏈上能夠找到Array構(gòu)造函數(shù)的話建瘫,那么這個Object應(yīng)該及就是一個數(shù)組崭捍,如果這個Object的原型鏈上只能找到Object構(gòu)造函數(shù)的話,那么它就不是一個數(shù)組啰脚。
({}) instanceof Object // true
([]) instanceof Array // true
③constructor:實例化的數(shù)組擁有一個constructor屬性殷蛇,這個屬性指向生成這個數(shù)組的方法登馒。但是constructor屬性可以改寫销睁,判斷的結(jié)果不靠譜。
const a = [];console.log(a.constructor);//function Array(){ [native code] }
console.log(a.constructor==Array);//true
const o = {};console.log(o.constructor);//function Object(){ [native code] }
const r = /^[0-9]$/;console.log(r.constructor);//function RegExp() { [native code] }
const n = null;console.log(n.constructor);//報錯
④Object.prototype.toString:每一個繼承自O(shè)bject的對象都擁有toString的方法鞋既,toString方法將會返回”[object type]”贮配,其中的type代表的是對象的類型谍倦,根據(jù)type的值,我們就可以判斷這個疑似數(shù)組的對象到底是不是數(shù)組了泪勒。但是對象的toString方法也可以重寫昼蛀,可能會判斷出錯宴猾。
注意:不能直接使用數(shù)組的。toString方法叼旋,會返回字符串仇哆,如下:
const a = ['Hello','world'];a.toString();//"Hello,world"
const b = {0:'Hello',1:'world'};b.toString();//"[object Object]"
const c = 'Hello world';c.toString();//"Hello,world"
所以要判斷除了對象之外的數(shù)據(jù)的數(shù)據(jù)類型,我們需要“借用”對象的toString方法夫植,使用call或者apply方法來改變toString方法的執(zhí)行上下文:
const a = ['Hello','world'];Object.prototype.toString.call(a);//"[object Array]"
Object.prototype.toString.apply(a);//"[object Array]"
const c = 'Hello world';Object.prototype.toString.call(c);//"[object String]"
Object.prototype.toString.apply(c);//"[object String]"
⑤isArray方法:最靠譜的判斷數(shù)組的方法
const a = [];Array.isArray(a);//true
const b = {};Array.isArray(b);//false
經(jīng)過驗證讹剔,即使重寫了Object.prototype.toString方法、修改了constructor對象是不影響判斷的結(jié)果的详民。
15.new Date()獲取的時間是什么時間
運行環(huán)境的時間:如果是瀏覽器那就是電腦時間或者手機時間延欠;如果是node.js那就是服務(wù)器時間
前臺獲取的時間都用處不大、用戶可以改沈跨、不安全由捎;通常時間用于從后端傳入、服務(wù)器的時間一定是統(tǒng)一的饿凛、解析時間戳狞玛;
js中單獨調(diào)用new Date(),例如document.write(new Date())涧窒;顯示的結(jié)果是:Mar 31 10:10:43 UTC+0800 2012 這種格式的時間心肪; 但是用new Date() 參與計算會自動轉(zhuǎn)換為從1970.1.1開始的毫秒數(shù)。
JS獲取當(dāng)前時間戳
//方法1
var timestamp =Date.parse(new Date());
注意:Date.parse()得到的結(jié)果將后三位(毫秒)轉(zhuǎn)換成了000顯示纠吴,使用時可能會出現(xiàn)問題硬鞍。例如動態(tài)添加頁面元素id的時候,不建議使用呜象。
//方法2
var timestamp =(new Date()).valueOf();
//方法3
var timestamp=new Date().getTime()膳凝;
//方法4
var timestamp=+new Date();
16.深拷貝和淺拷貝的區(qū)別?怎么實現(xiàn)深拷貝恭陡?
– | 是否指向原對象 | 第一層數(shù)據(jù)為基本數(shù)據(jù)類型 | 原數(shù)據(jù)中包含引用數(shù)據(jù)類型 |
---|---|---|---|
賦值 | 是 | 改變會使原數(shù)據(jù)一同改變 | 改變會使原數(shù)據(jù)一同改變 |
淺拷貝 | 否 | 改變不會使原數(shù)據(jù)一同改變 | 改變會使原數(shù)據(jù)一同改變 |
深拷貝 | 否 | 改變不會使原數(shù)據(jù)一同改變 | 改變不會使原數(shù)據(jù)一同改變 |
賦值是賦的是該對象的在棧中的地址蹬音,而不是堆中的數(shù)據(jù),也就是兩個對象指向的是同一個存儲空間休玩,是聯(lián)動的著淆;
淺拷貝只復(fù)制一層對象的屬性,如果屬性是基本類型拴疤,拷貝的就是基本類型的值永部;如果屬性是內(nèi)存地址(引用類型),拷貝的就是內(nèi)存地址 呐矾,并不包括對象里面的為引用類型的數(shù)據(jù)苔埋;
淺拷貝的實現(xiàn):
1.Object.assign():把任意多個的源對象自身的可枚舉屬性拷貝給目標對象,然后返回目標對象蜒犯,拷貝的是對象的屬性的引用组橄,而不是對象本身荞膘。
var obj = { a: {a: "copy", b: 1} };
var newlObj = Object.assign({}, obj);//-----------
initalObj.a.a = "swallow";
console.log(obj.a.a); //swallow
2.Array.prototype.concat():修改新對象會改到原對象
let arr = [1, 2, { username: 'copy' }];
let arr2=arr.concat();//----------------
arr2[2].username = 'swallow';
console.log(arr);
3.Array.prototype.slice():同樣修改新對象會改到原對象
let arr = [1, 2, { username: 'copy' }];
let arr2=arr.slice();//----------------
arr2[2].username = 'swallow';
console.log(arr);
注:Array的slice和concat方法不修改原數(shù)組,只會返回一個淺復(fù)制了原數(shù)組中的元素的一個新數(shù)組玉工。
深拷貝是對對象以及對象的所有子對象進行拷貝羽资。
深拷貝的實現(xiàn):
1.JSON.parse(JSON.stringify()):用JSON.stringify將對象轉(zhuǎn)成JSON字符串,再用JSON.parse()把字符串解析成對象遵班,一去一來屠升,新的對象產(chǎn)生了,而且對象會開辟新的棧狭郑,可以實現(xiàn)數(shù)組或?qū)ο笊羁截?/strong>腹暖,但不能處理函數(shù)。
因為JSON.stringify() 方法是將一個JavaScript值(對象或者數(shù)組)轉(zhuǎn)換為一個 JSON字符串愿阐,不能接受函數(shù)
let arr = [1, 3, { username: ' copy'}];
let arr2 = JSON.parse(JSON.stringify(arr));
arr2[2].username = 'shen';
console.log(arr, arr4)
2.遞歸:遞歸方法實現(xiàn)深度克隆原理:遍歷對象微服、數(shù)組直到里邊都是基本數(shù)據(jù)類型,然后再去復(fù)制缨历,就是深度拷貝。
//定義檢測數(shù)據(jù)類型的功能函數(shù)
function checkedType(target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
//實現(xiàn)深度克隆---對象/數(shù)組
function clone(target) {
//判斷拷貝的數(shù)據(jù)類型
//初始化變量result 成為最終克隆的數(shù)據(jù)
let result, targetType = checkedType(target)
if (targetType === 'Object') {
result = {}
} else if (targetType === 'Array') {
result = []
} else {
return target
}
//遍歷目標數(shù)據(jù)
for (let i in target) {
//獲取遍歷數(shù)據(jù)結(jié)構(gòu)的每一項值糙麦。
let value = target[i]
//判斷目標結(jié)構(gòu)里的每一值是否存在對象/數(shù)組
if (checkedType(value) === 'Object' ||
checkedType(value) === 'Array') { //對象/數(shù)組里嵌套了對象/數(shù)組
//繼續(xù)遍歷獲取到value值
result[i] = clone(value)
} else { //獲取到value值是基本的數(shù)據(jù)類型或者是函數(shù)辛孵。
result[i] = value;
}
}
return result
}
// 內(nèi)部方法:用戶合并一個或多個對象到第一個對象
// 參數(shù):
// target 目標對象 對象都合并到target里
// source 合并對象
// deep 是否執(zhí)行深度合并
function extend(target, source, deep) {
for (key in source)
if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
// source[key]是對象,而target[key]不是對象赡磅,則target[key]={}初始化一下魄缚,否則遞歸會出錯的
if (isPlainObject(source[key]) && !isPlainObject(target[key]))
target[key] = {}
// source[key] 是數(shù)組,而 target[key]不是數(shù)組焚廊,則 target[key] = []初始化一下冶匹,否則遞歸會出錯的
if (isArray(source[key]) && !isArray(target[key]))
target[key] = []
// 執(zhí)行遞歸
extend(target[key], source[key], deep)
}
// 不滿足以上條件,說明 source[key] 是一般的值類型咆瘟,直接賦值給 target 就是了
else if (source[key] !== undefined) target[key] = source[key]
}
// Copy all but undefined properties from one or more
// objects to the `target` object.
$.extend = function(target){
var deep, args = slice.call(arguments, 1);
//第一個參數(shù)為boolean值時嚼隘,表示是否深度合并
if (typeof target == 'boolean') {
deep = target;
//target取第二個參數(shù)
target = args.shift()
}
// 遍歷后面的參數(shù),都合并到target上
args.forEach(function(arg){ extend(target, arg, deep) })
return target
}
17.數(shù)組常用的遍歷方法有哪幾種袒餐?(for…in一般用于對象)
1.普通for循環(huán)
-
性能:使用頻率最高飞蛹,性能不弱,但仍有優(yōu)化空間
for(j = 0; j < arr.length; j++) {}
2.優(yōu)化版for循環(huán)
-
性能:使用臨時變量灸眼,將長度緩存起來卧檐,避免重復(fù)獲取數(shù)組長度,當(dāng)數(shù)組較大時優(yōu)化效果才會比較明顯焰宣。
基本上是所有循環(huán)遍歷方法中性能最高的一種霉囚。
for(j = 0,len=arr.length; j < len; j++) {}
3.foreach循環(huán),沒有返回值匕积,純粹用來遍歷數(shù)組
-
性能:數(shù)組自帶的foreach循環(huán)盈罐,性能比普通for循環(huán)弱
let numbers = [1, 2, 3, 4]; numbers.forEach((item, index, array)=>{ // 執(zhí)行某些操作 })
4.for…in循環(huán)
-
性能:效率最低逻澳,不推薦用于數(shù)組遍歷,一般用于對象循環(huán)
for(j in arr) {}
5.for…of循環(huán)
-
性能:性能好于for…in暖呕,但仍低于普通for循環(huán)
不僅可以遍歷數(shù)組斜做,還可以遍歷Map、Set這兩種ES6新推出的數(shù)據(jù)結(jié)構(gòu)湾揽。
let numbers = [1, 2, 3, 4];
for(let item of numbers){
console.log(item);
}
//輸出
// 1
// 2
// 3
// 4
map遍歷
-
注意: map() 不會對空數(shù)組進行檢測瓤逼;map() 不會改變原始數(shù)組。
對數(shù)組的每一項運行給定函數(shù)库物,返回每次函數(shù)調(diào)用的返回值組成的數(shù)組霸旗。
let numbers = [1, 2, 3, 4];
let mapResult = numbers.map((item, index, array)=>{
return item * 2;
})
console.log(mapResult); // 輸出 [2, 4, 6, 8]
性能:比較優(yōu)雅,低于普通for循環(huán)戚揭,還比不上foreach
- 大致比較如下:for循環(huán) > forEach > for…of > for…in > map
還有幾個用的比較少的es5的循環(huán)方法:
every( ):對數(shù)組中的每一項運行給定函數(shù)诱告,如果該函數(shù)對每一項都返回true,則every( )返回truelet
numbers = [1, 2, 3, 4];
let everyResult = numbers.every((item, index, array)=>{
return item > 1;
})
console.log(everyResult); // 輸出 false
filter( ):對數(shù)組中的每一項運行給定函數(shù)民晒,然后返回該函數(shù)會返回true的項精居。
let numbers = [1, 2, 3, 4];
let filterResult = numbers.filter((item, index, array)=>{
return item > 1;
})
console.log(filterResult); // 輸出 [2, 3, 4]
| some( ):對數(shù)組的每一項運行給定函數(shù),如果該函數(shù)對任一項返回true潜必,則some( )就返回true靴姿。some和every就像 | | 和 &&,some是只要有一項滿足磁滚,就返回true佛吓。 |
let numbers = [1, 2, 3, 4];
let someResult = numbers.some((item, index, array)=>{
return item > 3;
})
console.log(someResult); // 輸出 true
注意:ES5的這幾個迭代方法,如果是只對item(當(dāng)前遍歷的數(shù)組項)進行操作垂攘,是不會改變原數(shù)組的吱型,但是也可以通過給定函數(shù)中接收的index參數(shù)來改變原數(shù)組使用選擇的時候饺蔑,以現(xiàn)在的硬件水平,這里的差異其實可以忽略惶室,考慮語義和功能需求來選擇:
如果你需要將數(shù)組按照給定規(guī)則轉(zhuǎn)換并返回該結(jié)果數(shù)組,就使用map趾痘。
如果你需要進行簡單的遍歷兰绣,用 forEach 或者 for of瓢阴,但是 forEach 不能通過return和break語句來終止循環(huán)少漆,所以
如果需要中途終止循環(huán)论巍,就使用 for…of或者 for循環(huán)烛谊。
如果是在遍歷數(shù)組的同時,需要改變原數(shù)組中的對應(yīng)項嘉汰,就用for循環(huán)丹禀。
for…in會把數(shù)組所擁有可枚舉的屬性都遍歷一次,所以可能會有意想不到的結(jié)果鞋怀,不推薦用來遍歷數(shù)組双泪。
另外的三個,every( )密似、filter( )焙矛、some( )按功能需要來使用即可。
18.JS獲取url參數(shù)
function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
19.在js的瀏覽器對象模型中残腌,window對象的(status)屬性是用來指定瀏覽器狀態(tài)欄里面的臨時消息
status:狀態(tài)欄信息屬性
screen:屏幕對象(也叫顯示屏對象)
history:網(wǎng)頁歷史對象
document:文本對象
20.隱式轉(zhuǎn)換(三種)掘金有篇博客:你所忽略的js隱式轉(zhuǎn)換
+
運算符即可數(shù)字相加村斟,也可以字符串相加。所以轉(zhuǎn)換時很麻煩抛猫。
==
不同于===蟆盹,故也存在隱式轉(zhuǎn)換。
- * /
這些運算符只會針對number類型闺金,故轉(zhuǎn)換的結(jié)果只能是轉(zhuǎn)換成number類型逾滥。
隱式轉(zhuǎn)換中主要涉及到三種轉(zhuǎn)換:
? 1、將值轉(zhuǎn)為原始值败匹,ToPrimitive(input, PreferredType?)寨昙。
valueOf(),toString()
PreferredType沒有設(shè)置時,Date類型的對象掀亩,PreferredType默認設(shè)置為String舔哪,其他類型對象PreferredType默認設(shè)置為Number。
? 2归榕、將值轉(zhuǎn)為數(shù)字尸红,ToNumber()。.
參數(shù) | 結(jié)果 |
---|---|
undefined | NaN |
null | +0 |
布爾值 | true轉(zhuǎn)換1,false轉(zhuǎn)換為+0 |
數(shù)字 | 無須轉(zhuǎn)換 |
字符串 | 有字符串解析為數(shù)字外里,例如:‘324’轉(zhuǎn)換為324怎爵,‘qwer’轉(zhuǎn)換為NaN |
對象(obj) | 先進行 ToPrimitive(obj, Number)轉(zhuǎn)換得到原始值,在進行ToNumber轉(zhuǎn)換為數(shù)字 |
? 3盅蝗、將值轉(zhuǎn)為字符串鳖链,ToString()。
參數(shù) | 結(jié)果 |
---|---|
undefined | ‘undefined’ |
null | ‘null’ |
布爾值 | 轉(zhuǎn)換為’true’ 或 ‘false’ |
數(shù)字 | 數(shù)字轉(zhuǎn)換字符串墩莫,比如:1.765轉(zhuǎn)為’1.765’ |
字符串 | 無須轉(zhuǎn)換 |
對象(obj) | 先進行 ToPrimitive(obj, String)轉(zhuǎn)換得到原始值芙委,在進行ToString轉(zhuǎn)換為字符串 |
舉例
({} + {}) = ?
兩個對象的值進行+運算符,肯定要先進行隱式轉(zhuǎn)換為原始類型才能進行計算狂秦。
1灌侣、進行ToPrimitive轉(zhuǎn)換,由于沒有指定PreferredType類型裂问,{}會使默認值為Number侧啼,進行ToPrimitive(input, Number)運算。
2堪簿、所以會執(zhí)行valueOf方法痊乾,({}).valueOf(),返回的還是{}對象,不是原始值椭更。
3哪审、繼續(xù)執(zhí)行toString方法,({}).toString(),返回"[object Object]"虑瀑,是原始值湿滓。
故得到最終的結(jié)果,"[object Object]" + "[object Object]" = "[object Object][object Object]"
在==
進行轉(zhuǎn)換時舌狗,
1.類型相同時茉稠,沒有類型轉(zhuǎn)換,主要注意NaN不與任何值相等把夸,包括它自己,即NaN !== NaN
2.類型不相同時铭污,
x,y 為null恋日、undefined兩者中一個 // 返回true
x、y為Number和String類型時嘹狞,則轉(zhuǎn)換為Number類型比較岂膳。
有Boolean類型時,Boolean轉(zhuǎn)化為Number類型比較磅网。
一個Object類型谈截,一個String或Number類型,將Object類型進行原始轉(zhuǎn)換后,按上面流程進行原始值比較簸喂。
21.使用setTimeout模擬setInterval
var i=0;
setInterval(function(){
i=i++;
console.log(i)
},1000)
//使用setTimeout也可以產(chǎn)生setInterval的效果
var i=0;
function intv(){
setTimeout(function(){
consolr.log(i++);
intv()
},1000)
}
intv()
但是setInterval和intv()還是有些差別
/intv()函數(shù)表示在函數(shù)執(zhí)行完成之后的一秒再執(zhí)行下一個函數(shù)
函數(shù)A(執(zhí)行時間)-->(1s)-->函數(shù)B(執(zhí)行時間)-->(1S)-->函數(shù)C(執(zhí)行時間)-->(1S);
/setInterval表示函數(shù)執(zhí)行的間隔為1秒鐘,可以這樣認為
(函數(shù)A(執(zhí)行時間)+(間隔時間))=(1S)-->(函數(shù)B(執(zhí)行時間)+(間隔時間))=(1S)-->(函數(shù)C(執(zhí)行時間)+(間隔時間))=(1S)
22.計算數(shù)字各位相加之和
// 初始化 sum(和)為 0
var a = 283, sum = 0;
for(var i = 0; i < a.toString().length; i++) {
alert(a.toString()[i]);
// 每一位相加
// 第一次 0 + 2毙死,第二次 2 + 8,第三次 10 + 3
sum += parseInt(a.toString()[i]);
}
alert(sum);//13
23.數(shù)據(jù)結(jié)構(gòu)中”遍歷”是什么意思?
所謂遍歷喻鳄,是指沿著某條搜索路線扼倘,依次對樹中每個結(jié)點均做一次且僅做一次訪問。
24.在js中除呵,兩個整數(shù)進行除(/)運算再菊,結(jié)果也為整數(shù)嗎?不是颜曾,是小數(shù)
在js中計算5/2纠拔,不會像在java中得到2,結(jié)果會是2.5泛豪,那如何得到整數(shù)2呢稠诲,整合下搜索結(jié)果,總共有以下幾種方法:
- parseInt(5/2)
- Math.floor(5/2)
- | 5/2 | 0 |
| 第三種特別說明下候址,’ | ‘是位運算符吕粹,js中位運算之前會轉(zhuǎn)為整數(shù),與0位運算結(jié)果還是本身岗仑,所以也能達到取整數(shù)的目的匹耕。 |
24.form標簽對之間,可以出現(xiàn)p荠雕、ul等非表單域元素嗎稳其?可以
25.bind、apply炸卑、call三者區(qū)別既鞠?用apply實現(xiàn)bind
[圖片上傳失敗...(image-4b343b-1580399293051)]
26.畫一下原型鏈,prototype
和__proto__
有什么區(qū)別
[圖片上傳失敗...(image-997059-1580399293051)]
prototype
和__proto__
有什么區(qū)別:
__proto__
指向的是這個對象的構(gòu)造函數(shù)的prototype盖文;A函數(shù)的構(gòu)造函數(shù)是Object
27.刪除表格的第一行有哪些方法
方法一:removeChild
var tds = table.getElementsByTagName("td");
var tr = tds[0].parentNode;
tr.parentNode.removeChild(tr);
方法二:deleteRow
var trs = document.getElementsByTagName("tr");
var table = document.getElementById("table");
table.deleteRow(0);//刪除第一行
方法三:jQuery:remove嘱蛋、detach、empty
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
function delRow(row_id){
$(row_id).remove(); //方法1五续,刪移除元素及它的數(shù)據(jù)和事件
$(row_id).detach(); //方法2洒敏,移除被選元素,包括所有的文本和子節(jié)點疙驾。然后它會保留數(shù)據(jù)和事件凶伙。
$(del).empty(); //方法3,從被選元素移除內(nèi)容
}
方法四:樣式
var tds = table.getElementsByTagName("td");
var tr = tds[0].parentNode;
tr.style.display='none';//方法1它碎,不保留原有位置
tr.style.opacity=0;//方法2函荣,保留原有位置
tr.style.visibility='hidden';//方法3显押,保留原有位置
28.jQuery有哪些方法可以選擇一個表格中的第一行
$("#id") //id=“id”的元素
$(".class") //class=“class”的元素
$("p") //所有<p>元素
$(".intro.demo") //所有 class="intro" 且 class="demo" 的元素
$("p:first") //第一個<p>元素
$("p:last") //最后一個<p>元素
$("tr:even") //所有偶數(shù)<tr>元素
$("tr:odd") //所有奇數(shù)<tr>元素
$("ul li:eq(3)") //列表中的第四個元素(index從0開始)
$("ul li:gt(3)") //列出index大于3的元素
$("ul li:lt(3)") //列出index小于3的元素
$("[href]") //所有帶有href屬性的元素
$(":input") //所有<input>元素
$(":checked") //所有被選中的input元素
29.扁平化一個嵌套的數(shù)組
let arrays = [1, [2, [3, [4, 5]]], 6];
需求:多維數(shù)組=>一維數(shù)組
第一種方法:直接調(diào)用arr的flat方法
arr = arrays.flat(Infinity);//[1, 2, 3, 4, 5, 6]
第二種方法:正則表達式
let str = JSON.stringify(arrays);
arr = str.replace(/(\]|\[)/g, '').split(',');//["1", "2", "3", "4", "5", "6"]
第三種:正則表達式
str = str.replace(/(\[|\])/g, '');
str = '[' + str + ']';
ary = JSON.parse(str);//[1, 2, 3, 4, 5, 6]
第四種:遞歸
let result = [];
function fn(array) {
for (let i = 0; i < array.length; i++) {
let item = array[i];
if (Array.isArray(array[i])) {
fn(item);
} else {
result.push(item);
}
}
}
fn(arrays)
第五種:reduce
const flatten = (arr) => {
return arr.reduce((prev, next) => {
return prev.concat(Object.prototype.toString.call(next) === '[object Array]' ? flatten(next): next)
}, [])
}
第六種:…擴展運算符
while (arrays.some(Array.isArray)) {
arrays = [].concat(...arrays);
}//[1, 2, 3, 4, 5, 6]