摘要:借用構造函數類式繼承借用構造函數雖然解決了剛才兩種問題,但沒有原型,則復用無從談起。
寫在前面
【前端指南】前端面試庫已經開源,正在完善之中
[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 等屬性
一些布爾屬性的檢測設置需要 hasAttribute 和 removeAttribute 來完成,或者設置對應 property
像link中 href 屬性,轉換成 property 的時候需要通過轉換得到完整 URL
一些 attribute 和 property 不是一一對應如:form 控件中對應的是 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
在解析 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 和 asyncdefer 并行加載 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請解釋什么是事件代理255) bytes++; } return bytes; } alert(GetBytes("你好,as"));
事件代理(Event Delegation),又稱之為事件委托。是 JavaScript 中常用綁定事件的常用技巧。顧名思義,“事件代理”即是把原本需要綁定的事件委托給父元素,讓父元素擔當事件監聽的職務。事件代理的原理是 DOM 元素的事件冒泡。使用事件代理的好處是可以提高性能
attribute 和 property 的區別是什么?attribute 是 dom 元素在文檔中作為 html 標簽擁有的屬性;
property 就是 dom 元素在 js 中作為對象擁有的屬性。
對于 html 的標準屬性來說,attribute 和 property 是同步的,是會自動更新的
但是對于自定義的屬性來說,他們是不同步的
頁面編碼和被請求的資源編碼如果不一致如何處理?后端響應頭設置 charset
前端頁面設置 charset
把 放在之前和之后有什么區別?瀏覽器會如何解析它們?
按照 HTML 標準,在結束后出現或任何元素的開始標簽,都是解析錯誤
雖然不符合 HTML 標準,但瀏覽器會自動容錯,使實際效果與寫在之前沒有區別
瀏覽器的容錯機制會忽略之前的,視作仍在 body 體內。省略和