一、js的基本類型有哪些?引用類型有哪些?null和undefined的區(qū)別?
1、基本類型
Number,String,Undefined,Null,Boolean
2、引用類型
Object,Function
3、null和undefined的區(qū)別
Undefined類型只有一個值,即undefined。當(dāng)聲明的變量還未被初始化時,變量的默認值為undefined。
Null類型也只有一個值,即null。null用來表示尚未存在的對象,常用來表示函數(shù)企圖返回一個不存在的對象。
二、如何判斷一個變量是Array類型?如何判斷一個變量是Number類型?(都不止一種)
1、方法一: instanceof
instanceof 用于判斷一個變量是否某個對象的實例,左邊操作數(shù)是一個對象,右邊操作數(shù)是一個函數(shù)對象或者函數(shù)構(gòu)造器。原理是通過判斷左操作數(shù)的對象的原型鏈上是否具有右操作數(shù)的構(gòu)造函數(shù)的prototype屬性
var arr=[];
arr instanceof Array
2、constructor
constructor 屬性返回對創(chuàng)建此對象的數(shù)組函數(shù)的引用,就是返回對象相對應(yīng)的構(gòu)造函數(shù)。
[].constructor === Array
3、 Object.prototype.toString.call(arr) === “[object Array]”
Object.prototype.toString.call(o) === ‘[object Array]‘;
二、JS常見的dom操作api
1、Node
Node是一個接口,中文叫節(jié)點,很多類型的DOM元素都是繼承于它,都共享著相同的基本屬性和方法。常見的Node有 element,text,attribute,comment,document 等(所以要注意 節(jié)點 和 元素 的區(qū)別,元素屬于節(jié)點的一種)。
Node有一個屬性 nodeType 表示Node的類型,它是一個整數(shù),其數(shù)值分別表示相應(yīng)的Node類型
ELEMENT_NODE: 1, // 元素節(jié)點
ATTRIBUTE_NODE: 2, // 屬性節(jié)點
TEXT_NODE: 3, // 文本節(jié)點
COMMENT_NODE: 8, // 注釋節(jié)點
DOCUMENT_NODE: 9, // 文檔
DOCUMENT_FRAGMENT_NODE: 11, // 文檔碎片
2、NodeList
NodeList 對象是一個節(jié)點的集合,一般由 Node.childNodes 、 document.getElementsByName 和 document.querySelectorAll 返回的。
不過需要注意, Node.childNodes 、 document.getElementsByName 返回的 NodeList 的結(jié)果是實時的(此時跟HTMLCollection比較類似),而 document.querySelectorAll 返回的結(jié)果是固定的
3、HTMLCollection
HTMLCollection是一個特殊的NodeList,表示包含了若干元素(元素順序為文檔流中的順序)的通用集合,它是實時更新的,當(dāng)其所包含的元素發(fā)生改變時,它會自動更新。另外,它是一個偽數(shù)組,如果想像數(shù)組一樣操作它們需要像 Array.prototype.slice.call(nodeList, 2) 這樣調(diào)用。
4、節(jié)點查找API
document.getElementById :
根據(jù)ID查找元素,大小寫敏感,如果有多個結(jié)果,只返回第一個;
document.getElementsByClassName :
根據(jù)類名查找元素,多個類名用空格分隔,返回一個 HTMLCollection 。注意兼容性為IE9+(含)。另外,不僅僅是document,其它元素也支持 getElementsByClassName 方法;
document.getElementsByTagName :
根據(jù)標(biāo)簽查找元素, * 表示查詢所有標(biāo)簽,返回一個 HTMLCollection 。
document.getElementsByName :
根據(jù)元素的name屬性查找,返回一個 NodeList 。
document.querySelector :
返回單個Node,IE8+(含),如果匹配到多個結(jié)果,只返回第一個。
document.querySelectorAll :
返回一個 NodeList ,IE8+(含)。
document.forms :
獲取當(dāng)前頁面所有form,返回一個 HTMLCollection ;
5、節(jié)點創(chuàng)建API
createElement
通過 createElement 創(chuàng)建的元素并不屬于 document 對象,它只是創(chuàng)建出來,并未添加到html文檔中,要調(diào)用 appendChild 或 insertBefore 等方法將其添加到HTML文檔中。
createTextNode
創(chuàng)建文本節(jié)點
cloneNode
克隆一個節(jié)點: node.cloneNode(true/false) ,它接收一個bool參數(shù),用來表示是否復(fù)制子元素
克隆節(jié)點并不會克隆事件,除非事件是用 <div onclick="test()"></div> 這種方式綁定的,用 addEventListener 和 node.onclick=xxx; 方式綁定的都不會復(fù)制。
createDocumentFragment
本方法用來創(chuàng)建一個 DocumentFragment ,也就是文檔碎片,它表示一種輕量級的文檔,主要是用來存儲臨時節(jié)點,大量操作DOM時用它可以大大提升性能。
6、節(jié)點修改AP
appendChild
parent.appendChild(child);
它會將child追加到parent的子節(jié)點的最后面。另外,如果被添加的節(jié)點是一個頁面中存在的節(jié)點,則執(zhí)行后這個節(jié)點將會添加到新的位置,其原本所在的位置將移除該節(jié)點,也就是說不會同時存在兩個該節(jié)點在頁面上,且其事件會保留。
insertBefore
parentNode.insertBefore(newNode, refNode);
將某個節(jié)點插入到另外一個節(jié)點的前面
關(guān)于第二個參數(shù):
refNode是必傳的,如果不傳該參數(shù)會報錯;
如果refNode是undefined或null,則insertBefore會將節(jié)點添加到末尾;
removeChild
var deletedChild = parent.removeChild(node)
removeChild用于刪除指定的子節(jié)點并返回子節(jié)點
deletedChild指向被刪除節(jié)點的引用,它仍然存在于內(nèi)存中,可以對其進行下一步操作。另外,如果被刪除的節(jié)點不是其子節(jié)點,則將會報錯
replaceChild
parent.replaceChild(newChild, oldChild);
replaceChild用于將一個節(jié)點替換另一個節(jié)點
7、節(jié)點關(guān)系A(chǔ)PI
父關(guān)系A(chǔ)PI
parentNode :
每個節(jié)點都有一個parentNode屬性,它表示元素的父節(jié)點。Element的父節(jié)點可能是Element,Document或DocumentFragment;
parentElement :
返回元素的父元素節(jié)點,與parentNode的區(qū)別在于,其父節(jié)點必須是一個Element元素,如果不是,則返回null;
子關(guān)系A(chǔ)PI
children :
返回一個實時的 HTMLCollection ,子節(jié)點都是Element,IE9以下瀏覽器不支持;
childNodes :
返回一個實時的 NodeList ,表示元素的子節(jié)點列表,注意子節(jié)點可能包含文本節(jié)點、注釋節(jié)點等;
firstChild :
返回第一個子節(jié)點,不存在返回null,與之相對應(yīng)的還有一個 firstElementChild ;
lastChild :
返回最后一個子節(jié)點,不存在返回null,與之相對應(yīng)的還有一個 lastElementChild ;
兄弟關(guān)系型API
previousSibling :
節(jié)點的前一個節(jié)點,如果不存在則返回null。注意有可能拿到的節(jié)點是文本節(jié)點或注釋節(jié)點,與預(yù)期的不符,要進行處理一下。
nextSibling :
節(jié)點的后一個節(jié)點,如果不存在則返回null。注意有可能拿到的節(jié)點是文本節(jié)點,與預(yù)期的不符,要進行處理一下。
previousElementSibling :
返回前一個元素節(jié)點,前一個節(jié)點必須是Element,注意IE9以下瀏覽器不支持。
nextElementSibling :
返回后一個元素節(jié)點,后一個節(jié)點必須是Element,注意IE9以下瀏覽器不支持。
8、元素屬性型API
setAttribute
給元素設(shè)置屬性:
element.setAttribute(name, value);
其中name是特性名,value是特性值。如果元素不包含該特性,則會創(chuàng)建該特性并賦值。
getAttribute
getAttribute返回指定的特性名相應(yīng)的特性值,如果不存在,則返回null:
var value = element.getAttribute("id");
9、樣式相關(guān)API
直接修改元素的樣式
elem.style.color = 'red';
elem.style.setProperty('font-size', '16px');
elem.style.removeProperty('color');
動態(tài)添加樣式規(guī)則
var style = document.createElement('style');
style.innerHTML = 'body{color:red} #top:hover{background-color: red;color: white;}';
document.head.appendChild(style);
window.getComputedStyle
通過 element.sytle.xxx 只能獲取到內(nèi)聯(lián)樣式,借助 window.getComputedStyle 可以獲取應(yīng)用到元素上的所有樣式,IE8或更低版本不支持此方法。
var style = window.getComputedStyle(element[, pseudoElt]);
getBoundingClientRect
getBoundingClientRect 用來返回元素的大小以及相對于瀏覽器可視窗口的位置,用法如下:
var clientRect = element.getBoundingClientRect();
clientRect是一個 DOMRect 對象,包含width、height、left、top、right、bottom,它是相對于窗口頂部而不是文檔頂部,滾動頁面時它們的值是會發(fā)生變化的。
參考:
JS中常見原生DOM操作API【總結(jié)整理)
三、解釋一下事件冒泡和事件捕獲
事件捕獲指的是從document到觸發(fā)事件的那個節(jié)點,即自上而下的去觸發(fā)事件。
相反的,事件冒泡是自下而上的去觸發(fā)事件。
綁定事件方法的第三個參數(shù),就是控制事件觸發(fā)順序是否為事件捕獲。
true,事件捕獲;false,事件冒泡。默認false,即事件冒泡。
四、事件委托(手寫例子),事件冒泡和捕獲,如何阻止冒泡?如何組織默認事件?
<ul id="ul1">
<li></li><li></li><li></li>
</ul>
window.onload = function(){
var oUl = document.getElementById("ul1");
oUl.onclick = function(){
alert(123);
}
}
用父級ul做事件處理,當(dāng)li被點擊時,由于冒泡原理,事件就會冒泡到ul上,因為ul上有點擊事件,所以事件就會觸發(fā)
2、阻止冒泡和默認行為
2.1、event.stopPropagation()方法
這是阻止事件的冒泡方法,不讓事件向documen上蔓延,但是默認事件任然會執(zhí)行,當(dāng)你掉用這個方法的時候,如果點擊一個連接,這個連接仍然會被打開,
2.2、event.preventDefault()方法
這是阻止默認事件的方法,調(diào)用此方法是,連接不會被打開,但是會發(fā)生冒泡,冒泡會傳遞到上一層的父元素;
2.3、return false
這個方法比較暴力,他會阻止事件冒泡也會阻止默認事件;寫上此代碼,連接不會被打開,事件也不會傳遞到上一層的父元素;可以理解為return false就等于同時調(diào)用了event.stopPropagation()和event.preventDefault()
五、對閉包的理解?什么時候構(gòu)成閉包?閉包的實現(xiàn)方法?閉包的優(yōu)缺點?
function f1(){
var n=999;
var f2 = function f2(m){
return n + m;
}
return f2;
}
var fn = f1()
console.log(fn(1)); // 1000
1、由于在javascript中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,所以說,閉包可以簡單理解成“定義在一個函數(shù)內(nèi)部的函數(shù)“。
閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數(shù)內(nèi)部的變量,另一個就是讓這些變量的值始終保持在內(nèi)存中,不會在f1調(diào)用后被自動清除。
2、為什么會這樣呢?原因就在于f1是f2的父函數(shù),而f2被賦給了一個全局變量,這導(dǎo)致f2始終在內(nèi)存中,而f2的存在依賴于f1,因此f1也始終在內(nèi)存中,不會在調(diào)用結(jié)束后,被垃圾回收機制(garbage collection)回收。
六、this有哪些使用場景?跟C,Java中的this有什么區(qū)別?如何改變this的值?
1、this 指向調(diào)用該方法的對象
有五種情況
- 情況一:全局 & 調(diào)用普通函數(shù)
在全局環(huán)境中,this 永遠指向 window。 - 情況二:構(gòu)造函數(shù)
如果函數(shù)作為構(gòu)造函數(shù)使用,那么其中的 this 就代表它即將 new 出來的對象 - 情況三:對象方法
如果函數(shù)作為對象的方法時,方法中的 this 指向該對象
注意:若是在對象方法中定義函數(shù),那么this 指向 window - 情況四:構(gòu)造函數(shù) prototype 屬性
function Foo(){
this.x = 10;
}
Foo.prototype.getX = function () {
console.log(this); //Foo {x: 10, getX: function}
console.log(this.x); //10
}
var foo = new Foo();
foo.getX();
在 Foo.prototype.getX 函數(shù)中,this 指向的 foo 對象。不僅僅如此,即便是在整個原型鏈中,this 代表的也是當(dāng)前對象的值。
- 情況五:函數(shù)用 call、apply或者 bind 調(diào)用。
var obj = {
x: 10
}
function foo(){
console.log(this); //{x: 10}
console.log(this.x); //10
}
foo.call(obj);
foo.apply(obj);
foo.bind(obj)();
當(dāng)一個函數(shù)被 call、apply 或者 bind 調(diào)用時,this 的值就取傳入的對象的值。
- 情況六:DOM event this
在一個 HTML DOM 事件處理程序里,this 始終指向這個處理程序所綁定的 HTML DOM 節(jié)點
function Listener(){
document.getElementById('foo').addEventListener('click', this.handleClick);
//這里的 this 指向 Listener 這個對象。不是強調(diào)的是這里的 this
}
Listener.prototype.handleClick = function (event) {
console.log(this); //<div id="foo"></div>
}
var listener = new Listener();
document.getElementById('foo').click();
箭頭函數(shù)內(nèi)部的 this 是詞法作用域,由上下文確定
var obj = {
x: 10,
foo: function() {
var fn = () => {
return () => {
return () => {
console.log(this); //Object {x: 10}
console.log(this.x); //10
}
}
}
fn()()();
}
}
obj.foo();
this 總是指向詞法作用域,也就是外層調(diào)用者 obj。
就不再需要了
var obj = {
x: 10,
foo: function() {
var fn = () => {
return () => {
return () => {
console.log(this); // Object {x: 10}
console.log(this.x); //10
}
}
}
fn.bind({x: 14})()()();
fn.call({x: 14})()();
}
}
obj.foo();
七、call,apply,bind
1、bing 返回的是一個新的函數(shù),你必須調(diào)用它才會被執(zhí)行
obj.myFun.call(db);
obj.myFun.apply(db);
obj.myFun.bind(db)();
2、call 、bind 、 apply 這三個函數(shù)的第一個參數(shù)都是 this 的指向?qū)ο?/p>
2.1、call的參數(shù)是直接放進去的,第二第三第n個參數(shù)全都用逗號分隔,直接放到后面
obj.myFun.call(db,'1', ... ,'string' );
2.2、apply的所有參數(shù)都必須放在一個數(shù)組里面?zhèn)鬟M去
obj.myFun.apply(db,['1', ..., 'string' ]);
2.3、bind除了返回是函數(shù)以外,它 的參數(shù)和call 一樣。
八、顯示原型和隱式原型,手繪原型鏈,原型鏈?zhǔn)鞘裁矗繛槭裁匆性玩?/p>
方法的原型(Function.prototype)是對象,
對象具有屬性(proto)稱為隱式原型,
對象的隱式原型指向構(gòu)造該對象的構(gòu)造函數(shù)的顯式原型。
注意:
通過Function.prototype.bind方法構(gòu)造出來的函數(shù)沒有prototype屬性。
Object.prototype.這個對象的是個例外,它的proto值為null。
二者的關(guān)系
隱式原型指向創(chuàng)建這個對象的函數(shù)的prototype
首先我們來看如何創(chuàng)建一個對象
a.通過對象字面量的方式。
var person={
name:"Tom"
}
通過對象字面量的方式創(chuàng)建的對象他的隱式原型指向Object.prototype。
b.通過new的方式創(chuàng)建
//創(chuàng)建一個構(gòu)造函數(shù)
function person(name){
this.name=name
}
//創(chuàng)建一個構(gòu)造函數(shù)的實例
var person1=new person;
構(gòu)造函數(shù)function person本質(zhì)上是由Function構(gòu)造函數(shù)創(chuàng)建的,它是Function的一個實例。原型對象本質(zhì)上是由Object構(gòu)造函數(shù)創(chuàng)建的。內(nèi)置函數(shù)Array Number等也是有Function構(gòu)造函數(shù)創(chuàng)建的。
c.通過Object.creat()方式創(chuàng)建
var person={
name:"Tom"
}
const me = Object.create(person);
其中通過Object.creat(person)創(chuàng)建出來的對象他的隱式原型指向person。
但是本質(zhì)上3種方法都是通過new的方式創(chuàng)建的。


