1.引起內存泄漏的操作有哪些
- 不經意間的全局變量
function foo(arg) {
bar = "this is a hidden global variable";
}
解決方法: 設置為Null或者重新賦值
- 被遺忘的計時器及回調
- dom清空或刪除時,事件未清除導致的內存泄漏
- 閉包引起的內存泄漏
var leaks = (function(){
var leak = 'xxxxxx';// 被閉包所引用,不會被回收
return function(){
console.log(leak);
}
})()
2. 如何實現ajax請求
通過實例化一個XMLHttpRequest對象得到一個實例,調用實例的open方法為這次 ajax請求設定相應的http方法、相應的地址和以及是否異步,當然大多數情況下我們都是選異步, 以異步為例,之后調用send方法ajax請求,這個方法可以設定需要發送的報文主體,然后通過 監聽readystatechange事件,通過這個實例的readyState屬性來判斷這個ajax請求的狀態,其中分為0,1,2,3,4這四種 狀態,當狀態為4的時候也就是接收數據完成的時候,這時候可以通過實例的status屬性判斷這個請求是否成功
var xhr = new XMLHttpRequest()
xhr.open('get', 'aass.json', true)
xhr.send(null)
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status === 200) {
console.log(xhr.responseText)
}
}
}
狀態值:
0: (未初始化)還沒有調用send()方法。
1: (載入)已經調用send()方法,正在派發請求。
2: (載入完成)send()已經執行完成,已經接收到全部的響應內容。
3: (交互)正在解析響應內容。
4: (完成)響應內容已經解析完成,用戶可以調用
3.簡要介紹ES6
ES6在變量的聲明和定義方面增加了let、const聲明變量,有局部變量的概念,賦值中有比較吸引人的結構賦值,同時ES6對字符串、 數組、正則、對象、函數等拓展了一些方法,如字符串方面的模板字符串、函數方面的默認參數、對象方面屬性的簡潔表達方式,ES6也 引入了新的數據類型symbol,新的數據結構set和map,symbol可以通過typeof檢測出來,為解決異步回調問題,引入了promise和 generator,還有最為吸引人了實現Class和模塊,通過Class可以更好的面向對象編程,使用模塊加載方便模塊化編程,當然考慮到 瀏覽器兼容性,我們在實際開發中需要使用babel進行編譯。
4. 對JS原型的理解
我們知道在es6之前,js沒有類和繼承的概念,js是通過原型來實現繼承的。在js中一個構造函數默認自帶有一個prototype屬性, 這個的屬性值是一個對象,同時這個prototype對象自帶有一個constructor屬性,這個屬性指向這個構造函數,同時每一個實例 都有一個proto屬性指向這個prototype對象,我們可以將這個叫做隱式原型,我們在使用一個實例的方法的時候,會先檢查 這個實例中是否有這個方法,沒有則會繼續向上查找這個prototype對象是否有這個方法,剛剛我們說到prototype是一個對象, 那么也即是說這個是一個對象的實例,那么這個對象同樣也會有一個proto屬性指向對象的prototype對象。
5.對js模塊化的理解
在ES6出現之前,js沒有標準的模塊化概念,這也就造成了js多人寫作開發容易造成全局污染的情況,以前我們可能會采用立即執行 函數、對象等方式來盡量減少變量這種情況,后面社區為了解決這個問題陸續提出了AMD規范和CMD規范,這里不同于Node.js的 CommonJS的原因在于服務端所有的模塊都是存在于硬盤中的,加載和讀取幾乎是不需要時間的,而瀏覽器端因為加載速度取決于網速, 因此需要采用異步加載,AMD規范中使用define來定義一個模塊,使用require方法來加載一個模塊,現在ES6也推出了標準的模塊 加載方案,通過exports和require來導出和導入模塊。
6.如何實現一個JS的AMD模塊加載器
AMD是解決JS模塊化的規范,實現這樣的一個模塊加載器的關鍵在于解決每個模塊依賴的解析。首先我們需要有一個模塊的入口,也就是主模塊,比如我們使用 一個use方法作為入口,之后以數組的形式列出了主模塊的依賴,這時候我們要想到的是如何解析這一個一個的依賴,也就是如何解析出一個個js文件的絕對地址, 我們可以制定一個規則,如默認為主模塊的路徑為基準,也可以像requirejs一樣使用一個config方法來指定一個baseurl和為每一個模塊指定一個path,最后就是 模塊的問題,我們需要暴露一個define方法來定義模塊,也就是模塊名,依賴以及每個模塊的各自代碼。其中每個模塊的代碼都應該在依賴加載完之后執行,這就是一個 回調函數,模塊的依賴、回調函數、狀態、名字、模塊導出等可以看做是一個模塊的屬性,因此我們可以使用一個對象來保存所有的模塊,然后每個模塊的各個屬性存放在一個對象中。 最后我們來考慮一下模塊加載的問題,上面我們說到use方法,use方法的邏輯就是遍歷依賴,然后對每個模塊進行加載,也就是解析地址然后使用插入script,我們假設 使用loadModule方法來加載依賴,那么這個函數的邏輯就應該是檢查我們的模塊是否已經加載過來判斷是否需要加載,如果這個模塊還有依賴則調用use方法繼續解析,模塊依賴中我們 還沒有提到的問題就是每個模塊的依賴是需要被傳進模塊里來使用的,解決方法就是每個模塊的callback方法執行后的返回的export記錄下來然后使用apply之類的方法將這些參數傳遞進去。 大致就是這樣子的。
7.簡要介紹事件代理,以及什么時候使用,事件代理發生在事件處理流程的哪個階段,有什么好處?
事件代理就是說我們將事件添加到本來要添加事件的父節點,將事件委托給父節點來觸發處理函數,這通常會使用在大量的同級元素需要添加同一類事件的時候,比如一個動態的非常多的列表,需要為每個列表項都添加 點擊事件,這時可以使用事件代理,通過判斷e.target.nodeName來判斷發生的具體元素,從而判斷是否是在 列表項中觸發,這樣的好處是可以減少事件綁定,同時動態的DOM結構仍然可以監聽。事件代理發生在冒泡階段。
8.使用new操作符實例化一個對象的具體步驟
1.構造一個新的對象
2.將構造函數的作用域賦給新對象(也就是說this指向了新的對象)
3.執行構造函數中的代碼
4.返回新對象
9.js如何判斷網頁中圖片加載成功或者失敗
使用onload事件運行加載成功,使用onerror事件判斷失敗
10.遞歸和迭代的區別是什么,各有什么優缺點?
程序調用自身稱為遞歸,利用變量的原值推出新值稱為迭代,遞歸的優點 大問題轉化為小問題,可以減少代碼量,同時應為代碼精簡,可讀性好, 缺點就是,遞歸調用浪費了空間,而且遞歸太深容易造成堆棧的溢出。迭代的好處 就是代碼運行效率好,因為時間只因循環次數增加而增加,而且沒有額外的空間開銷, 缺點就是代碼不如遞歸簡潔
11.策略模式是什么,說一下你的理解?
//封裝的策略算法
var strategies={
"S":function (salary) {
return salary * 4;
},
"A":function (salary) {
return salary * 3;
},
"B":function (salary) {
return salary * 2;
}
};
//具體的計算方法
var calculateBonus=function (level, salary) {
return strategies[level](salary);
};
console.log(calculateBonus('S',1000));
console.log(calculateBonus('A',4000));
12.什么是事件循環(EVENT LOOP)?
我們常常說js是單線程的,是指js執行引擎是單線程的,除了這個單線程,還有一個 任務隊列,在執行js代碼的過程中,執行引擎遇到注冊的延時方法,如定時器,DOM事件, 會將這些方法交給相應的瀏覽器模塊處理,當這些延時方法有觸發條件去觸發的時候, 這些延時方法會被添加至任務隊列,而這些任務隊列中的方法只有js的主線程空閑了才會執行, 這也就是說我們常常用的定時器定的時間參數只是一個觸發條件,具體多少時間后執行其實還需要看 js主線程空閑與否
13.原生JS操作DOM的方法有哪些?
獲取節點的方法getElementById、getElementsByClassName、getElementsByTagName、 getElementsByName、querySelector、querySelectorAll,對元素屬性進行操作的 getAttribute、 setAttribute、removeAttribute方法,對節點進行增刪改的appendChild、insertBefore、replaceChild、removeChild、 createElement等
14.typeof操作符返回值有哪些,對undefined、null、NaN使用這個操作符分別返回什么?
number string undefined object function boolean
typeof undefined => undefined
typeof null => object
typeof NaN => number
15.實現一個類型判斷函數,需要鑒別出基本類型、function、null、NaN、數組、對象?
function type(ele) {
if(ele===null) {
return null;
} else if(typeof ele === 'object') {
if(Array.isArray(ele)) {
return 'array';
} else {
return typeof ele;
}
} else if(typeof ele === 'number') {
if(isNaN(ele)) {
return NaN;
} else {
return typeof ele;
}
} else{
return typeof ele;
}
}
16.javascript做類型判斷的方法有哪些?
- typeof
- instanceof 是用來判斷 A 是否為 B 的實例
- constructor 當一個函數 F被定義時,JS引擎會為F添加 prototype 原型,然后再在 prototype上添加一個 constructor 屬性,并讓其指向 F 的引用。
- toString toString() 是 Object 的原型方法,調用該方法,默認返回當前對象的 [[Class]] 。這是一個內部屬性,其格式為 [object Xxx] ,其中 Xxx 就是對象的類型。
17.JavaScript嚴格模式下有哪些不同?
- 不允許不使用var關鍵字去創建全局變量,拋出ReferenceError
- 不允許對變量使用delete操作符,拋ReferenceError
- 不可對對象的只讀屬性賦值,不可對對象的不可配置屬性使用delete操作符,不可為不可拓展的對象添加屬性,均拋TypeError
- 對象屬性名必須唯一
- 函數中不可有重名參數
- 在函數內部對修改參數不會反映到arguments中
- 淘汰arguments.callee和arguments.caller
- 不可在if內部聲明函數
- 拋棄with語句
18.setTimeout和setInterval的區別,包含內存方面的分析?
setTimeout表示間隔一段時間之后執行一次調用,而setInterval則是每間隔一段時間循環調用,直至clearInterval結束。 內存方面,setTimeout只需要進入一次隊列,不會造成內存溢出,setInterval因為不計算代碼執行時間,有可能同時執行多次代碼, 導致內存溢出
19.同源策略是什么?
同源策略是指只有具有相同源的頁面才能夠共享數據,比如cookie,同源是指頁面具有相同的協議、域名、端口號,有一項不同就不是同源。 有同源策略能夠保證web網頁的安全性。
20.ES6之前JavaScript如何實現繼承?
ES6之前的繼承是通過原型來實現的,也就是每一個構造函數都會有一個prototype屬性,然后如果我們調用一個實例的方法或者屬性,首先會在自身尋找,然后在 構造函數的prototype上尋找,而prototype本質上就是一個實例,因此如果prototype上還沒有則會往prototype上的構造函數的prototype尋找,因此實現繼承 可以讓構造函數的prototype是父級的一個實例就是以實現繼承。
21.如何阻止事件冒泡和默認事件?
標準的DOM對象中可以使用事件對象的stopPropagation()方法來阻止事件冒泡,但在IE8以下中IE的事件對象通過設置事件對象的cancelBubble屬性為true來阻止冒泡; 默認事件的話通過事件對象的preventDefault()方法來阻止,而IE通過設置事件對象的returnValue屬性為false來阻止默認事件。
22.addEventListener有哪些參數?
有三個參數,第一個是事件的類型,第二個是事件的回調函數,第三個是一個表示事件是冒泡階段還是捕獲階段捕獲的布爾值,true表示捕獲,false表示冒泡
23.介紹一下Promise,底層如何實現?
24.如何實現懶加載?
懶加載就是根據用戶的瀏覽需要記載內容,也就是在用戶即將瀏覽完當前的內容時進行繼續加載內容,這種技術常常用來加載圖片的時候使用。我們判斷用戶是否即將瀏覽到底部之后進行在家內容 這時候可能會需要加載大量的內容,可以使用fragment來優化一下,因為大部分是使用滑動和滾輪來觸發的,因此很有可能會不斷觸發,可以使用函數節流做一個優化,防止用戶不斷觸發。
vue懶加載
isElementInViewport (el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
},
getImg() {
let img = this.$refs.foodsWrapper.getElementsByTagName('img');
for (let i = 0; i < img.length; i++) {
if (this.isElementInViewport(img[i])) {
img[i].src = img[i].getAttribute('data-src');
}
}
},
25函數節流是什么?
函數節流就是讓一個函數無法在很短的時間間隔內連續調用,而是間隔一段時間執行,這在我們為元素綁定一些事件的時候經常會用到,比如我們 為window綁定了一個resize事件,如果用戶一直改變窗口大小,就會一直觸發這個事件處理函數,這對性能有很大影響。
throttle(fn, delay) {
let timer = null;
return function() {
let context = this;
let args = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(context, args);
}, delay);
};
},
// 增加必然觸發執行的時間間隔
throttleV2(fn, delay, mustRunDelay) {
let timer = null;
let tStart;
return function() {
let context = this;
let args = arguments;
let tCurr = +new Date();
clearTimeout(timer);
if (!tStart) {
tStart = tCurr;
}
if (tCurr - tStart >= mustRunDelay) {
fn.apply(context, args);
tStart = tCurr;
} else {
timer = setTimeout(function() {
fn.apply(context, args);
}, delay);
}
};
},
window.onresize = that.throttle(function() {
console.log('ok1');
}, 200);
26.瀏覽器內核有哪些?分別對應哪些瀏覽器?
常見的瀏覽器內核有Trident、Gecko、WebKit、Presto,對應的瀏覽器為Trident對應于IE,Gecko對應于火狐瀏覽器,Webkit有chrome和safari,Presto 有Opera。
27.什么是深拷貝,什么是淺拷貝?
淺拷貝是指僅僅復制對象的引用,而不是復制對象本身;深拷貝則是把復制對象所引用的全部對象都復制一遍。