前端面試絕對會考的JS問題!【已經開源】
寫在前面
【前端指南】前端面試庫已經開源,正在完善之中
- [x] css問題
- [x] html問題
- [x] javascript問題
github地址 https://github.com/nanhupatar...
JavaScript 的組成
JavaScript 由以下三部分組成:
- ECMAScript(核心):JavaScript 語言基礎
- DOM(文件物件模型):規定了訪問 HTML 和 XML 的介面
- BOM(瀏覽器物件模型):提供了瀏覽器視窗之間進行互動的物件和方法
JS 的基本資料型別和引用資料型別
- 基本資料型別:undefined、null、boolean、number、string、symbol
- 引用資料型別:object、array、function
檢測瀏覽器版本版本有哪些方式?
- 根據 navigator.userAgent // UA.toLowerCase().indexOf('chrome')
- 根據 window 物件的成員 // 'ActiveXObject' in window
介紹 JS 有哪些內建物件?
- 資料封裝類物件:Object、Array、Boolean、Number、String
- 其他物件:Function、Arguments、Math、Date、RegExp、Error
- ES6 新增物件:Symbol、Map、Set、Promises、Proxy、Reflect
說幾條寫 JavaScript 的基本規範?
- 程式碼縮排,建議使用“四個空格”縮排
- 程式碼段使用花括號{}包裹
- 語句結束使用分號;
- 變數和函式在使用前進行宣告
- 以大寫字母開頭命名建構函式,全大寫命名常量
- 規範定義 JSON 物件,補全雙引號
- 用{}和[]宣告物件和陣列
如何編寫高效能的 JavaScript?
- 遵循嚴格模式:"use strict";
- 將 js 指令碼放在頁面底部,加快渲染頁面
- 將 js 指令碼將指令碼成組打包,減少請求
- 使用非阻塞方式下載 js 指令碼
- 儘量使用區域性變數來儲存全域性變數
- 儘量減少使用閉包
- 使用 window 物件屬性方法時,省略 window
- 儘量減少物件成員巢狀
- 快取 DOM 節點的訪問
- 通過避免使用 eval() 和 Function() 構造器
- 給 setTimeout() 和 setInterval() 傳遞函式而不是字串作為引數
- 儘量使用直接量建立物件和陣列
- 最小化重繪(repaint)和迴流(reflow)
DOM 元素 e 的 e.getAttribute(propName)和 e.propName 有什麼區別和聯絡
- e.getAttribute(),是標準 DOM 操作文件元素屬性的方法,具有通用性可在任意文件上使用,返回元素在原始檔中設定的屬性
- e.propName 通常是在 HTML 文件中訪問特定元素的特性,瀏覽器解析元素後生成對應物件(如 a 標籤生成 HTMLAnchorElement),這些物件的特性會根據特定規則結合屬性設定得到,對於沒有對應特性的屬性,只能使用 getAttribute 進行訪問
- e.getAttribute()返回值是原始檔中設定的值,型別是字串或者 null(有的實現返回"")
- e.propName 返回值可能是字串、布林值、物件、undefined 等
- 大部分 attribute 與 property 是一一對應關係,修改其中一個會影響另一個,如 id,title 等屬性
- 一些布林屬性
<input hidden/>
的檢測設定需要 hasAttribute 和 removeAttribute 來完成,或者設定對應 property - 像
<a href="../index.html">link</a>
中 href 屬性,轉換成 property 的時候需要通過轉換得到完整 URL - 一些 attribute 和 property 不是一一對應如:form 控制元件中
<input value="hello"/>
對應的是 defaultValue,修改或設定 value property 修改的是控制元件當前值,setAttribute 修改 value 屬性不會改變 value property
offsetWidth/offsetHeight,clientWidth/clientHeight 與 scrollWidth/scrollHeight 的區別
- offsetWidth/offsetHeight 返回值包含 content + padding + border,效果與 e.getBoundingClientRect()相同
- clientWidth/clientHeight 返回值只包含 content + padding,如果有滾動條,也不包含滾動條
- scrollWidth/scrollHeight 返回值包含 content + padding + 溢位內容的尺寸
描述瀏覽器的渲染過程,DOM 樹和渲染樹的區別?
瀏覽器的渲染過程:
- 解析 HTML 構建 DOM(DOM 樹),並行請求 css/image/js
- CSS 檔案下載完成,開始構建 CSSOM(CSS 樹)
- CSSOM 構建結束後,和 DOM 一起生成 Render Tree(渲染樹)
- 佈局(Layout):計算出每個節點在螢幕中的位置
- 顯示(Painting):通過顯示卡把頁面畫到螢幕上
DOM 樹 和 渲染樹 的區別:
- DOM 樹與 HTML 標籤一一對應,包括 head 和隱藏元素
- 渲染樹不包括 head 和隱藏元素,大段文字的每一個行都是獨立節點,每一個節點都有對應的 css 屬性
重繪和迴流(重排)的區別和關係?
- 重繪:當渲染樹中的元素外觀(如:顏色)發生改變,不影響佈局時,產生重繪
- 迴流:當渲染樹中的元素的佈局(如:尺寸、位置、隱藏/狀態狀態)發生改變時,產生重繪迴流
- 注意:JS 獲取 Layout 屬性值(如:offsetLeft、scrollTop、getComputedStyle 等)也會引起迴流。因為瀏覽器需要通過迴流計算最新值
- 迴流必將引起重繪,而重繪不一定會引起迴流
如何最小化重繪(repaint)和迴流(reflow)?
- 需要要對元素進行復雜的操作時,可以先隱藏(display:"none"),操作完成後再顯示
- 需要建立多個 DOM 節點時,使用 DocumentFragment 建立完後一次性的加入 document
- 快取 Layout 屬性值,如:var left = elem.offsetLeft; 這樣,多次使用 left 只產生一次迴流
- 儘量避免用 table 佈局(table 元素一旦觸發迴流就會導致 table 裡所有的其它元素迴流)
- 避免使用 css 表示式(expression),因為每次呼叫都會重新計算值(包括載入頁面)
- 儘量使用 css 屬性簡寫,如:用 border 代替 border-width, border-style, border-color
批量修改元素樣式:elem.className 和 elem.style.cssText 代替 elem.style.xxx
script 的位置是否會影響首屏顯示時間?
- 在解析 HTML 生成 DOM 過程中,js 檔案的下載是並行的,不需要 DOM 處理到 script 節點。因此,script 的位置不影響首屏顯示的開始時間。
- 瀏覽器解析 HTML 是自上而下的線性過程,script 作為 HTML 的一部分同樣遵循這個原則
- 因此,script 會延遲 DomContentLoad,只顯示其上部分首屏內容,從而影響首屏顯示的完成時間
解釋 JavaScript 中的作用域與變數宣告提升?
JavaScript 作用域:
- 在 Java、C 等語言中,作用域為 for 語句、if 語句或{}內的一塊區域,稱為作用域;
- 而在 JavaScript 中,作用域為 function(){}內的區域,稱為函式作用域。
JavaScript 變數宣告提升:
- 在 JavaScript 中,函式宣告與變數宣告經常被 JavaScript 引擎隱式地提升到當前作用域的頂部。
- 宣告語句中的賦值部分並不會被提升,只有名稱被提升
- 函式宣告的優先順序高於變數,如果變數名跟函式名相同且未賦值,則函式宣告會覆蓋變數宣告
- 如果函式有多個同名引數,那麼最後一個引數(即使沒有定義)會覆蓋前面的同名引數
介紹 JavaScript 的原型,原型鏈?有什麼特點?
原型:
- JavaScript 的所有物件中都包含了一個 [proto] 內部屬性,這個屬性所對應的就是該物件的原型
- JavaScript 的函式物件,除了原型 [proto] 之外,還預置了 prototype 屬性
- 當函式物件作為建構函式建立例項時,該 prototype 屬性值將被作為例項物件的原型 [proto]。
原型鏈:
- 當一個物件呼叫的屬性/方法自身不存在時,就會去自己 [proto] 關聯的前輩 prototype 物件上去找
- 如果沒找到,就會去該 prototype 原型 [proto] 關聯的前輩 prototype 去找。依次類推,直到找到屬性/方法或 undefined 為止。從而形成了所謂的“原型鏈”
原型特點:
- JavaScript 物件是通過引用來傳遞的,當修改原型時,與之相關的物件也會繼承這一改變
JavaScript 有幾種型別的值?,你能畫一下他們的記憶體圖嗎
- 原始資料型別(Undefined,Null,Boolean,Number、String)-- 棧
- 引用資料型別(物件、陣列和函式)-- 堆
- 兩種型別的區別是:儲存位置不同:
- 原始資料型別是直接儲存在棧(stack)中的簡單資料段,佔據空間小、大小固定,屬於被頻繁使用資料;
- 引用資料型別儲存在堆(heap)中的物件,佔據空間大、大小不固定,如果儲存在棧中,將會影響程式執行的效能;
- 引用資料型別在棧中儲存了指標,該指標指向堆中該實體的起始地址。
- 當直譯器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中獲得實體。
JavaScript 如何實現一個類,怎麼例項化這個類?
-
建構函式法(this + prototype) -- 用 new 關鍵字 生成例項物件
- 缺點:用到了 this 和 prototype,編寫複雜,可讀性差
function Mobile(name, price){
this.name = name;
this.price = price;
}
Mobile.prototype.sell = function(){
alert(this.name + ",售價 $" + this.price);
}
var iPhone7 = new Mobile("iPhone7", 1000);
iPhone7.sell();
-
Object.create 法 -- 用 Object.create() 生成例項物件
- 缺點:不能實現私有屬性和私有方法,例項物件之間也不能共享資料
var Person = {
firstname: "Mark",
lastname: "Yun",
age: 25,
introduce: function(){
alert('I am ' + Person.firstname + ' ' + Person.lastname);
}
};
var person = Object.create(Person);
person.introduce();
// Object.create 要求 IE9+,低版本瀏覽器可以自行部署:
if (!Object.create) {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
-
極簡主義法(消除 this 和 prototype) -- 呼叫 createNew() 得到例項物件
- 優點:容易理解,結構清晰優雅,符合傳統的"面向物件程式設計"的構造
var Cat = {
age: 3, // 共享資料 -- 定義在類物件內,createNew() 外
createNew: function () {
var cat = {};
// var cat = Animal.createNew(); // 繼承 Animal 類
cat.name = "小咪";
var sound = "喵喵喵"; // 私有屬性--定義在 createNew() 內,輸出物件外
cat.makeSound = function () {
alert(sound); // 暴露私有屬性
};
cat.changeAge = function(num){
Cat.age = num; // 修改共享資料
};
return cat; // 輸出物件
}
};
var cat = Cat.createNew();
cat.makeSound();
- ES6 語法糖 class -- 用 new 關鍵字 生成例項物件
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var point = new Point(2, 3);
Javascript 如何實現繼承?
- 建構函式繫結:使用 call 或 apply 方法,將父物件的建構函式繫結在子物件上
function Cat(name,color){
Animal.apply(this, arguments);
this.name = name;
this.color = color;
}
- 例項繼承:將子物件的 prototype 指向父物件的一個例項
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
- 拷貝繼承:如果把父物件的所有屬性和方法,拷貝進子物件
function extend(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
- 原型繼承:將子物件的 prototype 指向父物件的 prototype
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
- ES6 語法糖 extends:class ColorPoint extends Point {}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 呼叫父類的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 呼叫父類的toString()
}
}
js 繼承方式及其優缺點
原型鏈繼承的缺點
- 一是字面量重寫原型會中斷關係,使用引用型別的原型,並且子型別還無法給超型別傳遞引數。
借用建構函式(類式繼承)
- 借用建構函式雖然解決了剛才兩種問題,但沒有原型,則複用無從談起。所以我們需要原型鏈+借用建構函式的模式,這種模式稱為組合繼承
組合式繼承
- 組合式繼承是比較常用的一種繼承方法,其背後的思路是使用原型鏈實現對原型屬性和方法的繼承,而通過借用建構函式來實現對例項屬性的繼承。這樣,既通過在原型上定義方法實現了函式複用,又保證每個例項都有它自己的屬性。
javascript 建立物件的幾種方式?
javascript 建立物件簡單的說,無非就是使用內建物件或各種自定義物件,當然還可以用 JSON;但寫法有很多種,也能混合使用
- 物件字面量的方式
person={firstname:"Mark",lastname:"Yun",age:25,eyecolor:"black"};
- 用 function 來模擬無參的建構函式
function Person(){}
var person=new Person();//定義一個function,如果使用new"例項化",該function可以看作是一個Class
person.name="Mark";
person.age="25";
person.work=function(){
alert(person.name+" hello...");
}
person.work();
- 用 function 來模擬參建構函式來實現(用 this 關鍵字定義構造的上下文屬性)
function Pet(name,age,hobby){
this.name=name;//this作用域:當前物件
this.age=age;
this.hobby=hobby;
this.eat=function(){
alert("我叫"+this.name+",我喜歡"+this.hobby+",是個程式設計師");
}
}
var maidou =new Pet("麥兜",25,"coding");//例項化、建立物件
maidou.eat();//呼叫eat方法
- 用工廠方式來建立(內建物件)
var wcDog =new Object();
wcDog.name="旺財";
wcDog.age=3;
wcDog.work=function(){
alert("我是"+wcDog.name+",汪汪汪......");
}
wcDog.work();
- 用原型方式來建立
function Dog(){
}
Dog.prototype.name="旺財";
Dog.prototype.eat=function(){
alert(this.name+"是個吃貨");
}
var wangcai =new Dog();
wangcai.eat();
- 用混合方式來建立
function Car(name,price){
this.name=name;
this.price=price;
}
Car.prototype.sell=function(){
alert("我是"+this.name+",我現在賣"+this.price+"萬元");
}
var camry =new Car("凱美瑞",27);
camry.sell();
Javascript 作用鏈域?
- 全域性函式無法檢視區域性函式的內部細節,但區域性函式可以檢視其上層的函式細節,直至全域性細節
- 如果當前作用域沒有找到屬性或方法,會向上層作用域查詢,直至全域性函式,這種形式就是作用域鏈
談談 this 物件的理解
- this 總是指向函式的直接呼叫者
- 如果有 new 關鍵字,this 指向 new 出來的例項物件
- 在事件中,this 指向觸發這個事件的物件
- IE 下 attachEvent 中的 this 總是指向全域性物件 Window
eval 是做什麼的?
eval 的功能是把對應的字串解析成 JS 程式碼並執行
- 應該避免使用 eval,不安全,非常耗效能(先解析成 js 語句,再執行)
- 由 JSON 字串轉換為 JSON 物件的時候可以用 eval('('+ str +')');
什麼是 Window 物件? 什麼是 Document 物件?
- Window 物件表示當前瀏覽器的視窗,是 JavaScript 的頂級物件。
- 我們建立的所有物件、函式、變數都是 Window 物件的成員。
- Window 物件的方法和屬性是在全域性範圍內有效的。
- Document 物件是 HTML 文件的根節點與所有其他節點(元素節點,文字節點,屬性節點, 註釋節點)
- Document 物件使我們可以通過指令碼對 HTML 頁面中的所有元素進行訪問
- Document 物件是 Window 物件的一部分,可通過 window.document 屬性對其進行訪問
介紹 DOM 的發展
- DOM:文件物件模型(Document Object Model),定義了訪問 HTML 和 XML 文件的標準,與程式語言及平臺無關
- DOM0:提供了查詢和操作 Web 文件的內容 API。未形成標準,實現混亂。如:document.forms['login']
- DOM1:W3C 提出標準化的 DOM,簡化了對文件中任意部分的訪問和操作。如:JavaScript 中的 Document 物件
- DOM2:原來 DOM 基礎上擴充了滑鼠事件等細分模組,增加了對 CSS 的支援。如:getComputedStyle(elem, pseudo)
- DOM3:增加了 XPath 模組和載入與儲存(Load and Save)模組。如:XPathEvaluator
介紹 DOM0,DOM2,DOM3 事件處理方式區別
DOM0 級事件處理方式:
- btn.onclick = func;
- btn.onclick = null;
DOM2 級事件處理方式:
- btn.addEventListener('click', func, false);
- btn.removeEventListener('click', func, false);
- btn.attachEvent("onclick", func);
- btn.detachEvent("onclick", func);
DOM3 級事件處理方式:
- eventUtil.addListener(input, "textInput", func);
- eventUtil 是自定義物件,textInput 是 DOM3 級事件
事件的三個階段
捕獲、目標、冒泡
介紹事件“捕獲”和“冒泡”執行順序和事件的執行次數?
按照 W3C 標準的事件:首是進入捕獲階段,直到達到目標元素,再進入冒泡階段
事件執行次數(DOM2-addEventListener):元素上繫結事件的個數
- 注意 1:前提是事件被確實觸發
- 注意 2:事件繫結幾次就算幾個事件,即使型別和功能完全一樣也不會“覆蓋”
事件執行順序:判斷的關鍵是否目標元素
- 非目標元素:根據 W3C 的標準執行:捕獲->目標元素->冒泡(不依據事件繫結順序)
- 目標元素:依據事件繫結順序:先繫結的事件先執行(不依據捕獲冒泡標準)
- 最終順序:父元素捕獲->目標元素事件 1->目標元素事件 2->子元素捕獲->子元素冒泡->父元素冒泡
- 注意:子元素事件執行前提 事件確實“落”到子元素佈局區域上,而不是簡單的具有巢狀關係
在一個 DOM 上同時繫結兩個點選事件:一個用捕獲,一個用冒泡。事件會執行幾次,先執行冒泡還是捕獲?
- 該 DOM 上的事件如果被觸發,會執行兩次(執行次數等於繫結次數)
- 如果該 DOM 是目標元素,則按事件繫結順序執行,不區分冒泡/捕獲
- 如果該 DOM 是處於事件流中的非目標元素,則先執行捕獲,後執行冒泡
事件的代理/委託
事件委託是指將事件繫結目標元素的到父元素上,利用冒泡機制觸發該事件
優點:
- 可以減少事件註冊,節省大量記憶體佔用
- 可以將事件應用於動態新增的子元素上
缺點: 使用不當會造成事件在不應該觸發時觸發
示例:
ulEl.addEventListener('click', function(e){
var target = event.target || event.srcElement;
if(!!target && target.nodeName.toUpperCase() === "LI"){
console.log(target.innerHTML);
}
}, false);
IE 與火狐的事件機制有什麼區別? 如何阻止冒泡?
IE 只事件冒泡,不支援事件捕獲;火狐同時支援件冒泡和事件捕獲。
阻止冒泡:
- 取消預設操作: w3c 的方法是 e.preventDefault(),IE 則是使用 e.returnValue = false;
- return false javascript 的 return false 只會阻止預設行為,而是用 jQuery 的話則既阻止預設行為又防止物件冒泡。
- 阻止冒泡 w3c 的方法是 e.stopPropagation(),IE 則是使用 e.cancelBubble = true
[js] view plaincopy
function stopHandler(event)
window.event?window.event.cancelBubble=true:event.stopPropagation();
}
參考連結:淺談 javascript 事件取消和阻止冒泡-開源中國 2015
IE 的事件處理和 W3C 的事件處理有哪些區別?(必考)
繫結事件
- W3C: targetEl.addEventListener('click', handler, false);
- IE: targetEl.attachEvent('onclick', handler);
刪除事件
- W3C: targetEl.removeEventListener('click', handler, false);
- IE: targetEl.detachEvent(event, handler);
事件物件
- W3C: var e = arguments.callee.caller.arguments[0]
- IE: window.event
事件目標
- W3C: e.target
- IE: window.event.srcElement
阻止事件預設行為
- W3C: e.preventDefault()
- IE: window.event.returnValue = false'
阻止事件傳播
- W3C: e.stopPropagation()
- IE: window.event.cancelBubble = true
W3C 事件的 target 與 currentTarget 的區別?
- target 只會出現在事件流的目標階段
- currentTarget 可能出現在事件流的任何階段
- 當事件流處在目標階段時,二者的指向相同
- 當事件流處於捕獲或冒泡階段時:currentTarget 指向當前事件活動的物件(一般為父級)
如何派發事件(dispatchEvent)?(如何進行事件廣播?)
- W3C: 使用 dispatchEvent 方法
- IE: 使用 fireEvent 方法
var fireEvent = function(element, event){
if (document.createEventObject){
var mockEvent = document.createEventObject();
return element.fireEvent('on' + event, mockEvent)
}else{
var mockEvent = document.createEvent('HTMLEvents');
mockEvent.initEvent(event, true, true);
return !element.dispatchEvent(mockEvent);
}
}
什麼是函式節流?介紹一下應用場景和原理?
- 函式節流(throttle)是指阻止一個函式在很短時間間隔內連續呼叫。 只有當上一次函式執行後達到規定的時間間隔,才能進行下一次呼叫。 但要保證一個累計最小呼叫間隔(否則拖拽類的節流都將無連續效果)
- 函式節流用於 onresize, onscroll 等短時間內會多次觸發的事件
- 函式節流的原理:使用定時器做時間節流。 當觸發一個事件時,先用 setTimout 讓這個事件延遲一小段時間再執行。 如果在這個時間間隔內又觸發了事件,就 clearTimeout 原來的定時器, 再 setTimeout 一個新的定時器重複以上流程。
函式節流簡單實現:
function throttle(method, context) {
clearTimeout(methor.tId);
method.tId = setTimeout(function(){
method.call(context);
}, 100); // 兩次呼叫至少間隔 100ms
}
// 呼叫
window.onresize = function(){
throttle(myFunc, window);
}
區分什麼是“客戶區座標”、“頁面座標”、“螢幕座標”?
- 客戶區座標:滑鼠指標在可視區中的水平座標(clientX)和垂直座標(clientY)
- 頁面座標:滑鼠指標在頁面佈局中的水平座標(pageX)和垂直座標(pageY)
- 螢幕座標:裝置物理螢幕的水平座標(screenX)和垂直座標(screenY)
如何獲得一個 DOM 元素的絕對位置?
- elem.offsetLeft:返回元素相對於其定位父級左側的距離
- elem.offsetTop:返回元素相對於其定位父級頂部的距離
- elem.getBoundingClientRect():返回一個 DOMRect 物件,包含一組描述邊框的只讀屬性,單位畫素
分析 ['1', '2', '3'].map(parseInt) 答案是多少?(常考)
答案:[1, NaN, NaN]
parseInt(string, radix) 第 2 個引數 radix 表示進位制。省略 radix 或 radix = 0,則數字將以十進位制解析
map 每次為 parseInt 傳 3 個引數(elem, index, array),其中 index 為陣列索引
因此,map 遍歷 ["1", "2", "3"],相應 parseInt 接收引數如下
parseInt('1', 0); // 1
parseInt('2', 1); // NaN
parseInt('3', 2); // NaN
所以,parseInt 引數 radix 不合法,導致返回值為 NaN
new 操作符具體幹了什麼?
- 建立例項物件,this 變數引用該物件,同時還繼承了建構函式的原型
- 屬性和方法被加入到 this 引用的物件中
- 新建立的物件由 this 所引用,並且最後隱式的返回 this
用原生 JavaScript 的實現過什麼功能嗎?
封裝選擇器、呼叫第三方 API、設定和獲取樣式(自由回答)
解釋一下這段程式碼的意思嗎?
[].forEach.call($$("*"), function(el){
el.style.outline = "1px solid #" + (~~(Math.random()*(1<<24))).toString(16);
})
解釋:獲取頁面所有的元素,遍歷這些元素,為它們新增 1 畫素隨機顏色的輪廓(outline)
- $$(sel) // $$函式被許多現代瀏覽器命令列支援,等價於 document.querySelectorAll(sel)
- [].forEach.call(NodeLists) // 使用 call 函式將陣列遍歷函式 forEach 應到節點元素列表
- el.style.outline = "1px solid #333" // 樣式 outline 位於盒模型之外,不影響元素佈局位置
- (1<<24) // parseInt("ffffff", 16) == 16777215 == 2^24 - 1 // 1<<24 == 2^24 == 16777216
- Math.random()*(1<<24) // 表示一個位於 0 到 16777216 之間的隨機浮點數
-
Math.random()*(1<<24) //作用相當於 parseInt 取整 - (~~(Math.random()*(1<<24))).toString(16) // 轉換為一個十六進位制-
JavaScript 實現非同步程式設計的方法?
- 回撥函式
- 事件監聽
- 釋出/訂閱
- Promises 物件
- Async 函式[ES7]
web 開發中會話跟蹤的方法有哪些
- cookie
- session
- url 重寫
- 隱藏 input
- ip 地址
什麼是閉包(closure),為什麼要用它?
閉包是指有權訪問另一個函式作用域中變數的函式,建立閉包的最常見的方式就是在一個函式內建立另一個函式,通過另一個函式訪問這個函式的區域性變數,利用閉包可以突破作用鏈域
閉包的特性:
- 函式內再巢狀函式
- 內部函式可以引用外層的引數和變數
- 引數和變數不會被垃圾回收機制回收
javascript 程式碼中的"use strict";是什麼意思 ? 使用它區別是什麼?
use strict 是一種 ECMAscript 5 新增的(嚴格)執行模式,這種模式使得 Javascript 在更嚴格的條件下執行,使 JS 編碼更加規範化的模式,消除 Javascript 語法的一些不合理、不嚴謹之處,減少一些怪異行為
如何判斷一個物件是否屬於某個類?
// 使用instanceof (待完善)
if(a instanceof Person){
alert('yes');
}
js 延遲載入的方式有哪些?
defer 和 async、動態建立 DOM 方式(用得最多)、按需非同步載入 js
defer 和 async
defer 並行載入 js 檔案,會按照頁面上 script 標籤的順序執行 async 並行載入 js 檔案,下載完成立即執行,不會按照頁面上 script 標籤的順序執行
Ajax 是什麼? 如何建立一個 Ajax?
ajax 的全稱:Asynchronous Javascript And XML
非同步傳輸+js+xml
所謂非同步,在這裡簡單地解釋就是:向伺服器傳送請求的時候,我們不必等待結果,而是可以同時做其他的事情,等到有了結果它自己會根據設定進行後續操作,與此同時,頁面是不會發生整頁重新整理的,提高了使用者體驗
- 建立 XMLHttpRequest 物件,也就是建立一個非同步呼叫物件
- 建一個新的 HTTP 請求,並指定該 HTTP 請求的方法、URL 及驗證資訊
- 設定響應 HTTP 請求狀態變化的函式
- 傳送 HTTP 請求
- 獲取非同步呼叫返回的資料
- 用 JavaScript 和 DOM 實現區域性重新整理
同步和非同步的區別?
- 同步:瀏覽器訪問伺服器請求,使用者看得到頁面重新整理,重新發請求,等請求完,頁面重新整理,新內容出現,使用者看到新內容,進行下一步操作
- 非同步:瀏覽器訪問伺服器請求,使用者正常操作,瀏覽器後端進行請求。等請求完,頁面不重新整理,新內容也會出現,使用者看到新內容
documen.write 和 innerHTML 的區別
- document.write 只能重繪整個頁面
- innerHTML 可以重繪頁面的一部分
DOM 操作——怎樣新增、移除、移動、複製、建立和查詢節點?
建立新節點
- createDocumentFragment() //建立一個 DOM 片段
- createElement() //建立一個具體的元素
- createTextNode() //建立一個文字節點
新增、移除、替換、插入
- appendChild()
- removeChild()
- replaceChild()
- insertBefore() //在已有的子節點前插入一個新的子節點
查詢
- getElementsByTagName() //通過標籤名稱
- getElementsByName() // 通過元素的 Name 屬性的值(IE 容錯能力較強,會得到一個數組,其中包括 id 等於 name 值的) * getElementById() //通過元素 Id,唯一性
那些操作會造成記憶體洩漏?
- 記憶體洩漏指任何物件在您不再擁有或需要它之後仍然存在
- 垃圾回收器定期掃描物件,並計算引用了每個物件的其他物件的數量。如果一個物件的引用數量為 0(沒有其他物件引用過該物件),或對該物件的惟一引用是迴圈的,那麼該物件的記憶體即可回收
- setTimeout 的第一個引數使用字串而非函式的話,會引發記憶體洩漏
- 閉包、控制檯日誌、迴圈(在兩個物件彼此引用且彼此保留時,就會產生一個迴圈)
漸進增強和優雅降級
- 漸進增強 :針對低版本瀏覽器進行構建頁面,保證最基本的功能,然後再針對高階瀏覽器進行效果、互動等改進和追加功能達到更好的使用者體驗。
- 優雅降級 :一開始就構建完整的功能,然後再針對低版本瀏覽器進行相容
Javascript 垃圾回收方法
標記清除(mark and sweep)
- 這是 JavaScript 最常見的垃圾回收方式,當變數進入執行環境的時候,比如函式中宣告一個變數,垃圾回收器將其標記為“進入環境”,當變數離開環境的時候(函式執行結束)將其標記為“離開環境”
- 垃圾回收器會在執行的時候給儲存在記憶體中的所有變數加上標記,然後去掉環境中的變數以及被環境中變數所引用的變數(閉包),在這些完成之後仍存在標記的就是要刪除的變量了
引用計數(reference counting)
- 在低版本 IE 中經常會出現記憶體洩露,很多時候就是因為其採用引用計數方式進行垃圾回收。引用計數的策略是跟蹤記錄每個值被使用的次數,當聲明瞭一個 變數並將一個引用型別賦值給該變數的時候這個值的引用次數就加 1,如果該變數的值變成了另外一個,則這個值得引用次數減 1,當這個值的引用次數變為 0 的時 候,說明沒有變數在使用,這個值沒法被訪問了,因此可以將其佔用的空間回收,這樣垃圾回收器會在執行的時候清理掉引用次數為 0 的值佔用的空間
參考連結 記憶體管理-MDN
用過哪些設計模式?
- 工廠模式:
- 主要好處就是可以消除物件間的耦合,通過使用工程方法而不是 new 關鍵字。將所有例項化的程式碼集中在一個位置防止程式碼重複
- 工廠模式解決了重複例項化的問題 ,但還有一個問題,那就是識別問題,因為根本無法 搞清楚他們到底是哪個物件的例項
function createObject(name,age,profession){//集中例項化的函式var obj = new Object();
obj.name = name;
obj.age = age;
obj.profession = profession;
obj.move = function () {
return this.name + ' at ' + this.age + ' engaged in ' + this.profession;
};
return obj;
}
var test1 = createObject('trigkit4',22,'programmer');//第一個例項var test2 = createObject('mike',25,'engineer');//第二個例項
- 建構函式模式
- 使用建構函式的方法 ,即解決了重複例項化的問題 ,又解決了物件識別的問題,該模式與工廠模式的不同之處在於
- 建構函式方法沒有顯示的建立物件 (new Object());
- 直接將屬性和方法賦值給 this 物件;
- 沒有 renturn 語句
說說你對閉包的理解
使用閉包主要是為了設計私有的方法和變數。閉包的優點是可以避免全域性變數的汙染,缺點是閉包會常駐記憶體,會增大記憶體使用量,使用不當很容易造成記憶體洩露。在 js 中,函式即閉包,只有函式才會產生作用域的概念
閉包有三個特性:
- 函式巢狀函式
- 函式內部可以引用外部的引數和變數
- 引數和變數不會被垃圾回收機制回收
請解釋一下 JavaScript 的同源策略
- 概念:同源策略是客戶端指令碼(尤其是 Javascript)的重要的安全度量標準。它最早出自 Netscape Navigator2.0,其目的是防止某個文件或指令碼從多個不同源裝載。這裡的同源策略指的是:協議,域名,埠相同,同源策略是一種安全協議
- 指一段指令碼只能讀取來自同一來源的視窗和文件的屬性
為什麼要有同源限制?
我們舉例說明:比如一個黑客程式,他利用 Iframe 把真正的銀行登入頁面嵌到他的頁面上,當你使用真實的使用者名稱,密碼登入時,他的頁面就可以通過 Javascript 讀取到你的表單中 input 中的內容,這樣使用者名稱,密碼就輕鬆到手了。]
缺點: 現在網站的 JS 都會進行壓縮,一些檔案用了嚴格模式,而另一些沒有。這時這些本來是嚴格模式的檔案,被 merge 後,這個串就到了檔案的中間,不僅沒有指示嚴格模式,反而在壓縮後浪費了位元組
實現一個函式 clone,可以對 JavaScript 中的 5 種主要的資料型別(包括 Number、String、Object、Array、Boolean)進行值複製(常考)
function deepClone(obj) {
if (!isObject(obj)) {
throw new Error('obj 不是一個物件!')
}
let isArray = Array.isArray(obj)
let cloneObj = isArray ? [] : {}
for (let key in obj) {
cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
}
return cloneObj
}
注意:for...in 法不支援拷貝 func、date、reg 和 err
// 代理法
function deepClone(obj) {
if (!isObject(obj)) {
throw new Error('obj 不是一個物件!')
}
let isArray = Array.isArray(obj)
let cloneObj = isArray ? [...obj] : { ...obj }
Reflect.ownKeys(cloneObj).forEach(key => {
cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
})
return cloneObj
}
說說嚴格模式的限制
- 嚴格模式主要有以下限制:
- 變數必須聲明後再使用
- 函式的引數不能有同名屬性,否則報錯
- 不能使用 with 語句
- 不能對只讀屬性賦值,否則報錯
- 不能使用字首 0 表示八進位制數,否則報錯
- 不能刪除不可刪除的屬性,否則報錯
- 不能刪除變數 delete prop,會報錯,只能刪除屬性 delete global[prop]
- eval 不會在它的外層作用域引入變數
- eval 和 arguments 不能被重新賦值
- arguments 不會自動反映函式引數的變化
- 不能使用 arguments.callee
- 不能使用 arguments.caller
- 禁止 this 指向全域性物件
- 不能使用 fn.caller 和 fn.arguments 獲取函式呼叫的堆疊
- 增加了保留字(比如 protected、static 和 interface)
如何刪除一個 cookie
將時間設為當前時間往前一點
var date = new Date();
date.setDate(date.getDate() - 1);//真正的刪除
setDate()方法用於設定一個月的某一天
expires 的設定
document.cookie = 'user='+ encodeURIComponent('name') + ';expires = ' + new Date(0)
編寫一個方法 求一個字串的位元組長度
假設:一個英文字元佔用一個位元組,一箇中文字元佔用兩個位元組
function GetBytes(str){
var len = str.length;
var bytes = len;
for(var i=0; i<len; i++){
if (str.charCodeAt(i) > 255) bytes++;
}
return bytes;
}
alert(GetBytes("你好,as"));
請解釋什麼是事件代理
事件代理(Event Delegation),又稱之為事件委託。是 JavaScript 中常用繫結事件的常用技巧。顧名思義,“事件代理”即是把原本需要繫結的事件委託給父元素,讓父元素擔當事件監聽的職務。事件代理的原理是 DOM 元素的事件冒泡。使用事件代理的好處是可以提高效能
attribute 和 property 的區別是什麼?
- attribute 是 dom 元素在文件中作為 html 標籤擁有的屬性;
- property 就是 dom 元素在 js 中作為物件擁有的屬性。
- 對於 html 的標準屬性來說,attribute 和 property 是同步的,是會自動更新的
- 但是對於自定義的屬性來說,他們是不同步的
頁面編碼和被請求的資源編碼如果不一致如何處理?
- 後端響應頭設定 charset
- 前端頁面
<meta>
設定 charset
把 <script>
放在 </body>
之前和之後有什麼區別?瀏覽器會如何解析它們?
按照 HTML 標準,在</body>結束後出現<script>
或任何元素的開始標籤,都是解析錯誤
雖然不符合 HTML 標準,但瀏覽器會自動容錯,使實際效果與寫在</body>
之前沒有區別
瀏覽器的容錯機制會忽略<script>
之前的</body>,視作<script>
仍在 body 體內。省略</body>
和</html>
閉合標籤符合 HTML 標準,伺服器可以利用這一標準
非同步載入 JS 的方式有哪些?
- 設定
<script>
屬性 async="async" (一旦指令碼可用,則會非同步執行) - 動態建立 script DOM:document.createElement('script');
- XmlHttpRequest 指令碼注入
- 非同步載入庫 LABjs
- 模組載入器 Sea.js
JavaScript 中,呼叫函式有哪幾種方式?
- 方法呼叫模式 Foo.foo(arg1, arg2);
- 函式呼叫模式 foo(arg1, arg2);
- 構造器呼叫模式 (new Foo())(arg1, arg2);
- call/applay 呼叫模式 Foo.foo.call(that, arg1, arg2);
- bind 呼叫模式 Foo.foo.bind(that)(arg1, arg2)();
簡單實現 Function.bind 函式?
if (!Function.prototype.bind) {
Function.prototype.bind = function(that) {
var func = this, args = arguments;
return function() {
return func.apply(that, Array.prototype.slice.call(args, 1));
}
}
}
// 只支援 bind 階段的預設引數:
func.bind(that, arg1, arg2)();
// 不支援以下呼叫階段傳入的引數:
func.bind(that)(arg1, arg2);
列舉一下 JavaScript 陣列和物件有哪些原生方法
-
陣列:
- arr.concat(arr1, arr2, arrn);
- arr.join(",");
- arr.sort(func);
- arr.pop();
- arr.push(e1, e2, en);
- arr.shift();
- unshift(e1, e2, en);
- arr.reverse();
- arr.slice(start, end);
- arr.splice(index, count, e1, e2, en);
- arr.indexOf(el);
- arr.includes(el); // ES6
-
物件:
- object.hasOwnProperty(prop);
- object.propertyIsEnumerable(prop);
- object.valueOf();
- object.toString();
- object.toLocaleString();
- Class.prototype.isPropertyOf(object);
Array.splice() 與 Array.splice() 的區別?
-
slice -- “讀取”陣列指定的元素,不會對原陣列進行修改
- 語法:arr.slice(start, end)
- start 指定選取開始位置(含)
- end 指定選取結束位置(不含)
-
splice
- “操作”陣列指定的元素,會修改原陣列,返回被刪除的元素
- 語法:arr.splice(index, count, [insert Elements])
- index 是操作的起始位置
- count = 0 插入元素,count > 0 刪除元素
- [insert Elements] 向陣列新插入的元素
JavaScript 物件生命週期的理解?
- 當建立一個物件時,JavaScript 會自動為該物件分配適當的記憶體
- 垃圾回收器定期掃描物件,並計算引用了該物件的其他物件的數量
- 如果被引用數量為 0,或惟一引用是迴圈的,那麼該物件的記憶體即可回收
哪些操作會造成記憶體洩漏?
- JavaScript 記憶體洩露指物件在不需要使用它時仍然存在,導致佔用的記憶體不能使用或回收
- 未使用 var 宣告的全域性變數
- 閉包函式(Closures)
- 迴圈引用(兩個物件相互引用)
- 控制檯日誌(console.log)
- 移除存在繫結事件的 DOM 元素(IE)