九、實現(xiàn)繼承的多種方式和優(yōu)缺點
參考:
javascript 中各種繼承方式的優(yōu)缺點
十、new 一個對象具體做了什么
使用關(guān)鍵字new創(chuàng)建新實例對象經(jīng)過了以下幾步:
1、創(chuàng)建一個新對象,如:var person = {};
2、新對象的proto屬性指向構(gòu)造函數(shù)的原型對象。
3、將構(gòu)造函數(shù)的作用域賦值給新對象。(也所以this對象指向新對象)
4、執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼,將屬性添加給person中的this對象。
5、返回新對象person。
十一、手寫Ajax,XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
// 通信成功時,狀態(tài)值為4
if (xhr.readyState === 4){
if (xhr.status === 200){
console.log(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.onerror = function (e) {
console.error(xhr.statusText);
};
xhr.open('GET', '/endpoint', true);
xhr.send(null);
0,表示 XMLHttpRequest 實例已經(jīng)生成,但是實例的open()方法還沒有被調(diào)用。
1,表示open()方法已經(jīng)調(diào)用,但是實例的send()方法還沒有調(diào)用,仍然可以使用實例的setRequestHeader()方法,設(shè)定 HTTP 請求的頭信息。
2,表示實例的send()方法已經(jīng)調(diào)用,并且服務(wù)器返回的頭信息和狀態(tài)碼已經(jīng)收到。
3,表示正在接收服務(wù)器傳來的數(shù)據(jù)體(body 部分)。這時,如果實例的responseType屬性等于text或者空字符串,responseText屬性就會包含已經(jīng)收到的部分信息。
4,表示服務(wù)器返回的數(shù)據(jù)已經(jīng)完全接收,或者本次接收已經(jīng)失敗。
十二、指出JS的宿主對象和原生對象的區(qū)別,為什么擴展JS內(nèi)置對象不是好的做法?
宿主對象
“宿主”當(dāng)然就是我們網(wǎng)頁的運行環(huán)境,即“操作系統(tǒng)”和“瀏覽器”。
所有的BOM和DOM對象都是宿主對象。
原生對象
獨立于宿主環(huán)境的ECMAScript實現(xiàn)提供的對象。與宿主無關(guān),在javascript(遠景瀏覽器)、nodejs(node平臺)、jscript(ie瀏覽器)、typescript(微軟平臺)等等中均有這些對象。
十三、document load和document DOMContentLoaded兩個事件的區(qū)別?
- 當(dāng) onload 事件觸發(fā)時,頁面上所有的DOM,樣式表,腳本,圖片,flash都已經(jīng)加載完成了。
- 當(dāng) DOMContentLoaded 事件觸發(fā)時,僅當(dāng)外部腳本,樣式表文件,DOM樹完成,不包括圖片,flash。
DOM文檔加載的步驟為
- 解析HTML結(jié)構(gòu)。
- 加載外部腳本和樣式表文件。
- 解析并執(zhí)行腳本代碼。
- DOM樹構(gòu)建完成。//DOMContentLoaded
- 加載圖片等外部文件。
- 頁面加載完畢。//load
在第4步,會觸發(fā)DOMContentLoaded事件。在第6步,觸發(fā)load事件。
十四、typeof能夠得到哪些值?
number, boolean, string, undefined, object, function.
十五、什么是“use strict”,好處和壞處
設(shè)立"嚴(yán)格模式"的優(yōu)點:
1. 消除Javascript語法的一些不合理、不嚴(yán)謹(jǐn)之處,減少一些怪異行為;
2. 消除代碼運行的一些不安全之處,保證代碼運行的安全;
3. 提高編譯器效率,增加運行速度;
4. 為未來新版本的Javascript做好鋪墊。
注:經(jīng)過測試 IE6,7,8,9 均不支持嚴(yán)格模式。
缺點:
現(xiàn)在網(wǎng)站的 JS 都會進行壓縮,一些文件用了嚴(yán)格模式,而另一些沒有。這時這些本來是嚴(yán)格模式的文件,被 merge 后,這個串就到了文件的中間,不僅沒有指示嚴(yán)格模式,反而在壓縮后浪費了字節(jié)。
具體優(yōu)點
1.使調(diào)試更加容易。那些被忽略或默默失敗了的代碼錯誤,會產(chǎn)生錯誤或拋出異常,因此盡早提醒你代碼中的問題,你才能更快地指引到它們的源代碼。
2.變量在賦值之前必須聲明,防止意外的全局變量。如果沒有嚴(yán)格模式,將值分配給一個未聲明的變量會自動創(chuàng)建該名稱的全局變量。這是JavaScript中最常見的錯誤之一。在嚴(yán)格模式下,這樣做的話會拋出錯誤。
3.取消this值的強制轉(zhuǎn)換。如果沒有嚴(yán)格模式,引用null或未定義的值到 this 值會自動強制到全局變量。在嚴(yán)格模式下,引用 null或未定義的 this 值會拋出錯誤。嚴(yán)格模式下,this不會指向window
4.不允許重復(fù)的屬性名稱或參數(shù)值。當(dāng)檢測到對象中重復(fù)命名的屬性,例如:
var object = {foo: "bar", foo: "baz"};
或檢測到函數(shù)中重復(fù)命名的參數(shù)時,例如:
function foo(val1, val2, val1){})
嚴(yán)格模式會拋出錯誤,因此捕捉幾乎可以肯定是代碼中的bug可以避免浪費大量的跟蹤時間。
5.使 eval() 更安全。在嚴(yán)格模式和非嚴(yán)格模式下, eval() 的行為方式有所不同。最顯而易見的是,在嚴(yán)格模式下,變量和聲明在 eval() 語句內(nèi)部的函數(shù)不會在包含范圍內(nèi)創(chuàng)建(它們會在非嚴(yán)格模式下的包含范圍中被創(chuàng)建,這也是一個常見的問題源)。
6.在 delete 使用無效時拋出錯誤。 delete 操作符(用于從對象中刪除屬性)不能用在對象不可配置的屬性上。當(dāng)試圖刪除一個不可配置的屬性時,非嚴(yán)格代碼將默默地失敗,而嚴(yán)格模式將在這樣的情況下拋出異常。
7.嚴(yán)格模式去除了with語句
8.不能修改arguments ,不能在函數(shù)內(nèi)定義arguments變量 ,不能使用arugment.caller和argument.callee。因此如果你要引用匿名函數(shù),需要對匿名函數(shù)命名。
十六、函數(shù)的作用域是什么?js 的作用域有幾種?
var 函數(shù)作用域
let,const塊作用域
十七、JS如何實現(xiàn)重載和多態(tài)
- 重載:
重載可認為是靜態(tài)的多態(tài),靜態(tài)聯(lián)編,發(fā)生在編譯階段;
重載就是一組具有相同名字、不同參數(shù)列表的函數(shù)(方法)。
重載,函數(shù)特征之一,表現(xiàn)為在一個類中同名不同參的方法分別被調(diào)用會產(chǎn)生不同的結(jié)果。
- 多態(tài):
多態(tài)是動態(tài)的,動態(tài)聯(lián)編,發(fā)生在運行階段;
多態(tài),面向?qū)ο筇卣髦?,表現(xiàn)為不同對象調(diào)用相同方法會產(chǎn)生不同的結(jié)果??梢岳斫庖粋€方法被不同實現(xiàn)后 展現(xiàn)不同的效果及狀態(tài)。
靜態(tài)的比動態(tài)的效率高,但動態(tài)的最大優(yōu)點是多態(tài)性,提高代碼復(fù)用性。
十八、原生事件綁定(跨瀏覽器),dom0和dom2的區(qū)別?
- DOM0級事件處理程序(屬性綁定,兼容性好)
btn.onclick = 事件函數(shù)
btn.onclick = null; //刪除事件處理程序
this指向當(dāng)前元素
- DOM2級事件處理程序(函數(shù)綁定,兼容性不好)(IE9及IE9以上)
addEventListener()
removeEventListener()
參數(shù):
要綁定的事件名
作為事件處理的函數(shù)
布爾值:true在捕獲階段調(diào)用事件處理程序;false在冒泡階段調(diào)用
- IE事件處理程序
attachEvent()
detachEvent()
事件處理程序都被添加到冒泡階段
4.區(qū)別:
如果定義了兩個dom0級事件,dom0級事件會覆蓋
dom2不會覆蓋,會依次執(zhí)行
dom0和dom2可以共存,不互相覆蓋,但是dom0之間依然會覆蓋
十九、給定一個元素獲取它相對于視圖窗口的坐標(biāo)
document.getElementById("ele").getBoundingClientRect()
二十、如何實現(xiàn)圖片滾動懶加載
js
var temp = "<ul class='container'>";
for (var i = 0; i < 10; i++){
temp += `<li><img src="${i}.png"/></li>`
}
for (var i = 0; i < 10; i++){
temp += "<li><img data-src='loading.png'/></li>"
}
temp += "</ul>"
document.body.innerHTML = temp;
var url = "http://192.168.0.0/page"
var isCanReq = true;
var urlArr = [];
var xhr = new XMLHttpRequest();
var pageNo = 2;
var pageSize = 10;
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr,status === 200) {
urlArr = JSON.parse(xhr.responseText);
var newtemp = "";
for (var i = 0; i < 10; i++){
newtemp += `<li><img data-src="${urlArr[i]}"/></li>`
}
}
}
}
document.getElementsByClassName("container")[0].addEventListener("scroll",(event) => {
var target = event.target;
if (target.scrollHeight - target.scrollTop < 100 && isCanReq){
isCanReq = false;
xhr.open("post", `${url}/${pageNo}/${pageSize}`, true);
var elesChildren = document.getElementsByClassName("container")[0].children;
for (var i = 10;i > 0;i++){
elesChildren[elesChildren.length - i].setAttribute("src", elesChildren[elesChildren.length - i].dataset.url)
}
xhr.send(null);
pageNo++;
}
})
css
ul > li {
margin:10px auto;
width:100px;
height:100px;
}
img {
width:100%;
height:100%;
}
二十一、js 的深拷貝
functon clone (val) {
if (JSON && JSON.parse){
return JSON.parse(JSON.stringify(val))
}
let res;
if (typeof val === "object") {
if (val === null) {
res = null;
} else if (val instanceof Array) {
res = [];
for (let i = 0;i < val.length;i++) {
res.push(clone(val[i]))
}
} else if (val instanceof Object) {
res = {};
for (const key in val) {
res[key] = clone(val[key]);
}
}
} else {
res = val;
}
return res;
}
二十二、web端cookie的設(shè)置和獲取
var duration = +new Date() + (30 * 24 * 60 * 60 * 1000),
function setCookie(name, value) {
const me = this;
const exp = new Date(me.duration);
document.cookie = `${name}=${escape(value)};expires=${exp.toGMTString()}`;
}
function getCookie(name) {
if (!name) return "";
const reg = new RegExp(`(^|)${name}=([^;]*)(;|$)`);
const arr = document.cookie.match(reg);
if (arr) {
return decodeURI(arr[2]);
}
return "";
}
二十三、setTimeout和promise的執(zhí)行順序
先執(zhí)行同步代碼,遇到異步代碼就先加入隊列,然后按入隊的順序執(zhí)行異步代碼,最后執(zhí)行setTimeout隊列的代碼。
二十四、navigator對象,location和history
Location 對象方法
屬性 描述
assign() 加載新的文檔。
reload() 重新加載當(dāng)前文檔。
replace() 用新的文檔替換當(dāng)前文檔。
History 對象方法
方法 描述
back() 加載 history 列表中的前一個 URL。
forward() 加載 history 列表中的下一個 URL。
go()
二十五、js的垃圾回收機制
1、標(biāo)記清除
這是javascript中最常用的垃圾回收方式。當(dāng)變量進入執(zhí)行環(huán)境是,就標(biāo)記這個變量為“進入環(huán)境”。從邏輯上講,永遠不能釋放進入環(huán)境的變量所占用的內(nèi)存,因為只要執(zhí)行流進入相應(yīng)的環(huán)境,就可能會用到他們。當(dāng)變量離開環(huán)境時,則將其標(biāo)記為“離開環(huán)境”。
2、引用計數(shù)
另一種不太常見的垃圾回收策略是引用計數(shù)。引用計數(shù)的含義是跟蹤記錄每個值被引用的次數(shù)。當(dāng)聲明了一個變量并將一個引用類型賦值給該變量時,則這個值的引用次數(shù)就是1。相反,如果包含對這個值引用的變量又取得了另外一個值,則這個值的引用次數(shù)就減1。當(dāng)這個引用次數(shù)變成0時,則說明沒有辦法再訪問這個值了,因而就可以將其所占的內(nèi)存空間給收回來。這樣,垃圾收集器下次再運行時,它就會釋放那些引用次數(shù)為0的值所占的內(nèi)存。
這樣的相互引用如果說很大量的存在就會導(dǎo)致大量的內(nèi)存泄露。
二十六、內(nèi)存泄漏的原因和場景
雖然JavaScript 會自動垃圾收集,但是如果我們的代碼寫法不當(dāng),會讓變量一直處于“進入環(huán)境”的狀態(tài),無法被回收
1、給DOM對象添加的屬性是一個對象的引用。范例:
var MyObject = {};
document.getElementById('myDiv').myProp = MyObject;
解決方法:
在window.onunload事件中寫上: document.getElementById('myDiv').myProp = null;
2、給DOM對象用attachEvent綁定事件。范例:
function doClick() {}
element.attachEvent("onclick", doClick);
解決方法:
在onunload事件中寫上: element.detachEvent('onclick', doClick);
二十七、DOM事件中target和currentTarget的區(qū)別
target在事件流的目標(biāo)階段;
currentTarget在事件流的捕獲,目標(biāo)及冒泡階段。
只有當(dāng)事件流處在目標(biāo)階段的時候,兩個的指向才是一樣的, 而當(dāng)處于捕獲和冒泡階段的時候,target指向被單擊的對象而currentTarget指向當(dāng)前事件活動的對象(注冊該事件的對象)(一般為父級)。this指向永遠和currentTarget指向一致(只考慮this的普通函數(shù)調(diào)用)。
typeof 和 instanceof 區(qū)別,instanceof原理
typeof
用以獲取一個變量的類型,typeof一般只能返回如下幾個結(jié) 果:number,boolean,string,function,object,undefined
instanceof
用于 判斷一個變量是否某個構(gòu)造函數(shù)的實例
instanceof原理
檢測對象A是不是另一個對象B的實例的原理是:查看對象B的prototype屬性指向的原型對象是否在對象A的原型鏈上,若在則返回true,若不在則返回false。
obj.__proto__ === Object.prototype
二十八、js處理異常
try {
} catch (err) {
} finally{
}
二十九、js的設(shè)計模式知道那些
創(chuàng)建型
- 單體模式
單體模式思想在于保證一個特定類僅有一個實例,意味著當(dāng)你第二次使用同一個類創(chuàng)建信對象時,應(yīng)得到和第一次創(chuàng)建對象完全相同
var Universe;
(function(){
var instance;
Universe=function Universe(){
if(instance){
return instance;
}
instance=this;
this.xx="xx";
}
})();
var uni = new Universe();
Universe.prototype.a = 1
var uni2 = new Universe();
console.log(uni === uni2) //true
console.log(uni.a) //1
console.log(uni2.a) //1
console.log(uni.constructor === Universe); //true
- 工廠模式
工廠模式是為了創(chuàng)建對象。
例子
公共構(gòu)造函數(shù) CarMaker
名為factory的CarMaker靜態(tài)方法來創(chuàng)建car對象
function CarMaker() {}
CarMaker.prototype.drive = function() {
return "I have " + this.doors + " doors";
}
CarMaker.compact = function() {
this.doors = 4;
}
CarMaker.convertible = function() {
this.doors = 2
}
CarMaker.suv = function() {
this.doors = 6;
}
CarMaker.factory = function(type) {
if (typeof CarMaker[type] !== "function") {
throw "Error"
}
if (typeof CarMaker[type].prototype.drive !== "function") {
CarMaker[type].prototype = new CarMaker();
}
var newCar = new CarMaker[type]();
return newCar;
}
var corolla = CarMaker.factory('compact');
console.log(corolla.drive()); //I have 4 doors
var WarriorFactory = function() {},
MageFactory = function() {},
ArcherFactory = function() {};
WarriorFactory.prototype.createCharacter = function() {
return new Warrior();
};
MageFactory.prototype.createCharacter = function() {
return new Mage();
};
ArcherFactory.prototype.createCharacter = function() {
return new Archer();
};
Player.prototype.play = function(role) {
var factory, character;
switch (role) {
case "戰(zhàn)士":
factory = new WarriorFactory();
break;
case "法師":
factory = new MageFactory();
break;
case "弓箭手":
factory = new ArcherFactory();
break;
default :
factory = new WarriorFactory();
}
character = factory.createCharacter();
character.level();
character.gather();
character.fight();
};
結(jié)構(gòu)型
- 裝飾者模式
可以在運行時候添加附加功能到對象中,他的一個方便特征在于其預(yù)期行為的可定制和可配置特性。
例子 假設(shè)在開發(fā)一個銷售商品的Web應(yīng)用,每一筆信銷售都是一個人新的 sale 對象。該對象“知道”有關(guān)項目的價格,并可以通過 getPrice() 方法返回加個。
根據(jù)不同情況,可以用額外的功能裝飾此對象。
假設(shè)客戶在魁北克省,買房需要支付聯(lián)邦稅和魁北克省稅,則此時需要調(diào)用聯(lián)邦稅裝飾者和魁北克省稅裝飾者。
function Sale(price) {
this.price = price;
this.decorateList = [];
}
Sale.decorators = {};
Sale.decorators.fedtax = {
getPrice: function(price) {
return price * 0.8; //對price進行處理
},
}
Sale.decorators.quebec = {
getPrice: function(price) {
return price * 0.7; //對price進行處理
},
}
Sale.decorators.money = {
getPrice: function(price) {
return "$" + price * 0.9; //對price進行處理
},
}
Sale.prototype.decorate = function(decorator) {
this.decorateList.push(decorator);
};
Sale.prototype.getPrice = function() {
var price = this.price;
this.decorateList.forEach(function(name) {
price = Sale.decorators[name].getPrice(price);
});
return price;
};
var sale = new Sale(100);
sale = sale.decorate("fedtax"); //聯(lián)邦稅
sale = sale.decorate("quebec"); //魁北克省稅
sale = sale.decorate("money"); //轉(zhuǎn)為美元格式
console.log(sale.getPrice()); //$50.4
行為型
- 迭代器模式
有一個包含某種數(shù)據(jù)集合的對象,該數(shù)據(jù)可能存儲在一個復(fù)雜數(shù)據(jù)結(jié)構(gòu)內(nèi)部,而要提供一個簡單方法訥訥感訪問到數(shù)據(jù)結(jié)構(gòu)中沒一個元素。
next() 下一個
hasNext() 是否有下一個
reWind() 重置指針
current() 返回當(dāng)前
var agg = (function() {
var index = 0;
var data = [1, 2, 3, 4, 5, 6];
var length = data.length;
return {
next: function() {
if (!this.hasNext()) {
return null;
}
var element = data[index];
index++;
return element;
},
hasNext: function() {
return index < length;
},
reWind: function() {
index = 0;
},
current: function() {
return data[index];
}
}
})();
while (agg.hasNext()) {
console.log(agg.next()); //1,2,3,4,5,6
}
agg.reWind(); //此時重置指針到0
- 策略模式
策略模式支持在運行時候選擇算法。例如用在表單驗證問題上,可以創(chuàng)建一個具有 validate() 方法的驗證器對象,無論表單具體類型是什么,該方法都會被調(diào)用,
并且返回結(jié)果或者錯誤信息。
var validator = {
// 所有可以的驗證規(guī)則處理類存放的地方,后面會單獨定義
types: {},
// 驗證類型所對應(yīng)的錯誤消息
messages: [],
// 當(dāng)然需要使用的驗證類型
config: {},
// 暴露的公開驗證方法
// 傳入的參數(shù)是 key => value對
validate: function (data) {
var i, msg, type, checker, result_ok;
// 清空所有的錯誤信息
this.messages = [];
for (i in data) {
if (data.hasOwnProperty(i)) {
type = this.config[i]; // 根據(jù)key查詢是否有存在的驗證規(guī)則
checker = this.types[type]; // 獲取驗證規(guī)則的驗證類
if (!type) {
continue; // 如果驗證規(guī)則不存在,則不處理
}
if (!checker) { // 如果驗證規(guī)則類不存在,拋出異常
throw {
name: "ValidationError",
message: "No handler to validate type " + type
};
}
result_ok = checker.validate(data[i]); // 使用查到到的單個驗證類進行驗證
if (!result_ok) {
msg = "Invalid value for *" + i + "*, " + checker.instructions;
this.messages.push(msg);
}
}
}
return this.hasErrors();
},
// helper
hasErrors: function () {
return this.messages.length !== 0;
}
};
//然后剩下的工作,就是定義types里存放的各種驗證類了
// 驗證給定的值是否不為空
validator.types.isNonEmpty = {
validate: function (value) {
return value !== "";
},
instructions: "傳入的值不能為空"
};
// 驗證給定的值是否是數(shù)字
validator.types.isNumber = {
validate: function (value) {
return !isNaN(value);
},
instructions: "傳入的值只能是合法的數(shù)字,例如:1, 3.14 or 2010"
};
// 驗證給定的值是否只是字母或數(shù)字
validator.types.isAlphaNum = {
validate: function (value) {
return !/[^a-z0-9]/i.test(value);
},
instructions: "傳入的值只能保護字母和數(shù)字,不能包含特殊字符"
};
//使用的時候,我們首先要定義需要驗證的數(shù)據(jù)集合,
//然后還需要定義每種數(shù)據(jù)需要驗證的規(guī)則類型,代碼如下:
var data = {
first_name: "Tom",
last_name: "Xu",
age: "unknown",
username: "TomXu"
};
validator.config = {
first_name: 'isNonEmpty',
age: 'isNumber',
username: 'isAlphaNum'
};
//最后獲取驗證結(jié)果
validator.validate(data);
if (validator.hasErrors()) {
console.log(validator.messages.join("\n"));
}
- 代理模式
在代理模式中,一個對象充當(dāng)另外一個對象的接口
var package = function(receiver) {
this.receiver = receiver;
}
var seller = function(package) {
this.package = package;
this.send = function(gift) {
return package.receiver + "你的包裹:" + gift;
}
}
var express = function(package) {
this.package = package;
this.send = function(packageName) {
return new seller(package).send(packageName);
}
}
//調(diào)用
var ems = new express(new package("gary"));
console.log(ems.send("鍵盤")); //gary你的包裹:鍵盤
- 中介者模式
中介者模式可以讓多個對象之間松耦合,并降低維護成本
例如:游戲程序,兩名玩家分別給與半分鐘時間來競爭決出勝負(誰按鍵的次數(shù)多勝出,這里玩家1按1,玩家2按0)
計分板(scoreboard)
中介者 (mediator)
中介者知道所有其他對象的信息。他與輸入設(shè)備(此時是鍵盤)進行通信并處理鍵盤上的按鍵時間,之后還將消息通知玩家。玩家玩游戲同時(每一分都更新自己分?jǐn)?shù))還要
通知中介者他所做的事情。中介者將更新后的分?jǐn)?shù)傳達給計分板。
// 玩家對象構(gòu)造函數(shù)
function Player(name) {
this.points = 0;
this.name = name;
}
Player.prototype.play = function() {
this.points += 1;
mediator.played();
};
// 中介者
var mediator = {
players: {}, //玩家對象
setup: function() { // 創(chuàng)建一個玩家home和一個玩家guest
var players = this.players;
players.home = new Player("home");
players.guest = new Player('guest');
},
played: function() { // 玩一次,就記錄一次玩家和分?jǐn)?shù)
var players = this.players;
var score = {
home: players.home.points,
guest: players.guest.points
}
},
keypress: function(e) { // 輸入鍵盤,記錄一次游戲,
//如果是1,記一分和游戲者home,如果是0,記一分和游戲者guest
e = e || window.event;
if (e.which === 49) { //or keycode 對應(yīng)按鍵 1
mediator.players.home.play();
return;
}
if (e.which === 48) { // 對應(yīng)按鍵 0
mediator.player.guest.play();
return;
}
},
}
//運行
mediator.setup(); // 創(chuàng)建一個中間者和一個玩家
window.onkeypress = mediator.keypress; // 在窗口綁定鍵盤keypress事件,
//每次輸入鍵盤,記錄一次游戲,如果是1,記一分和游戲者home,
//如果是0,記一分和游戲者guest
setTimeout(function() { //設(shè)置30秒游戲時間
window.onkeypress = null;
alert("game end");
}, 30000);
三十、輪播圖的實現(xiàn),以及輪播圖組件開發(fā),輪播10000張圖片過程
<div class="swiper-container">
<div class="swiper-content">
<div class="swiper-item"><img data-src="" src="" /></div>
</div>
</div>
JS
function initSwiper() {
const urlArr = new Array(10000); // 圖片URL數(shù)組
const eleContent = document.getElementsByClassName("swiper-content")[0];
let temp = "";
for (let i = 0;i < 10000;i++) {
temp += `<div class="swiper-item"><img data-src="${urlArr[i]}" src="" /></div>`;
}
eleContent.innerHTML = temp;
const eleChildren = eleContent.children;
let srcIndex = 0;
for (let i = 0;i < 3;i++) {
const eleImg = eleChildren[srcIndex].firstElementChild;
eleImg.setAttribute("src", eleImg.dataset.src);
srcIndex++;
}
setInterval(() => {
for (let i = 0;i < 3;i++) {
const eleImg = eleChildren[srcIndex].firstElementChild;
eleImg.setAttribute("src", eleImg.dataset.src);
srcIndex++;
}
}, 300);
startSwiper();
}
function startSwiper() {
const eleContent = document.getElementsByClassName("swiper-content")[0];
let marginLeftVal = +eleContent.style.marginLeft.replace(/px$/, "");
setInterval(() => {
marginLeftVal += 200;
eleContent.style.marginLeft = `${marginLeftVal}px`;
}, 200);
}
initSwiper()
CSS
.swiper-container {
width:200px;
height:100px;
overflow:hidden
}
.swiper-content {
display:flex;
}
.swiper-content.is-animated {
animation:margin-left 1s;
}
.swiper-item {
width:200px;
height:100px;
}
img {
width:100%;
height:100%;
}
requestAnimationFram
var eleDiv = document,getElementById("demo");
window.requestAnimatiion = window.requestAnimatiion ||
window.mozRequestAnimatiion ||
window.webkitRequestAnimatiion;
window.requestAnimationFrame(function fn(){
eleDiv.style.top = 1000 * Math.random() + "px";
window.requestAnimationFrame(fn);
})
三十一、websocket的工作原理和機制
WebSocket是一種雙向通信協(xié)議,在建立連接后,WebSocket服務(wù)器和Browser/Client Agent都能主動的向?qū)Ψ桨l(fā)送或接收數(shù)據(jù),就像Socket一樣;
WebSocket需要類似TCP的客戶端和服務(wù)器端通過握手連接,連接成功后才能相互通信。
相對于傳統(tǒng)的HTTP每次請求-應(yīng)答都需要客戶端與服務(wù)端建立連接的模式,WebSocket是類似Socket的TCP長連接的通訊模式,一旦WebSocket連接建立后,后續(xù)數(shù)據(jù)都以幀序列的形式傳輸。在客戶端斷開WebSocket連接或Server端斷掉連接前,不需要客戶端和服務(wù)端重新發(fā)起連接請求。在海量并發(fā)及客戶端與服務(wù)器交互負載流量大的情況下,極大的節(jié)省了網(wǎng)絡(luò)帶寬資源的消耗,有明顯的性能優(yōu)勢,且客戶端發(fā)送和接受消息是在同一個持久連接上發(fā)起,實時性優(yōu)勢明顯。
手指點擊可以觸控的屏幕時,是什么事件?
什么是函數(shù)柯里化?以及說一下JS的API有哪些應(yīng)用到了函數(shù)柯里化的實現(xiàn)?(函數(shù)柯里化一些了解,以及在函數(shù)式編程的應(yīng)用,最后說了一下JS中bind函數(shù)和數(shù)組的reduce方法用到了函數(shù)柯里化。)
JS代碼調(diào)試
pjax是什么呢?
pjax是一種基于ajax+history.pushState的新技術(shù),該技術(shù)可以無刷新改變頁面的內(nèi)容,并且可以改變頁面的URL。pjax是ajax+pushState的封裝,同時支持本地存儲、動畫等多種功能
具體可以看ajax與HTML5 history pushState/replaceState實例