摘要:變量聲明提升在中,函數(shù)聲明與變量聲明經(jīng)常被引擎隱式地提升到當(dāng)前作用域的頂部。對(duì)象的方法和屬性是在全局范圍內(nèi)有效的。未形成標(biāo)準(zhǔn),實(shí)現(xiàn)混亂。
前端面試題JavaScript(一)
JavaScript的組成
JavaScript 由以下三部分組成:
ECMAScript(核心):JavaScript 語(yǔ)言基礎(chǔ)
DOM(文檔對(duì)象模型):規(guī)定了訪問(wèn)HTML和XML的接口
BOM(瀏覽器對(duì)象模型):提供了瀏覽器窗口之間進(jìn)行交互的對(duì)象和方法
JS的基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型
基本數(shù)據(jù)類(lèi)型:undefined、null、boolean、number、string、symbol
引用數(shù)據(jù)類(lèi)型:object、array、function
檢測(cè)瀏覽器版本版本有哪些方式?
根據(jù) navigator.userAgent // UA.toLowerCase().indexOf("chrome")
根據(jù) window 對(duì)象的成員 // "ActiveXObject" in window
介紹JS有哪些內(nèi)置對(duì)象?
數(shù)據(jù)封裝類(lèi)對(duì)象:Object、Array、Boolean、Number、String
其他對(duì)象:Function、Arguments、Math、Date、RegExp、Error
ES6新增對(duì)象:Symbol、Map、Set、Promises、Proxy、Reflect
說(shuō)幾條寫(xiě)JavaScript的基本規(guī)范?
代碼縮進(jìn),建議使用“四個(gè)空格”縮進(jìn)
代碼段使用花括號(hào){}包裹
語(yǔ)句結(jié)束使用分號(hào);
變量和函數(shù)在使用前進(jìn)行聲明
以大寫(xiě)字母開(kāi)頭命名構(gòu)造函數(shù),全大寫(xiě)命名常量
規(guī)范定義JSON對(duì)象,補(bǔ)全雙引號(hào)
用{}和[]聲明對(duì)象和數(shù)組
如何編寫(xiě)高性能的JavaScript?
遵循嚴(yán)格模式:"use strict";
將js腳本放在頁(yè)面底部,加快渲染頁(yè)面
將js腳本將腳本成組打包,減少請(qǐng)求
使用非阻塞方式下載js腳本
盡量使用局部變量來(lái)保存全局變量
盡量減少使用閉包
使用 window 對(duì)象屬性方法時(shí),省略 window
盡量減少對(duì)象成員嵌套
緩存 DOM 節(jié)點(diǎn)的訪問(wèn)
通過(guò)避免使用 eval() 和 Function() 構(gòu)造器
給 setTimeout() 和 setInterval() 傳遞函數(shù)而不是字符串作為參數(shù)
盡量使用直接量創(chuàng)建對(duì)象和數(shù)組
最小化重繪(repaint)和回流(reflow)
描述瀏覽器的渲染過(guò)程,DOM樹(shù)和渲染樹(shù)的區(qū)別?
瀏覽器的渲染過(guò)程:
解析HTML構(gòu)建 DOM(DOM樹(shù)),并行請(qǐng)求 css/image/js
CSS 文件下載完成,開(kāi)始構(gòu)建 CSSOM(CSS樹(shù))
CSSOM 構(gòu)建結(jié)束后,和 DOM 一起生成 Render Tree(渲染樹(shù))
布局(Layout):計(jì)算出每個(gè)節(jié)點(diǎn)在屏幕中的位置
顯示(Painting):通過(guò)顯卡把頁(yè)面畫(huà)到屏幕上
DOM樹(shù) 和 渲染樹(shù) 的區(qū)別:
DOM樹(shù)與HTML標(biāo)簽一一對(duì)應(yīng),包括head和隱藏元素
渲染樹(shù)不包括head和隱藏元素,大段文本的每一個(gè)行都是獨(dú)立節(jié)點(diǎn),每一個(gè)節(jié)點(diǎn)都有對(duì)應(yīng)的css屬性
重繪和回流(重排)的區(qū)別和關(guān)系?
重繪:當(dāng)渲染樹(shù)中的元素外觀(如:顏色)發(fā)生改變,不影響布局時(shí),產(chǎn)生重繪
回流:當(dāng)渲染樹(shù)中的元素的布局(如:尺寸、位置、隱藏/狀態(tài)狀態(tài))發(fā)生改變時(shí),產(chǎn)生重繪回流
注意:JS獲取Layout屬性值(如:offsetLeft、scrollTop、getComputedStyle等)也會(huì)引起回流。因?yàn)闉g覽器需要通過(guò)回流計(jì)算最新值
回流必將引起重繪,而重繪不一定會(huì)引起回流
如何最小化重繪(repaint)和回流(reflow)?
需要要對(duì)元素進(jìn)行復(fù)雜的操作時(shí),可以先隱藏(display:"none"),操作完成后再顯示
需要?jiǎng)?chuàng)建多個(gè)DOM節(jié)點(diǎn)時(shí),使用DocumentFragment創(chuàng)建完后一次性的加入document
緩存Layout屬性值,如:var left = elem.offsetLeft; 這樣,多次使用 left 只產(chǎn)生一次回流
盡量避免用table布局(table元素一旦觸發(fā)回流就會(huì)導(dǎo)致table里所有的其它元素回流)
避免使用css表達(dá)式(expression),因?yàn)槊看握{(diào)用都會(huì)重新計(jì)算值(包括加載頁(yè)面)
盡量使用 css 屬性簡(jiǎn)寫(xiě),如:用 border 代替 border-width, border-style, border-color
批量修改元素樣式:elem.className 和 elem.style.cssText 代替 elem.style.xxx
script 的位置是否會(huì)影響首屏顯示時(shí)間?
在解析 HTML 生成 DOM 過(guò)程中,js 文件的下載是并行的,不需要 DOM 處理到 script 節(jié)點(diǎn)。因此,script的位置不影響首屏顯示的開(kāi)始時(shí)間。
瀏覽器解析 HTML 是自上而下的線性過(guò)程,script作為 HTML 的一部分同樣遵循這個(gè)原則
因此,script 會(huì)延遲 DomContentLoad,只顯示其上部分首屏內(nèi)容,從而影響首屏顯示的完成時(shí)間
解釋JavaScript中的作用域與變量聲明提升?
JavaScript作用域:
在Java、C等語(yǔ)言中,作用域?yàn)閒or語(yǔ)句、if語(yǔ)句或{}內(nèi)的一塊區(qū)域,稱(chēng)為作用域;
而在 JavaScript 中,作用域?yàn)閒unction(){}內(nèi)的區(qū)域,稱(chēng)為函數(shù)作用域。
JavaScript變量聲明提升:
在JavaScript中,函數(shù)聲明與變量聲明經(jīng)常被JavaScript引擎隱式地提升到當(dāng)前作用域的頂部。
聲明語(yǔ)句中的賦值部分并不會(huì)被提升,只有名稱(chēng)被提升
函數(shù)聲明的優(yōu)先級(jí)高于變量,如果變量名跟函數(shù)名相同且未賦值,則函數(shù)聲明會(huì)覆蓋變量聲明
如果函數(shù)有多個(gè)同名參數(shù),那么最后一個(gè)參數(shù)(即使沒(méi)有定義)會(huì)覆蓋前面的同名參數(shù)
介紹JavaScript的原型,原型鏈?有什么特點(diǎn)?
原型:
JavaScript的所有對(duì)象中都包含了一個(gè) [__proto__] 內(nèi)部屬性,這個(gè)屬性所對(duì)應(yīng)的就是該對(duì)象的原型
JavaScript的函數(shù)對(duì)象,除了原型 [__proto__] 之外,還預(yù)置了 prototype 屬性
當(dāng)函數(shù)對(duì)象作為構(gòu)造函數(shù)創(chuàng)建實(shí)例時(shí),該 prototype 屬性值將被作為實(shí)例對(duì)象的原型 [__proto__]。
原型鏈:
當(dāng)一個(gè)對(duì)象調(diào)用的屬性/方法自身不存在時(shí),就會(huì)去自己 [__proto__] 關(guān)聯(lián)的前輩 prototype 對(duì)象上去找
如果沒(méi)找到,就會(huì)去該 prototype 原型 [__proto__] 關(guān)聯(lián)的前輩 prototype 去找。依次類(lèi)推,直到找到屬性/方法或 undefined 為止。從而形成了所謂的“原型鏈”
原型特點(diǎn):
JavaScript對(duì)象是通過(guò)引用來(lái)傳遞的,當(dāng)修改原型時(shí),與之相關(guān)的對(duì)象也會(huì)繼承這一改變
JavaScript有幾種類(lèi)型的值
原始數(shù)據(jù)類(lèi)型(Undefined,Null,Boolean,Number、String)-- 棧
引用數(shù)據(jù)類(lèi)型(對(duì)象、數(shù)組和函數(shù))-- 堆
兩種類(lèi)型的區(qū)別是:存儲(chǔ)位置不同:
原始數(shù)據(jù)類(lèi)型是直接存儲(chǔ)在棧(stack)中的簡(jiǎn)單數(shù)據(jù)段,占據(jù)空間小、大小固定,屬于被頻繁使用數(shù)據(jù);
引用數(shù)據(jù)類(lèi)型存儲(chǔ)在堆(heap)中的對(duì)象,占據(jù)空間大、大小不固定,如果存儲(chǔ)在棧中,將會(huì)影響程序運(yùn)行的性能;
引用數(shù)據(jù)類(lèi)型在棧中存儲(chǔ)了指針,該指針指向堆中該實(shí)體的起始地址。
當(dāng)解釋器尋找引用值時(shí),會(huì)首先檢索其在棧中的地址,取得地址后從堆中獲得實(shí)體。
JavaScript如何實(shí)現(xiàn)一個(gè)類(lèi),怎么實(shí)例化這個(gè)類(lèi)?
構(gòu)造函數(shù)法(this + prototype) -- 用 new 關(guān)鍵字 生成實(shí)例對(duì)象
缺點(diǎn):用到了 this 和 prototype,編寫(xiě)復(fù)雜,可讀性差
function Mobile(name, price){ this.name = name; this.price = price; } Mobile.prototype.sell = function(){ alert(this.name + ",售價(jià) $" + this.price); } var iPhone7 = new Mobile("iPhone7", 1000); iPhone7.sell();
Object.create 法 -- 用 Object.create() 生成實(shí)例對(duì)象
缺點(diǎn):不能實(shí)現(xiàn)私有屬性和私有方法,實(shí)例對(duì)象之間也不能共享數(shù)據(jù)
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(); }; }
極簡(jiǎn)主義法(消除 this 和 prototype) -- 調(diào)用 createNew() 得到實(shí)例對(duì)象
優(yōu)點(diǎn):容易理解,結(jié)構(gòu)清晰優(yōu)雅,符合傳統(tǒng)的"面向?qū)ο缶幊?的構(gòu)造
var Cat = { age: 3, // 共享數(shù)據(jù) -- 定義在類(lèi)對(duì)象內(nèi),createNew() 外 createNew: function () { var cat = {}; // var cat = Animal.createNew(); // 繼承 Animal 類(lèi) cat.name = "小咪"; var sound = "喵喵喵"; // 私有屬性--定義在 createNew() 內(nèi),輸出對(duì)象外 cat.makeSound = function () { alert(sound); // 暴露私有屬性 }; cat.changeAge = function(num){ Cat.age = num; // 修改共享數(shù)據(jù) }; return cat; // 輸出對(duì)象 } }; var cat = Cat.createNew(); cat.makeSound();
ES6 語(yǔ)法糖 class -- 用 new 關(guān)鍵字 生成實(shí)例對(duì)象
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return "(" + this.x + ", " + this.y + ")"; } } var point = new Point(2, 3);
Javascript如何實(shí)現(xiàn)繼承?
構(gòu)造函數(shù)綁定:使用 call 或 apply 方法,將父對(duì)象的構(gòu)造函數(shù)綁定在子對(duì)象上
function Cat(name,color){ Animal.apply(this, arguments); this.name = name; this.color = color; }
實(shí)例繼承:將子對(duì)象的 prototype 指向父對(duì)象的一個(gè)實(shí)例
Cat.prototype = new Animal(); Cat.prototype.constructor = Cat;
拷貝繼承:如果把父對(duì)象的所有屬性和方法,拷貝進(jìn)子對(duì)象
function extend(Child, Parent) { var p = Parent.prototype; var c = Child.prototype; for (var i in p) { c[i] = p[i]; } c.uber = p; }
原型繼承:將子對(duì)象的 prototype 指向父對(duì)象的 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 語(yǔ)法糖 extends:class ColorPoint extends Point {}
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 調(diào)用父類(lèi)的constructor(x, y) this.color = color; } toString() { return this.color + " " + super.toString(); // 調(diào)用父類(lèi)的toString() } }
談?wù)則his對(duì)象的理解
this 總是指向函數(shù)的直接調(diào)用者
如果有 new 關(guān)鍵字,this 指向 new 出來(lái)的實(shí)例對(duì)象
在事件中,this指向觸發(fā)這個(gè)事件的對(duì)象
IE下 attachEvent 中的this總是指向全局對(duì)象Window
eval是做什么的?
eval的功能是把對(duì)應(yīng)的字符串解析成JS代碼并運(yùn)行
應(yīng)該避免使用eval,不安全,非常耗性能(先解析成js語(yǔ)句,再執(zhí)行)
由JSON字符串轉(zhuǎn)換為JSON對(duì)象的時(shí)候可以用 eval("("+ str +")");
什么是 Window 對(duì)象? 什么是 Document 對(duì)象?
Window 對(duì)象表示當(dāng)前瀏覽器的窗口,是JavaScript的頂級(jí)對(duì)象。
我們創(chuàng)建的所有對(duì)象、函數(shù)、變量都是 Window 對(duì)象的成員。
Window 對(duì)象的方法和屬性是在全局范圍內(nèi)有效的。
Document 對(duì)象是 HTML 文檔的根節(jié)點(diǎn)與所有其他節(jié)點(diǎn)(元素節(jié)點(diǎn),文本節(jié)點(diǎn),屬性節(jié)點(diǎn), 注釋節(jié)點(diǎn))
Document 對(duì)象使我們可以通過(guò)腳本對(duì) HTML 頁(yè)面中的所有元素進(jìn)行訪問(wèn)
Document 對(duì)象是 Window 對(duì)象的一部分,可通過(guò) window.document 屬性對(duì)其進(jìn)行訪問(wèn)
介紹 DOM 的發(fā)展
DOM:文檔對(duì)象模型(Document Object Model),定義了訪問(wèn)HTML和XML文檔的標(biāo)準(zhǔn),與編程語(yǔ)言及平臺(tái)無(wú)關(guān)
DOM0:提供了查詢和操作Web文檔的內(nèi)容API。未形成標(biāo)準(zhǔn),實(shí)現(xiàn)混亂。如:document.forms["login"]
DOM1:W3C提出標(biāo)準(zhǔn)化的DOM,簡(jiǎn)化了對(duì)文檔中任意部分的訪問(wèn)和操作。如:JavaScript中的Document對(duì)象
DOM2:原來(lái)DOM基礎(chǔ)上擴(kuò)充了鼠標(biāo)事件等細(xì)分模塊,增加了對(duì)CSS的支持。如:getComputedStyle(elem, pseudo)
DOM3:增加了XPath模塊和加載與保存(Load and Save)模塊。如:XPathEvaluator
介紹DOM0,DOM2,DOM3事件處理方式區(qū)別
DOM0級(jí)事件處理方式:
btn.onclick = func;
btn.onclick = null;
DOM2級(jí)事件處理方式:
btn.addEventListener("click", func, false);
btn.removeEventListener("click", func, false);
btn.attachEvent("onclick", func);
btn.detachEvent("onclick", func);
DOM3級(jí)事件處理方式:
eventUtil.addListener(input, "textInput", func);
eventUtil 是自定義對(duì)象,textInput 是DOM3級(jí)事件
事件的三個(gè)階段
捕獲、目標(biāo)、冒泡
介紹事件“捕獲”和“冒泡”執(zhí)行順序和事件的執(zhí)行次數(shù)?
按照W3C標(biāo)準(zhǔn)的事件:首是進(jìn)入捕獲階段,直到達(dá)到目標(biāo)元素,再進(jìn)入冒泡階段
事件執(zhí)行次數(shù)(DOM2-addEventListener):元素上綁定事件的個(gè)數(shù)
注意1:前提是事件被確實(shí)觸發(fā)
注意2:事件綁定幾次就算幾個(gè)事件,即使類(lèi)型和功能完全一樣也不會(huì)“覆蓋”
事件執(zhí)行順序:判斷的關(guān)鍵是否目標(biāo)元素
非目標(biāo)元素:根據(jù)W3C的標(biāo)準(zhǔn)執(zhí)行:捕獲->目標(biāo)元素->冒泡(不依據(jù)事件綁定順序)
目標(biāo)元素:依據(jù)事件綁定順序:先綁定的事件先執(zhí)行(不依據(jù)捕獲冒泡標(biāo)準(zhǔn))
最終順序:父元素捕獲->目標(biāo)元素事件1->目標(biāo)元素事件2->子元素捕獲->子元素冒泡->父元素冒泡
注意:子元素事件執(zhí)行前提 事件確實(shí)“落”到子元素布局區(qū)域上,而不是簡(jiǎn)單的具有嵌套關(guān)系
在一個(gè)DOM上同時(shí)綁定兩個(gè)點(diǎn)擊事件:一個(gè)用捕獲,一個(gè)用冒泡。事件會(huì)執(zhí)行幾次,先執(zhí)行冒泡還是捕獲?
該DOM上的事件如果被觸發(fā),會(huì)執(zhí)行兩次(執(zhí)行次數(shù)等于綁定次數(shù))
如果該DOM是目標(biāo)元素,則按事件綁定順序執(zhí)行,不區(qū)分冒泡/捕獲
如果該DOM是處于事件流中的非目標(biāo)元素,則先執(zhí)行捕獲,后執(zhí)行冒泡
事件的代理/委托
事件委托是指將事件綁定目標(biāo)元素的到父元素上,利用冒泡機(jī)制觸發(fā)該事件
優(yōu)點(diǎn):
可以減少事件注冊(cè),節(jié)省大量?jī)?nèi)存占用
可以將事件應(yīng)用于動(dòng)態(tài)添加的子元素上
缺點(diǎn):
使用不當(dāng)會(huì)造成事件在不應(yīng)該觸發(fā)時(shí)觸發(fā)
示例:
ulEl.addEventListener("click", function(e){ var target = event.target || event.srcElement; if(!!target && target.nodeName.toUpperCase() === "LI"){ console.log(target.innerHTML); } }, false);
IE與火狐的事件機(jī)制有什么區(qū)別? 如何阻止冒泡?
IE只事件冒泡,不支持事件捕獲;火狐同時(shí)支持件冒泡和事件捕獲
IE的事件處理和W3C的事件處理有哪些區(qū)別?
綁定事件
W3C: targetEl.addEventListener("click", handler, false);
IE: targetEl.attachEvent("onclick", handler);
刪除事件
W3C: targetEl.removeEventListener("click", handler, false);
IE: targetEl.detachEvent(event, handler);
事件對(duì)象
W3C: var e = arguments.callee.caller.arguments[0]
IE: window.event
事件目標(biāo)
W3C: e.target
IE: window.event.srcElement
阻止事件默認(rèn)行為
W3C: e.preventDefault()
IE: window.event.returnValue = false
阻止事件傳播
W3C: e.stopPropagation()
IE: window.event.cancelBubble = true
W3C事件的 target 與 currentTarget 的區(qū)別?
target 只會(huì)出現(xiàn)在事件流的目標(biāo)階段
currentTarget 可能出現(xiàn)在事件流的任何階段
當(dāng)事件流處在目標(biāo)階段時(shí),二者的指向相同
當(dāng)事件流處于捕獲或冒泡階段時(shí):currentTarget 指向當(dāng)前事件活動(dòng)的對(duì)象(一般為父級(jí))
如何派發(fā)事件(dispatchEvent)?(如何進(jìn)行事件廣播?)
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); } }
什么是函數(shù)節(jié)流?介紹一下應(yīng)用場(chǎng)景和原理?
函數(shù)節(jié)流(throttle)是指阻止一個(gè)函數(shù)在很短時(shí)間間隔內(nèi)連續(xù)調(diào)用。
只有當(dāng)上一次函數(shù)執(zhí)行后達(dá)到規(guī)定的時(shí)間間隔,才能進(jìn)行下一次調(diào)用。
但要保證一個(gè)累計(jì)最小調(diào)用間隔(否則拖拽類(lèi)的節(jié)流都將無(wú)連續(xù)效果)
函數(shù)節(jié)流用于 onresize, onscroll 等短時(shí)間內(nèi)會(huì)多次觸發(fā)的事件
函數(shù)節(jié)流的原理:使用定時(shí)器做時(shí)間節(jié)流。
當(dāng)觸發(fā)一個(gè)事件時(shí),先用 setTimout 讓這個(gè)事件延遲一小段時(shí)間再執(zhí)行。
如果在這個(gè)時(shí)間間隔內(nèi)又觸發(fā)了事件,就 clearTimeout 原來(lái)的定時(shí)器,
再 setTimeout 一個(gè)新的定時(shí)器重復(fù)以上流程。
函數(shù)節(jié)流簡(jiǎn)單實(shí)現(xiàn):
function throttle(method, context) { clearTimeout(methor.tId); method.tId = setTimeout(function(){ method.call(context); }, 100); // 兩次調(diào)用至少間隔 100ms } // 調(diào)用 window.onresize = function(){ throttle(myFunc, window); }
區(qū)分什么是“客戶區(qū)坐標(biāo)”、“頁(yè)面坐標(biāo)”、“屏幕坐標(biāo)”?
客戶區(qū)坐標(biāo):鼠標(biāo)指針在可視區(qū)中的水平坐標(biāo)(clientX)和垂直坐標(biāo)(clientY)
頁(yè)面坐標(biāo):鼠標(biāo)指針在頁(yè)面布局中的水平坐標(biāo)(pageX)和垂直坐標(biāo)(pageY)
屏幕坐標(biāo):設(shè)備物理屏幕的水平坐標(biāo)(screenX)和垂直坐標(biāo)(screenY)
如何獲得一個(gè)DOM元素的絕對(duì)位置?
elem.offsetLeft:返回元素相對(duì)于其定位父級(jí)左側(cè)的距離
elem.offsetTop:返回元素相對(duì)于其定位父級(jí)頂部的距離
elem.getBoundingClientRect():返回一個(gè)DOMRect對(duì)象,包含一組描述邊框的只讀屬性,單位像素
分析 ["1", "2", "3"].map(parseInt) 答案是多少?
答案:[1, NaN, NaN]
parseInt(string, radix) 第2個(gè)參數(shù) radix 表示進(jìn)制。省略 radix 或 radix = 0,則數(shù)字將以十進(jìn)制解析
map 每次為 parseInt 傳3個(gè)參數(shù)(elem, index, array),其中 index 為數(shù)組索引
因此,map 遍歷 ["1", "2", "3"],相應(yīng) parseInt 接收參數(shù)如下
parseInt("1", 0); // 1 parseInt("2", 1); // NaN parseInt("3", 2); // NaN
所以,parseInt 參數(shù) radix 不合法,導(dǎo)致返回值為 NaN
new 操作符具體干了什么?
創(chuàng)建實(shí)例對(duì)象,this 變量引用該對(duì)象,同時(shí)還繼承了構(gòu)造函數(shù)的原型
屬性和方法被加入到 this 引用的對(duì)象中
新創(chuàng)建的對(duì)象由 this 所引用,并且最后隱式的返回 this
用原生JavaScript的實(shí)現(xiàn)過(guò)什么功能嗎?
封裝選擇器、調(diào)用第三方API、設(shè)置和獲取樣式
解釋一下這段代碼的意思嗎?
[].forEach.call($$("*"), function(el){ el.style.outline = "1px solid #" + (~~(Math.random()*(1<<24))).toString(16); })
解釋?zhuān)韩@取頁(yè)面所有的元素,遍歷這些元素,為它們添加1像素隨機(jī)顏色的輪廓(outline)
$$(sel)// $$函數(shù)被許多現(xiàn)代瀏覽器命令行支持,等價(jià)于 document.querySelectorAll(sel)
[].forEach.call(NodeLists) // 使用 call 函數(shù)將數(shù)組遍歷函數(shù) forEach 應(yīng)到節(jié)點(diǎn)元素列表
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) // 表示一個(gè)位于 0 到 16777216 之間的隨機(jī)浮點(diǎn)數(shù)
~~Math.random()*(1<<24) // ~~ 作用相當(dāng)于 parseInt 取整
(~~(Math.random()*(1<<24))).toString(16) // 轉(zhuǎn)換為一個(gè)十六進(jìn)制-
JavaScript實(shí)現(xiàn)異步編程的方法?
回調(diào)函數(shù)
事件監(jiān)聽(tīng)
發(fā)布/訂閱
Promises對(duì)象
Async函數(shù)[ES7]
web開(kāi)發(fā)中會(huì)話跟蹤的方法有哪些
cookie
session
url重寫(xiě)
隱藏input
ip地址
說(shuō)幾條寫(xiě)JavaScript的基本規(guī)范?
不要在同一行聲明多個(gè)變量
請(qǐng)使用 ===/!==來(lái)比較true/false或者數(shù)值
使用對(duì)象字面量替代new Array這種形式
不要使用全局函數(shù)
Switch語(yǔ)句必須帶有default分支
函數(shù)不應(yīng)該有時(shí)候有返回值,有時(shí)候沒(méi)有返回值
If語(yǔ)句必須使用大括號(hào)
for-in循環(huán)中的變量 應(yīng)該使用var關(guān)鍵字明確限定作用域,從而避免作用域污
Javascript如何實(shí)現(xiàn)繼承?
構(gòu)造繼承
原型繼承
實(shí)例繼承
拷貝繼承
原型prototype機(jī)制或apply和call方法去實(shí)現(xiàn)較簡(jiǎn)單,建議使用構(gòu)造函數(shù)與原型混合方式
function Parent(){ this.name = "wang"; } function Child(){ this.age = 28; } Child.prototype = new Parent();//繼承了Parent,通過(guò)原型 var demo = new Child(); alert(demo.age); alert(demo.name);//得到被繼承的屬性 }
javascript創(chuàng)建對(duì)象的幾種方式?
javascript創(chuàng)建對(duì)象簡(jiǎn)單的說(shuō),無(wú)非就是使用內(nèi)置對(duì)象或各種自定義對(duì)象,當(dāng)然還可以用JSON;但寫(xiě)法有很多種,也能混合使用
對(duì)象字面量的方式
person={firstname:"Mark",lastname:"Yun",age:25,eyecolor:"black"};
用function來(lái)模擬無(wú)參的構(gòu)造函數(shù)
function Person(){} var person=new Person();//定義一個(gè)function,如果使用new"實(shí)例化",該function可以看作是一個(gè)Class person.name="Mark"; person.age="25"; person.work=function(){ alert(person.name+" hello..."); } person.work();
用function來(lái)模擬參構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)(用this關(guān)鍵字定義構(gòu)造的上下文屬性)
function Pet(name,age,hobby){ this.name=name;//this作用域:當(dāng)前對(duì)象 this.age=age; this.hobby=hobby; this.eat=function(){ alert("我叫"+this.name+",我喜歡"+this.hobby+",是個(gè)程序員"); } } var maidou =new Pet("麥兜",25,"coding");//實(shí)例化、創(chuàng)建對(duì)象 maidou.eat();//調(diào)用eat方法
用工廠方式來(lái)創(chuàng)建(內(nèi)置對(duì)象)
var wcDog =new Object(); wcDog.name="旺財(cái)"; wcDog.age=3; wcDog.work=function(){ alert("我是"+wcDog.name+",汪汪汪......"); } wcDog.work();
用原型方式來(lái)創(chuàng)建
function Dog(){ } Dog.prototype.name="旺財(cái)"; Dog.prototype.eat=function(){ alert(this.name+"是個(gè)吃貨"); } var wangcai =new Dog(); wangcai.eat();
用混合方式來(lái)創(chuàng)建
function Car(name,price){ this.name=name; this.price=price; } Car.prototype.sell=function(){ alert("我是"+this.name+",我現(xiàn)在賣(mài)"+this.price+"萬(wàn)元"); } var camry =new Car("凱美瑞",27); camry.sell();
null,undefined 的區(qū)別?
undefined 表示不存在這個(gè)值。
undefined :是一個(gè)表示"無(wú)"的原始值或者說(shuō)表示"缺少值",就是此處應(yīng)該有一個(gè)值,但是還沒(méi)有定義。當(dāng)嘗試讀取時(shí)會(huì)返回 undefined
例如變量被聲明了,但沒(méi)有賦值時(shí),就等于undefined
null 表示一個(gè)對(duì)象被定義了,值為“空值”
null : 是一個(gè)對(duì)象(空對(duì)象, 沒(méi)有任何屬性和方法)
例如作為函數(shù)的參數(shù),表示該函數(shù)的參數(shù)不是對(duì)象;
在驗(yàn)證null時(shí),一定要使用 === ,因?yàn)?== 無(wú)法分別 null 和 undefined
寫(xiě)一個(gè)通用的事件偵聽(tīng)器函數(shù)
// event(事件)工具集,來(lái)源:github.com/markyun markyun.Event = { // 頁(yè)面加載完成后 readyEvent : function(fn) { if (fn==null) { fn=document; } var oldonload = window.onload; if (typeof window.onload != "function") { window.onload = fn; } else { window.onload = function() { oldonload(); fn(); }; } }, // 視能力分別使用dom0||dom2||IE方式 來(lái)綁定事件 // 參數(shù): 操作的元素,事件名稱(chēng) ,事件處理程序 addEvent : function(element, type, handler) { if (element.addEventListener) { //事件類(lèi)型、需要執(zhí)行的函數(shù)、是否捕捉 element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent("on" + type, function() { handler.call(element); }); } else { element["on" + type] = handler; } }, // 移除事件 removeEvent : function(element, type, handler) { if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else if (element.datachEvent) { element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } }, // 阻止事件 (主要是事件冒泡,因?yàn)镮E不支持事件捕獲) stopPropagation : function(ev) { if (ev.stopPropagation) { ev.stopPropagation(); } else { ev.cancelBubble = true; } }, // 取消事件的默認(rèn)行為 preventDefault : function(event) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } }, // 獲取事件目標(biāo) getTarget : function(event) { return event.target || event.srcElement; }, // 獲取event對(duì)象的引用,取到事件的所有信息,確保隨時(shí)能使用event; getEvent : function(e) { var ev = e || window.event; if (!ev) { var c = this.getEvent.caller; while (c) { ev = c.arguments[0]; if (ev && Event == ev.constructor) { break; } c = c.caller; } } return ev; } };
什么是閉包(closure),為什么要用它?
閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中變量的函數(shù),創(chuàng)建閉包的最常見(jiàn)的方式就是在一個(gè)函數(shù)內(nèi)創(chuàng)建另一個(gè)函數(shù),通過(guò)另一個(gè)函數(shù)訪問(wèn)這個(gè)函數(shù)的局部變量,利用閉包可以突破作用鏈域
閉包的特性:
函數(shù)內(nèi)再嵌套函數(shù)
內(nèi)部函數(shù)可以引用外層的參數(shù)和變量
參數(shù)和變量不會(huì)被垃圾回收機(jī)制回收
javascript 代碼中的"use strict";是什么意思 ? 使用它區(qū)別是什么?
use strict是一種ECMAscript 5 添加的(嚴(yán)格)運(yùn)行模式,這種模式使得 Javascript 在更嚴(yán)格的條件下運(yùn)行,使JS編碼更加規(guī)范化的模式,消除Javascript語(yǔ)法的一些不合理、不嚴(yán)謹(jǐn)之處,減少一些怪異行為
如何判斷一個(gè)對(duì)象是否屬于某個(gè)類(lèi)?
// 使用instanceof (待完善) if(a instanceof Person){ alert("yes"); }
new操作符具體干了什么呢?
創(chuàng)建一個(gè)空對(duì)象,并且 this 變量引用該對(duì)象,同時(shí)還繼承了該函數(shù)的原型
屬性和方法被加入到 this 引用的對(duì)象中
新創(chuàng)建的對(duì)象由 this 所引用,并且最后隱式的返回 this
var obj = {}; obj.__proto__ = Base.prototype; Base.call(obj);
js延遲加載的方式有哪些?
defer和async、動(dòng)態(tài)創(chuàng)建DOM方式(用得最多)、按需異步載入js
Ajax 是什么? 如何創(chuàng)建一個(gè)Ajax?
ajax的全稱(chēng):Asynchronous Javascript And XML
異步傳輸+js+xml
所謂異步,在這里簡(jiǎn)單地解釋就是:向服務(wù)器發(fā)送請(qǐng)求的時(shí)候,我們不必等待結(jié)果,而是可以同時(shí)做其他的事情,等到有了結(jié)果它自己會(huì)根據(jù)設(shè)定進(jìn)行后續(xù)操作,與此同時(shí),頁(yè)面是不會(huì)發(fā)生整頁(yè)刷新的,提高了用戶體驗(yàn)
創(chuàng)建XMLHttpRequest對(duì)象,也就是創(chuàng)建一個(gè)異步調(diào)用對(duì)象
建一個(gè)新的HTTP請(qǐng)求,并指定該HTTP請(qǐng)求的方法、URL及驗(yàn)證信息
設(shè)置響應(yīng)HTTP請(qǐng)求狀態(tài)變化的函數(shù)
發(fā)送HTTP請(qǐng)求
獲取異步調(diào)用返回的數(shù)據(jù)
用JavaScript和DOM實(shí)現(xiàn)局部刷新
同步和異步的區(qū)別?
同步:瀏覽器訪問(wèn)服務(wù)器請(qǐng)求,用戶看得到頁(yè)面刷新,重新發(fā)請(qǐng)求,等請(qǐng)求完,頁(yè)面刷新,新內(nèi)容出現(xiàn),用戶看到新內(nèi)容,進(jìn)行下一步操作
異步:瀏覽器訪問(wèn)服務(wù)器請(qǐng)求,用戶正常操作,瀏覽器后端進(jìn)行請(qǐng)求。等請(qǐng)求完,頁(yè)面不刷新,新內(nèi)容也會(huì)出現(xiàn),用戶看到新內(nèi)容
documen.write和 innerHTML的區(qū)別
document.write只能重繪整個(gè)頁(yè)面
innerHTML可以重繪頁(yè)面的一部分
DOM操作——怎樣添加、移除、移動(dòng)、復(fù)制、創(chuàng)建和查找節(jié)點(diǎn)?
(1)創(chuàng)建新節(jié)點(diǎn)
createDocumentFragment() //創(chuàng)建一個(gè)DOM片段
createElement() //創(chuàng)建一個(gè)具體的元素
createTextNode() //創(chuàng)建一個(gè)文本節(jié)點(diǎn)
(2)添加、移除、替換、插入
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子節(jié)點(diǎn)前插入一個(gè)新的子節(jié)點(diǎn)
(3)查找
getElementsByTagName() //通過(guò)標(biāo)簽名稱(chēng)
getElementsByName() // 通過(guò)元素的Name屬性的值(IE容錯(cuò)能力較強(qiáng),會(huì)得到一個(gè)數(shù)組,其中包括id等于name值的)
getElementById() //通過(guò)元素Id,唯一性
那些操作會(huì)造成內(nèi)存泄漏?
內(nèi)存泄漏指任何對(duì)象在您不再擁有或需要它之后仍然存在
垃圾回收器定期掃描對(duì)象,并計(jì)算引用了每個(gè)對(duì)象的其他對(duì)象的數(shù)量。如果一個(gè)對(duì)象的引用數(shù)量為 0(沒(méi)有其他對(duì)象引用過(guò)該對(duì)象),或?qū)υ搶?duì)象的惟一引用是循環(huán)的,那么該對(duì)象的內(nèi)存即可回收
setTimeout 的第一個(gè)參數(shù)使用字符串而非函數(shù)的話,會(huì)引發(fā)內(nèi)存泄漏
閉包、控制臺(tái)日志、循環(huán)(在兩個(gè)對(duì)象彼此引用且彼此保留時(shí),就會(huì)產(chǎn)生一個(gè)循環(huán))
漸進(jìn)增強(qiáng)和優(yōu)雅降級(jí)
漸進(jìn)增強(qiáng) :針對(duì)低版本瀏覽器進(jìn)行構(gòu)建頁(yè)面,保證最基本的功能,然后再針對(duì)高級(jí)瀏覽器進(jìn)行效果、交互等改進(jìn)和追加功能達(dá)到更好的用戶體驗(yàn)。
優(yōu)雅降級(jí) :一開(kāi)始就構(gòu)建完整的功能,然后再針對(duì)低版本瀏覽器進(jìn)行兼容
Javascript垃圾回收方法
標(biāo)記清除(mark and sweep)
這是JavaScript最常見(jiàn)的垃圾回收方式,當(dāng)變量進(jìn)入執(zhí)行環(huán)境的時(shí)候,比如函數(shù)中聲明一個(gè)變量,垃圾回收器將其標(biāo)記為“進(jìn)入環(huán)境”,當(dāng)變量離開(kāi)環(huán)境的時(shí)候(函數(shù)執(zhí)行結(jié)束)將其標(biāo)記為“離開(kāi)環(huán)境”
垃圾回收器會(huì)在運(yùn)行的時(shí)候給存儲(chǔ)在內(nèi)存中的所有變量加上標(biāo)記,然后去掉環(huán)境中的變量以及被環(huán)境中變量所引用的變量(閉包),在這些完成之后仍存在標(biāo)記的就是要?jiǎng)h除的變量了
引用計(jì)數(shù)(reference counting)
在低版本IE中經(jīng)常會(huì)出現(xiàn)內(nèi)存泄露,很多時(shí)候就是因?yàn)槠洳捎靡糜?jì)數(shù)方式進(jìn)行垃圾回收。引用計(jì)數(shù)的策略是跟蹤記錄每個(gè)值被使用的次數(shù),當(dāng)聲明了一個(gè) 變量并將一個(gè)引用類(lèi)型賦值給該變量的時(shí)候這個(gè)值的引用次數(shù)就加1,如果該變量的值變成了另外一個(gè),則這個(gè)值得引用次數(shù)減1,當(dāng)這個(gè)值的引用次數(shù)變?yōu)?的時(shí) 候,說(shuō)明沒(méi)有變量在使用,這個(gè)值沒(méi)法被訪問(wèn)了,因此可以將其占用的空間回收,這樣垃圾回收器會(huì)在運(yùn)行的時(shí)候清理掉引用次數(shù)為0的值占用的空間
js繼承方式及其優(yōu)缺點(diǎn)
原型鏈繼承的缺點(diǎn)
一是字面量重寫(xiě)原型會(huì)中斷關(guān)系,使用引用類(lèi)型的原型,并且子類(lèi)型還無(wú)法給超類(lèi)型傳遞參數(shù)。
借用構(gòu)造函數(shù)(類(lèi)式繼承)
借用構(gòu)造函數(shù)雖然解決了剛才兩種問(wèn)題,但沒(méi)有原型,則復(fù)用無(wú)從談起。所以我們需要原型鏈+借用構(gòu)造函數(shù)的模式,這種模式稱(chēng)為組合繼承
組合式繼承
組合式繼承是比較常用的一種繼承方法,其背后的思路是使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,而通過(guò)借用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。這樣,既通過(guò)在原型上定義方法實(shí)現(xiàn)了函數(shù)復(fù)用,又保證每個(gè)實(shí)例都有它自己的屬性。
defer和async
defer并行加載js文件,會(huì)按照頁(yè)面上script標(biāo)簽的順序執(zhí)行async并行加載js文件,下載完成立即執(zhí)行,不會(huì)按照頁(yè)面上script標(biāo)簽的順序執(zhí)行
用過(guò)哪些設(shè)計(jì)模式?
工廠模式:
主要好處就是可以消除對(duì)象間的耦合,通過(guò)使用工程方法而不是new關(guān)鍵字。將所有實(shí)例化的代碼集中在一個(gè)位置防止代碼重復(fù)
工廠模式解決了重復(fù)實(shí)例化的問(wèn)題 ,但還有一個(gè)問(wèn)題,那就是識(shí)別問(wèn)題,因?yàn)楦緹o(wú)法 搞清楚他們到底是哪個(gè)對(duì)象的實(shí)例
-
function createObject(name,age,profession){//集中實(shí)例化的函數(shù)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");//第一個(gè)實(shí)例var test2 = createObject("mike",25,"engineer");//第二個(gè)實(shí)例
構(gòu)造函數(shù)模式
使用構(gòu)造函數(shù)的方法 ,即解決了重復(fù)實(shí)例化的問(wèn)題 ,又解決了對(duì)象識(shí)別的問(wèn)題,該模式與工廠模式的不同之處在于
構(gòu)造函數(shù)方法沒(méi)有顯示的創(chuàng)建對(duì)象 (new Object());
直接將屬性和方法賦值給 this 對(duì)象;
沒(méi)有 renturn 語(yǔ)句
請(qǐng)解釋一下 JavaScript 的同源策略
概念:同源策略是客戶端腳本(尤其是Javascript)的重要的安全度量標(biāo)準(zhǔn)。它最早出自Netscape Navigator2.0,其目的是防止某個(gè)文檔或腳本從多個(gè)不同源裝載。這里的同源策略指的是:協(xié)議,域名,端口相同,同源策略是一種安全協(xié)議
指一段腳本只能讀取來(lái)自同一來(lái)源的窗口和文檔的屬性
為什么要有同源限制?
我們舉例說(shuō)明:比如一個(gè)黑客程序,他利用Iframe把真正的銀行登錄頁(yè)面嵌到他的頁(yè)面上,當(dāng)你使用真實(shí)的用戶名,密碼登錄時(shí),他的頁(yè)面就可以通過(guò)Javascript讀取到你的表單中input中的內(nèi)容,這樣用戶名,密碼就輕松到手了。
缺點(diǎn)
現(xiàn)在網(wǎng)站的JS都會(huì)進(jìn)行壓縮,一些文件用了嚴(yán)格模式,而另一些沒(méi)有。這時(shí)這些本來(lái)是嚴(yán)格模式的文件,被 merge后,這個(gè)串就到了文件的中間,不僅沒(méi)有指示嚴(yán)格模式,反而在壓縮后浪費(fèi)了字節(jié)
實(shí)現(xiàn)一個(gè)函數(shù)clone,可以對(duì)JavaScript中的5種主要的數(shù)據(jù)類(lèi)型(包括Number、String、Object、Array、Boolean)進(jìn)行值復(fù)制
Object.prototype.clone = function(){ var o = this.constructor === Array ? [] : {}; for(var e in this){ o[e] = typeof this[e] === "object" ? this[e].clone() : this[e]; } return o; }
說(shuō)說(shuō)嚴(yán)格模式的限制
嚴(yán)格模式主要有以下限制:
變量必須聲明后再使用
函數(shù)的參數(shù)不能有同名屬性,否則報(bào)錯(cuò)
不能使用with語(yǔ)句
不能對(duì)只讀屬性賦值,否則報(bào)錯(cuò)
不能使用前綴0表示八進(jìn)制數(shù),否則報(bào)錯(cuò)
不能刪除不可刪除的屬性,否則報(bào)錯(cuò)
不能刪除變量delete prop,會(huì)報(bào)錯(cuò),只能刪除屬性delete global[prop]
eval不會(huì)在它的外層作用域引入變量
eval和arguments不能被重新賦值
arguments不會(huì)自動(dòng)反映函數(shù)參數(shù)的變化
不能使用arguments.callee
不能使用arguments.caller
禁止this指向全局對(duì)象
不能使用fn.caller和fn.arguments獲取函數(shù)調(diào)用的堆棧
增加了保留字(比如protected、static和interface)
如何刪除一個(gè)cookie
將時(shí)間設(shè)為當(dāng)前時(shí)間往前一點(diǎn)
var date = new Date(); date.setDate(date.getDate() - 1);//真正的刪除
setDate()方法用于設(shè)置一個(gè)月的某一天
expires的設(shè)置
document.cookie = "user="+ encodeURIComponent("name") + ";expires = " + new Date(0)
編寫(xiě)一個(gè)方法 求一個(gè)字符串的字節(jié)長(zhǎng)度
假設(shè):一個(gè)英文字符占用一個(gè)字節(jié),一個(gè)中文字符占用兩個(gè)字節(jié)
function GetBytes(str){ var len = str.length; var bytes = len; for(var i=0; i255) bytes++; } return bytes; } alert(GetBytes("你好,as"));
請(qǐng)解釋什么是事件代理
事件代理(Event Delegation),又稱(chēng)之為事件委托。是 JavaScript 中常用綁定事件的常用技巧。顧名思義,“事件代理”即是把原本需要綁定的事件委托給父元素,讓父元素?fù)?dān)當(dāng)事件監(jiān)聽(tīng)的職務(wù)。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好處是可以提高性能
attribute和property的區(qū)別是什么?
attribute是dom元素在文檔中作為html標(biāo)簽擁有的屬性;
property就是dom元素在js中作為對(duì)象擁有的屬性。
對(duì)于html的標(biāo)準(zhǔn)屬性來(lái)說(shuō),attribute和property是同步的,是會(huì)自動(dòng)更新的
但是對(duì)于自定義的屬性來(lái)說(shuō),他們是不同步的
頁(yè)面編碼和被請(qǐng)求的資源編碼如果不一致如何處理?
后端響應(yīng)頭設(shè)置 charset
前端頁(yè)面設(shè)置 charset
把放在
之前和之后有什么區(qū)別?瀏覽器會(huì)如何解析它們?
按照HTML標(biāo)準(zhǔn),在結(jié)束后出現(xiàn)或任何元素的開(kāi)始標(biāo)簽,都是解析錯(cuò)誤
雖然不符合HTML標(biāo)準(zhǔn),但瀏覽器會(huì)自動(dòng)容錯(cuò),使實(shí)際效果與寫(xiě)在之前沒(méi)有區(qū)別
瀏覽器的容錯(cuò)機(jī)制會(huì)忽略