請(qǐng)描述一下 JavaScript 事件冒泡機(jī)制?
當(dāng)頁(yè)面中某個(gè)元素的事件被觸發(fā)以后,比如點(diǎn)擊了頁(yè)面中的某個(gè)按鈕,
就觸發(fā)了當(dāng)前按鈕的點(diǎn)擊事件,但是 JavaScript 并不是簡(jiǎn)單就直接觸發(fā)該元素的相應(yīng)事件,
而是會(huì)首先從 DOM 樹的最頂層(window)依次的去觸發(fā)目標(biāo)(被直接點(diǎn)擊的)元素所有父級(jí)的同類事件,
直到觸發(fā)到目標(biāo)元素,然后又會(huì)再一次的從目標(biāo)元素開始觸發(fā)其所有父級(jí)的所有同類事件直到window,
也就是同類型事件的目標(biāo)元素與 window 之間觸發(fā)一個(gè)來(lái)回,
window 到目標(biāo)的觸發(fā)階段,我們稱為捕獲階段,
目標(biāo)觸發(fā)事件的時(shí)候我們稱為目標(biāo)階段,
而最后目標(biāo)到 window 的觸發(fā)階段,我們稱為冒泡階段。
這種機(jī)制我們稱為事件流(event flow),冒泡機(jī)制其實(shí)就是事件流機(jī)制中的冒泡階段規(guī)則。
出處:https://www.w3.org/TR/2016/WD-uievents-20160804/ 中的
3.1. Event dispatch and DOM event flow
JavaScript 的事件流模型都有什么?
分析:
事件流模型這個(gè)知識(shí)點(diǎn)在妙味中級(jí)階段 event 事件章節(jié)就有詳細(xì)講解,如果已經(jīng)忘記的朋友,再去看看這個(gè)階段的視頻吧。 題外話:一般純理論的知識(shí)點(diǎn)學(xué)起來(lái)枯燥(妙味實(shí)體班的學(xué)員也是如此),但理論的好處在于當(dāng)遇到問(wèn)題時(shí),能迅速判斷出錯(cuò)的原理所在,從而可以準(zhǔn)確迅速的查找問(wèn)題并精準(zhǔn)修復(fù),可以讓冗余代碼簡(jiǎn)化到最低、可以不再出了錯(cuò)以后像撞大運(yùn)般的采用 “試來(lái)試去大法” 來(lái)修復(fù)~
事件流描述的是從頁(yè)面中接收事件的順序。 DOM 結(jié)構(gòu)是樹形結(jié)構(gòu),當(dāng)頁(yè)面中的某一個(gè)元素觸發(fā)了某個(gè)一個(gè)事件,事件會(huì)從最頂層的 window 對(duì)象開始,向下傳播到目標(biāo)元素,途徑的祖先節(jié)點(diǎn)都會(huì)觸發(fā)對(duì)應(yīng)的事件,如果當(dāng)前節(jié)點(diǎn)的該事件綁定了事件處理函數(shù)的話,則會(huì)執(zhí)行該函數(shù)當(dāng)事件達(dá)到目標(biāo)元素并執(zhí)行綁定函數(shù)(如果有綁定的話)后,事件又會(huì)向上傳播到 window 元素,途徑的祖先節(jié)點(diǎn)都會(huì)觸發(fā)對(duì)應(yīng)的事件(如果綁定事件處理函數(shù)的話)
事件流包含三個(gè)階段:
- 事件捕捉階段:事件開始由頂層對(duì)象觸發(fā),然后逐級(jí)向下傳播,直到目標(biāo)的元素;
- 處于目標(biāo)階段:處在綁定事件的元素上;
- 事件冒泡階段:事件由具體的元素先接收,然后逐級(jí)向上傳播,直到不具體的元素;
什么是閉包(closure),為什么要用它?
閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中變量的函數(shù),創(chuàng)建閉包的最常見的方式就是在一個(gè)函數(shù)內(nèi)創(chuàng)建另一個(gè)函數(shù),通過(guò)另一個(gè)函數(shù)訪問(wèn)這個(gè)函數(shù)的局部變量,利用閉包可以突破作用鏈域,將函數(shù)內(nèi)部的變量和方法傳遞到外部。
閉包的特性:
- 函數(shù)內(nèi)再嵌套函數(shù)
- 內(nèi)部函數(shù)可以引用外層的參數(shù)和變量
- 參數(shù)和變量不會(huì)被垃圾回收機(jī)制回收
BOM 對(duì)象有哪些,列舉 window 對(duì)象?
- window 對(duì)象,是 JS 的最頂層對(duì)象,其他的 BOM 對(duì)象都是 window 對(duì)象的屬性;
- location 對(duì)象,瀏覽器當(dāng)前URL信息;
- navigator 對(duì)象,瀏覽器本身信息;
- screen 對(duì)象,客戶端屏幕信息;
- history 對(duì)象,瀏覽器訪問(wèn)歷史信息;
new操作符具體干了什么呢?
- 創(chuàng)建一個(gè)空對(duì)象,并且 this 變量引用該對(duì)象,同時(shí)還繼承了該函數(shù)的原型。
- 屬性和方法被加入到 this 引用的對(duì)象中。
- 新創(chuàng)建的對(duì)象由 this 所引用,并且最后隱式的返回 this 。
"==" 和 "===" 有什么不同?
- 相同的是:== 和 === 都是比較等值比較運(yùn)算符,返回的布爾類型的比較結(jié)果。
- 不同的是:
- == 是等值比較運(yùn)算符,使用的是 抽象等值 比較算法。
=== 是嚴(yán)格等值比較運(yùn)算符,使用的 嚴(yán)格等值 比較算法。 - == 運(yùn)算符在比較值的時(shí)候,會(huì)根據(jù)兩者類型是否相同而做不同的處理,
在兩者不同類型的時(shí)候,會(huì)轉(zhuǎn)換類型后進(jìn)行比較:
基本類型會(huì)轉(zhuǎn)成數(shù)字,引用類型會(huì)轉(zhuǎn)成對(duì)象原始值,然后再進(jìn)行比較。
而 === 首先也會(huì)判斷類型是否一致,不同的是如果類型不一致則直接返回 false。
- == 是等值比較運(yùn)算符,使用的是 抽象等值 比較算法。
資料參考:
如何判斷一個(gè)對(duì)象是否屬于某個(gè)類?
使用
instanceof
Javascript中,有一個(gè)函數(shù),執(zhí)行時(shí)對(duì)象查找時(shí),永遠(yuǎn)不會(huì)去查找原型,這個(gè)函數(shù)是?
hasOwnProperty
+ hasOwnProperty函數(shù)方法是返回一個(gè)布爾值,
+ 指出一個(gè)對(duì)象是否具有指定名稱的屬性。
+ 此方法無(wú)法檢查該對(duì)象的原型鏈中是否具有該屬性;
+ 該屬性必須是對(duì)象本身的一個(gè)成員。
+如果 object 具有指定名稱的屬性,那么JavaScript中hasOwnProperty函數(shù)方法返回 true,反之則返回 false。
------
##### window.foo || (window.foo = "bar"); 返回值是什么?
>參考答案:
"bar"
>分析:
|| 又稱為短路或,短路:如果左側(cè)為真,則不再進(jìn)行右側(cè)運(yùn)算,同時(shí)返回左側(cè)表達(dá)式運(yùn)算結(jié)果。
如果左側(cè)為假則執(zhí)行右側(cè)表達(dá)式運(yùn)算,并返回右側(cè)計(jì)算結(jié)果。
上面window.foo是不存在的,所有結(jié)果為undefined,轉(zhuǎn)成boolean就是false,
那么就會(huì)運(yùn)算 window.foo = "bar",
把 "bar" 賦值給 window.foo 的同時(shí),
返回值也是 "foo",所以打印返回結(jié)果是 "bar"
------------------------------------------------------------------------------------------------
##### JavaScript 的 typeof 返回哪些數(shù)據(jù)類型?
> + **基礎(chǔ)類型**包括:Number、String、Boolean、Null、Undefined、Symbol(該類型位 ES2015 中新增類型)
+ **引用類型**包括:Object typeof 運(yùn)算符把類型信息以字符串形式返回,需要注意的是 typeof 返回的類型和 JavaScript 定義的類型有細(xì)微的差異。 typeof 返回七種可能的值:“number”、“string”、“boolean”、“object”、"symbol"、“function”和“undefined”。
------------------------------------------------------------------------------------------------
##### 請(qǐng)寫出以下運(yùn)算結(jié)果:
alert(typeof null); // object
alert(typeof undefined); // undefined
alert(typeof NaN); // number
alert(NaN == undefined); // false
alert(NaN == NaN); // false
var str = "123abc";
alert(typeof str++); // number
alert(str); // NaN
------------------------------------------------------------------------------------------------
##### null 和 undefined 的區(qū)別?
>+ ** null**表示空值,轉(zhuǎn)為數(shù)值時(shí)為0;
+ **undefined**表示"缺少值",就是此處應(yīng)該有一個(gè)值,但是還沒有定義。
+ 變量被聲明了,但沒有賦值時(shí),就等于undefined。
+ 對(duì)象沒有賦值的屬性,該屬性的值為undefined。
+ 函數(shù)沒有返回值時(shí),默認(rèn)返回undefined。
------------------------------------------------------------------------------------------------
##### 例舉至少 3 種強(qiáng)制類型轉(zhuǎn)換和 2 種隱式類型轉(zhuǎn)換?
> 1. 強(qiáng)制類型轉(zhuǎn)換: 明確調(diào)用內(nèi)置函數(shù),強(qiáng)制把一種類型的值轉(zhuǎn)換為另一種類型。強(qiáng)制類型轉(zhuǎn)換主要有:Boolean、Number、String、parseInt、parseFloat
>2. 隱式類型轉(zhuǎn)換: 在使用算術(shù)運(yùn)算符時(shí),運(yùn)算符兩邊的數(shù)據(jù)類型可以是任意的,比如,一個(gè)字符串可以和數(shù)字相加。之所以不同的數(shù)據(jù)類型之間可以做運(yùn)算,是因?yàn)?JavaScript 引擎在運(yùn)算之前會(huì)悄悄的把他們進(jìn)行了隱式類型轉(zhuǎn)換。隱式類型轉(zhuǎn)換主要有:+、–、==、!
------------------------------------------------------------------------------------------------
##### $(".foo div#bar:eq(0)") 請(qǐng)優(yōu)化這段 JQuery 選擇器?
>參考答案:
> ```
$("#bar:eq(0)")
分析:
因?yàn)橛?id 選擇器,所以前面的 .foo div 是沒有必要的。
請(qǐng)解釋 JQuery 中 .end() 的用途?
參考答案:
返回當(dāng)前jq對(duì)象的上級(jí)jq對(duì)象
分析:
- 當(dāng)我們通過(guò)$()會(huì)得到一個(gè)對(duì)象
```
$jq1 = $('#div1');
>2. jq對(duì)象下有一系列的方法,有的方法會(huì)返回一個(gè)新的對(duì)象
// 通過(guò)$jq1的find返回了一個(gè)新的jquery對(duì)象
var $jq2 = $jq1.find('p');
>3. 這個(gè)時(shí)候在$jq2下面有一個(gè)屬性 prevObject,該屬性保存的就是 $jq1,通過(guò)比較 $jq2.prevObject == $jq1,會(huì)發(fā)現(xiàn)返回true。
>4. 通過(guò) prevObject 屬性會(huì)產(chǎn)生一個(gè)類似原型鏈的引用,
而 .end() 方法就是返回就是當(dāng)前 JQ 對(duì)象的 prevObject 對(duì)象,
也就是當(dāng)我們 $jq2.end() 的時(shí)候,返回的就是上層的 $jq1。
------------------------------------------------------------------------------------------------
##### 注冊(cè)賬號(hào)要求以字母開頭,可以包含字母、數(shù)字、下劃線,請(qǐng)寫出驗(yàn)證該賬號(hào)的正則表達(dá)式?
>參考答案:
/^[a-zA-Z]\w+$/
------------------------------------------------------------------------------------------------
##### 請(qǐng)列舉三種減低頁(yè)面加載時(shí)間的方法。(加載時(shí)間指感知的時(shí)間或者實(shí)際加載時(shí)間)
>參考答案:
>1. 減少實(shí)際加載時(shí)間
1. 減少 http 請(qǐng)求(合并文件、合并圖片)
2. 壓縮 JavaScript、CSS 代碼
3. 啟用服務(wù)器壓縮傳輸(如 gzip)
>2. 減少感知時(shí)間
1. script 外部腳本加載放到 html 最后進(jìn)行
2. 按需加載資源(如:只加載當(dāng)前能看到的區(qū)域的圖片)
------------------------------------------------------------------------------------------------
##### "I'm lasagna hog".split("").reverse().jion(""); 語(yǔ)句的返回值是什么?
>參考答案:
goh angasal m'I
>分析:
>1. split(""),拆分字符串,得到數(shù)組:
["I", "'", "m", " ", "l", "a", "s", "a", "g", "n", "a", " ", "h", "o", "g"]
>2. 對(duì)數(shù)組使用.reverse(),翻轉(zhuǎn)數(shù)組,得到:
["g", "o", "h", " ", "a", "n", "g", "a", "s", "a", "l", " ", "m", "'", "I"]
>3. 最后使用join(""),把數(shù)組再次拼接成字符串,得到字符串:
goh angasal m'I
------------------------------------------------------------------------------------------------
##### $(function(){console.log(1)}); 和 window.onload = function(){console.log(2)};執(zhí)行結(jié)果?詳細(xì)說(shuō)明原因?
>參考答案:
>先輸出2,再輸出1
>分析:
>這里重點(diǎn)是:JQ 的 $(function(){}) 和 window.onload = function(){},并不等同
>1. **window.onload **是頁(yè)面資源加載完成后觸發(fā)的事件,
比如頁(yè)面中有圖片需要加載,那么onload是等圖片加載完成以后才觸發(fā)的。
>2. **$(function) **監(jiān)聽的是 DOMContentLoaded 事件,而該事件只需要把 HTML 結(jié)構(gòu)加載完成就會(huì)觸發(fā)
(一般我們js操作的就是頁(yè)面元素,所以只需要等結(jié)構(gòu)加載完成能操作頁(yè)面元素就可以了)
所以該事件會(huì)比 onload 事件要先觸發(fā),所以 2 先執(zhí)行。
------------------------------------------------------------------------------------------------
##### 請(qǐng)指出 JQuery中 ".bind()"、".live()" 和 "delegate()" 的區(qū)別?
>參考答案:
>1. bind:把函數(shù)直接綁定到指定元素的指定事件上。
>2. live:把函數(shù)綁定到document上,接收選擇器和事件類型作為參數(shù),當(dāng)觸發(fā)一個(gè)元素的事件的時(shí)候,會(huì)利用事件冒泡到document上這一特性,判斷事件目標(biāo)元素和綁定參數(shù)中的選擇器是否匹配,如果匹配則執(zhí)行綁定函數(shù)的執(zhí)行。
3. delegate:和live有點(diǎn)類似,但是可以指定綁定元素,而不是document,其他和live一致,但是比live更加靈活。
------------------------------------------------------------------------------------------------
##### 請(qǐng)使用標(biāo)準(zhǔn)的 JSON 格式封裝一組學(xué)生信息數(shù)據(jù),內(nèi)容包括:姓名、性別、住址(包括城市、街道、門牌號(hào)、地鐵線)
[
{
"name": "北京妙味",
"gender": "男",
"address": {
"city": "北京",
"street": "西二旗輝煌國(guó)際"
"RoomNo": "6樓319室",
"subwayLine": "13"
}
},
{
"name": "上海妙味",
"gender": "男",
"address": {
"city": "上海",
"street": "閔行區(qū)新龍路七寶寶龍城"
"RoomNo": "T4樓9層902室",
"subwayLine": "9"
}
}
]
------------------------------------------------------------------------------------------------
##### 客戶查詢手機(jī)消費(fèi)清單要求:
實(shí)現(xiàn) A、B、C 三個(gè)異步接口,A 接口需傳參 user_name、mobi(用戶姓名和手機(jī)號(hào)碼),請(qǐng)求成功返回該用戶此手機(jī)號(hào)碼的消費(fèi)清單信息,user_name 可通過(guò)接口 B 獲取,mobi 可通過(guò)接口 C 獲取,請(qǐng)使用 JQuery 寫出具體的實(shí)現(xiàn)方法?
```javascript
function getUserName() {
return $.ajax('/get_user_name.php');
}
function getMobi() {
return $.ajax('/get_mobi.php');
}
$.when(getUserName(), getMobi()).then(function(data1, data2) {
$.ajax({
url: 'getConsumerList.php',
data: {
user_name: data1[0],
mobi: data2[0],
}
}).success(function(consumerList) {
//consumerList
});
}, function() {
console.log('獲取用戶名或手機(jī)號(hào)未成功');
});
請(qǐng)簡(jiǎn)述 AJAX 及基本步驟?
- 初始化ajax對(duì)象
- 連接地址,準(zhǔn)備數(shù)據(jù)
- 發(fā)送請(qǐng)求
- 接收數(shù)據(jù)(正在接收,尚未完成)
- 接收數(shù)據(jù)完成
//初始化ajax對(duì)象
var xhr = new XMLHttpRequest();
//連接地址,準(zhǔn)備數(shù)據(jù)
xhr.open(“方式”,”地址”,是否為異步);
//接收數(shù)據(jù)完成觸發(fā)的事件
xhr.onload =function(){}
//發(fā)送數(shù)據(jù)
xhr.send();
js延遲加載的方式有哪些?
normaldefer和async、動(dòng)態(tài)創(chuàng)建DOM方式(用得最多)、按需異步載入js
同步和異步的區(qū)別?
概念1:同步異步可以說(shuō)是對(duì)被請(qǐng)求方來(lái)說(shuō)的,被請(qǐng)求者使用什么方式來(lái)告知處理結(jié)果。
- 首先同步異步于阻塞非阻塞并沒有關(guān)系。同步異步主要是事情做完以后,如何進(jìn)行處理、或者說(shuō)關(guān)注的是一 種消息通信機(jī)制。
- 同步的情況下,是由處理消息者自己去等待消息是否被觸發(fā);
- 異步的情況下是由觸發(fā)機(jī)制來(lái)通知處理消息者;
概念2:同步可以是阻塞的也可以是非阻塞的,異步也是如此。
- 阻塞非阻塞,主要是對(duì)于請(qǐng)求者而言的。
- 阻塞:發(fā)出請(qǐng)求等待結(jié)果返回,然后再處理后續(xù)的事情;
- 非阻塞:發(fā)出請(qǐng)求不等待結(jié)果返回,可以接著做后續(xù)的事情;
GET和POST的區(qū)別,何時(shí)使用POST?
- GET:一般用于查詢數(shù)據(jù),使用URL傳遞參數(shù),由于瀏覽器對(duì)地址欄長(zhǎng)度有限制,所以對(duì)使用get方式所發(fā)送信息的數(shù)量有限制,同時(shí)瀏覽器會(huì)記錄(歷史記錄,緩存)中會(huì)保留請(qǐng)求地址的信息,包括地址后面的數(shù)據(jù)。get 只能發(fā)送普通格式(URL 編碼格式)的數(shù)據(jù)。
- POST:一般用于向服務(wù)器發(fā)送數(shù)據(jù),對(duì)所發(fā)送的數(shù)據(jù)的大小理論上是沒有限制,瀏覽器會(huì)緩存記錄地址,但是不會(huì)記錄 post 提交的數(shù)據(jù)。post 可以發(fā)送純文本、URL編碼格式、二進(jìn)制格式的字符串,形式多樣。
- 在以下情況中,請(qǐng)使用 POST 請(qǐng)求:
- 以提交為目的的請(qǐng)求(類似語(yǔ)義化,get 表示請(qǐng)求,post 表示提交);
- 發(fā)送私密類數(shù)據(jù)(用戶名、密碼)(因?yàn)闉g覽器緩存記錄特性);
- 向服務(wù)器發(fā)送大量數(shù)據(jù)(數(shù)據(jù)大小限制區(qū)別);
- 上傳文件圖片時(shí)(數(shù)據(jù)類型區(qū)別);
AJAX 的局限性?
- AJAX 不支持瀏覽器 back 按鈕。
- 安全問(wèn)題 AJAX 暴露了與服務(wù)器交互的細(xì)節(jié)。
- 對(duì)搜索引擎的支持比較弱。不會(huì)執(zhí)行你的 JS 腳本,只會(huì)操作你的網(wǎng)頁(yè)源代碼;
- 跨域請(qǐng)求有一定限制。解決方式:jsonp;
new 操作符具體干了什么呢?
當(dāng)使用 new 操作符調(diào)用構(gòu)造函數(shù),函數(shù)實(shí)際會(huì)經(jīng)歷如下步驟:
- 創(chuàng)建一個(gè)新對(duì)象;
- 把函數(shù)中上下文(作用域)對(duì)象this指向該對(duì)象;
- 執(zhí)行代碼,通過(guò)this給新對(duì)象添加屬性或方法;
- 返回對(duì)象;
JavaScript 原型,原型鏈 ? 有什么特點(diǎn)?
- JavaScript 原型: 每創(chuàng)建一個(gè)函數(shù),函數(shù)上都有一個(gè)屬性為 prototype,它的值是一個(gè)對(duì)象。 這個(gè)對(duì)象的作用在于當(dāng)使用函數(shù)創(chuàng)建實(shí)例的時(shí)候,那么這些實(shí)例都會(huì)共享原型上的屬性和方法。
- 原型鏈: 在 JavaScript 中,每個(gè)對(duì)象都有一個(gè)指向它的原型(prototype)對(duì)象的內(nèi)部鏈接(proto)。這個(gè)原型對(duì)象又有自己的原型,直到某個(gè)對(duì)象的原型為 null 為止(也就是不再有原型指向)。這種一級(jí)一級(jí)的鏈結(jié)構(gòu)就稱為原型鏈(prototype chain)。 當(dāng)查找一個(gè)對(duì)象的屬性時(shí),JavaScript 會(huì)向上遍歷原型鏈,直到找到給定名稱的屬性為止;到查找到達(dá)原型鏈的頂部(Object.prototype),仍然沒有找到指定的屬性,就會(huì)返回 undefined。
實(shí)現(xiàn)對(duì)數(shù)組進(jìn)行亂序
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var sign = 1;
a.sort(function(a, b) {
return Math.random() - 0.5
});
實(shí)現(xiàn)一個(gè)函數(shù) clone(),可以對(duì) JavaScript 中的5種主要的數(shù)據(jù)類型(包括 Number、String、Object、Array、Boolean)進(jìn)行值復(fù)制。
這道題考察了以下知識(shí)點(diǎn):
- 使用 typeof 判斷值得類型;
- 使用 toString 區(qū)分?jǐn)?shù)組和對(duì)象;
- 遞歸函數(shù)的使用;
function clone(obj) {
//判斷是對(duì)象,就進(jìn)行循環(huán)復(fù)制
if (typeof obj === 'object' && typeof obj !== 'null') {
// 區(qū)分是數(shù)組還是對(duì)象,創(chuàng)建空的數(shù)組或?qū)ο? var o = Object.prototype.toString.call(obj).slice(8, -1) === "Array" ? [] : {};
for (var k in obj) {
// 如果屬性對(duì)應(yīng)的值為對(duì)象,則遞歸復(fù)制
if(typeof obj[k] === 'object' && typeof obj[k] !== 'null'){
o[k] = clone(obj[k])
}else{
o[k] = obj[k];
}
}
}else{ //不為對(duì)象,直接把值返回
return obj;
}
return o;
}
判斷代碼輸出結(jié)果
var test = 2;
console.log("test is a Number--" + (test.constructor == Number)); //true
function test1() {};
var t1 = new test1();
var test2 = "37degree";
console.info("typeof test--" + typeof test); //number
console.info("typeof test1--" + typeof test1); //function
console.info("typeof t1--" + typeof t1); //object
console.info("t1 instanceof test1--" + (t1 instanceof Object)); //true
console.info("test instanceof Array--" + (test instanceof Array)); //false
首先涉及到 JS 預(yù)解析的概念,這里的 變量申明 和 函數(shù)申明 都會(huì)被預(yù)解析處理,知道這個(gè)以后,那么下面一個(gè)一個(gè)來(lái)分析:
- 第一個(gè):
因?yàn)?test = 2 先執(zhí)行了,所以這里 test 的值已經(jīng)是一個(gè)數(shù)字了,那么 test.constructr 就是 Number,所以下面這個(gè)打印 true;
console.log("test is a Number--" + (test.constructr == Number))
- 第二個(gè):
原因同上,需要注意 typeof 得到的結(jié)果 number 的 n 是小寫的!
- 第三個(gè):
test1 是一個(gè)函數(shù),typeof 值為 function;
- 第四個(gè):
通過(guò) new 得到的結(jié)果一定是一個(gè) Object,所以 t1 的 typeof 結(jié)果為 object;
- 第五個(gè):
這個(gè)題有點(diǎn)誤導(dǎo)人的,首先出題人寫的是 instanceof test1,但真正計(jì)算的卻是 instanceof Object,請(qǐng)注意它們的差異!不過(guò)不影響結(jié)果的,結(jié)果都是 true;
- 第六個(gè):
test 是一個(gè)數(shù)字,所以他的構(gòu)造函數(shù)應(yīng)是 Number,而不是 Array,所以結(jié)果為 false;
解釋 call 與 apply 的區(qū)別,并寫出下面代碼輸出的結(jié)果:
function add(a, b) {
alert(a + b);
}
function sub(a, b) {
alert(a - b);
}
add.call(sub, 3, 1) //結(jié)果為 4
- call 和 apply 都是函數(shù)下的一個(gè)靜態(tài)方法,可以通過(guò)函數(shù) .call() 或 函數(shù) .apply() 的方式來(lái)間接調(diào)用該函數(shù),通過(guò) call 或 apply 執(zhí)行時(shí)候的第一個(gè)參數(shù)改變?cè)摵瘮?shù)執(zhí)行過(guò)程中的上下文對(duì)象(this),如果第一個(gè)參數(shù)不存在或者設(shè)置成 null/undefined,那么該函數(shù)執(zhí)行過(guò)程中的上下文對(duì)象指向全局上下文對(duì)象,在 JavaScript 中指向了 window 對(duì)象。
- 不同之處在于后續(xù)參數(shù)上,call 第一個(gè)參數(shù)以后的參數(shù)值將被一一對(duì)應(yīng)的賦值給源函數(shù)的形參,而 apply 則是傳入一個(gè)數(shù)組,間接傳給函數(shù)的 arguments 對(duì)象。
研究下面 JS 代碼是否有問(wèn)題,有問(wèn)題請(qǐng)描述問(wèn)題并解決,沒有問(wèn)題請(qǐng)回答最終結(jié)果。
代碼一:
var a = 10;
sayHi();
function sayHi() {
a = a + 10;
console.log(a);
return a;
}
console.info(a);
console.info(sayHi() + 10);
代碼一分析:
//申明全局 a,值為 10
var a = 10;
sayHi(); //調(diào)用
function sayHi() {
a = a + 10; //這里的 a 是全局的 a,而不是局部的,所以執(zhí)行的結(jié)果是把全局 a 設(shè)置成了 20
console.log(a); //20
return a; //返回 20
}
console.info(a); //全局 a 已經(jīng)是 20 了
console.info(sayHi() + 10); //首先又一次執(zhí)行了 sayHi(),結(jié)果把 a 改成了 30,然后打印了一次 30,執(zhí)行完以后返回了 30,然后在加 10,打印結(jié)果 40
代碼二:
var a = 10;
sayHi();
function sayHi() {
var a = a + 10;
console.info(a);
return a;
}
console.info(a);
console.info(sayHi() + 10);
代碼二分析:
//申明全局 a,值為 10
var a = 10;
//調(diào)用
sayHi();
function sayHi() {
//注意這里有一個(gè) var,那么這里的 a 就是局部變量了,另外還是需要預(yù)解析一下,其實(shí)可以這么去看代碼:
/*
var a; //申明未賦值,默認(rèn)值是 undefined
a = a + 10; // a = undefined + 10 結(jié)果是 NaN
*/
var a = a + 10;
console.info(a); // NaN
return a; //返回 NaN
}
console.info(a); //這個(gè)還是全局的 a,所以結(jié)果 10
console.info(sayHi() + 10); //依據(jù)上面的分析,這里的 sayHi 會(huì)打印一次 NaN,然后加 10,結(jié)果還是 NaN
代碼三:
function init() {
var ps = document.getElementsByTagName("p"); //body內(nèi)有四個(gè)p標(biāo)簽
for (var i=0; i<ps.length; i++) {
ps[i].onclick = function() {
console.info(i);
}
}
}
代碼三分析:
// 1. console.info(i); 的執(zhí)行是需要用戶點(diǎn)擊后執(zhí)行的,當(dāng)用戶點(diǎn)擊的時(shí)候,for 循環(huán)的執(zhí)行已經(jīng)結(jié)束,那么 i 的值已經(jīng)被設(shè)置成了 4,也就說(shuō)當(dāng)用戶去點(diǎn)擊的時(shí)候 i 的值已經(jīng)是 4 了;
// 2. 當(dāng) console.info(i); 執(zhí)行的時(shí)候,會(huì)根據(jù)作用域鏈去查找 i,這樣會(huì)找到 for 中定義的全局 i,這個(gè)時(shí)候不管點(diǎn)擊那個(gè) p 標(biāo)簽其實(shí)打印的都是全局 i 變量,所以結(jié)果都是統(tǒng)一的 4;
// 解決方案一:
function init() {
var ps = document.getElementsByTagName("p");
for (var i=0; i<ps.length; i++) {
(function(n) {
ps[n].onclick = function() {
console.info(n);
}
})(i);
}
}
// 解決方案二:
function init() {
var ps = document.getElementsByTagName("p");
for (let i=0; i<ps.length; i++) {
ps[i].onclick = function() {
console.info(i);
}
}
}
設(shè)計(jì)一個(gè)列表,包含:地域、人數(shù)、百分比、時(shí)間。請(qǐng)實(shí)現(xiàn)按照 人數(shù) 與 時(shí)間 的排序算法。
var data = [
{
area: '深圳',
percentage: 15,
number: 80,
staytime: 2
},
{
area: '北京',
percentage: 30,
number: 150,
staytime: 4
},
{
area: '廣州',
percentage: 25,
number: 60,
staytime: 3
},
{
area: '上海',
percentage: 30,
number: 100,
staytime: 4
}
];
/*
* 根據(jù)指定的字段和規(guī)則排序數(shù)據(jù)
* data Array 要排序的數(shù)據(jù)
* field string 排序依據(jù)的字段
* rule string 排序規(guī)則 DESC / ASC
* throw
* data is invalid : 要排序的數(shù)據(jù)不存在或類型不正確
* field is invalid : 排序參考字段不存在
* return Array 排序后的數(shù)據(jù)
*/
function mySort(data, field, rule) {
if (!(data instanceof Array)) {
throw new TypeError('data is invalid');
}
if ( !(field in data[0]) ) {
throw new RangeError('field is invalid');
}
if ( !rule || ['DESC','ASC'].indexOf( (rule = rule.toString().toUpperCase()) ) == -1 ) {
rule = 'DESC';
}
data.sort(function(a, b) {
var v = a[field] - b[field];
return rule == 'ASC' ? v : -v;
});
}
mySort(data, 'number', 'desc');
console.dir( data );
請(qǐng)列舉一些瀏覽器兼容性問(wèn)題?以及提高性能方面的方案
JS兼容問(wèn)題
- JSON 解析問(wèn)題:
ecmascript5 通過(guò) JSON 對(duì)象進(jìn)行處理,ecmascript5 之前通過(guò) eval 進(jìn)行解析;
- 自定義屬性問(wèn)題:
IE 下,可以使用獲取常規(guī)屬性的方法來(lái)獲取自定義屬性,也可以使用 getAttribute() 獲取自定義屬性;
Firefox下,只能使用 getAttribute( )獲取自定義屬性。
解決方法:
統(tǒng)一通過(guò) getAttribute() 獲取自定義屬性,不過(guò)更推薦直接通過(guò) “點(diǎn)” 運(yùn)算符訪問(wèn)元素屬性。
- 事件對(duì)象兼容性問(wèn)題:
非標(biāo)準(zhǔn) IE 和 chrome 下可以通過(guò)全局 event 對(duì)象來(lái)獲取,標(biāo)準(zhǔn)(包括標(biāo)準(zhǔn) IE,chrome 等)瀏覽器通過(guò)事件函數(shù)的第一個(gè)參數(shù)傳入。
- 事件源對(duì)象
IE 下使用 event.srcElement,標(biāo)準(zhǔn)下使用 event.target 來(lái)獲取。
- 阻止事件冒泡
通常可以通過(guò) event.cancelBubble = false 來(lái)阻止,但是標(biāo)準(zhǔn)推薦使用 event.stopPropagation() 方法來(lái)阻止;
- 事件默認(rèn)行為的阻止
DOM1 事件綁定中(屬性 on... 的方式)可以通過(guò) return false 來(lái)阻止,但是在 DOM2 的事件綁定中(addEventListener)中,只能通過(guò) event.preventDefault() 方法來(lái)阻止。
其實(shí)還有很多……懶得寫了~~ ㄟ(▔,▔)ㄏ
**JS 優(yōu)化問(wèn)題 **
- 最小化 DOM 訪問(wèn)次數(shù),盡可能在 JS 端執(zhí)行;
- 如果需要多次訪問(wèn)某個(gè) DOM 節(jié)點(diǎn),請(qǐng)使用局部變量存儲(chǔ)對(duì)它的引用;
- 小心處理 HTML 集合,因?yàn)樗鼘?shí)時(shí)連系著底層的文檔,把集合的長(zhǎng)度緩存到一個(gè)變量中,并在迭代中使用它,如果需要經(jīng)常操作集合,建議把它拷貝到一個(gè)數(shù)組中;
- 如果可能的話,使用速度更快的 API,比如 querySelectorAll 和 firstElementChild;
- 要留意重繪和重排,批量修改樣式時(shí),“離線”操作 DOM 樹。使用緩存,并減少訪問(wèn)布局的次數(shù);
- 使用事件委托來(lái)減少事件處理器的數(shù)量;
- 避免多次訪問(wèn)對(duì)象成員或函數(shù)中的全局變量,盡量將它們賦值給局部變量以緩存;
- 能用 CSS 解決的問(wèn)題,盡量不用 JS 去解決;
寫一個(gè)通用的事件偵聽器函數(shù)?
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ù): 操作的元素,事件名稱 ,事件處理程序
addEvent: function(element, type, handler) {
if (element.addEventListener) {
//事件類型、需要執(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;
}
};
eval是做什么的?
- 它的功能是把對(duì)應(yīng)的字符串解析成JS代碼并運(yùn)行;
- 應(yīng)該避免使用eval,不安全,非常耗性能(2次,一次解析成js語(yǔ)句,一次執(zhí)行)。
- 由JSON字符串轉(zhuǎn)換為JSON對(duì)象的時(shí)候可以用eval,例如
var obj =eval('('+ str +')');
談?wù)凾his對(duì)象的理解。
- this總是指向函數(shù)的直接調(diào)用者(而非間接調(diào)用者);
- 如果有new關(guān)鍵字,this指向new出來(lái)的那個(gè)對(duì)象;
- 在事件中,this指向觸發(fā)這個(gè)事件的對(duì)象,特殊的是,IE中的attachEvent中的this總是指向全局對(duì)象Window;
javascript創(chuàng)建對(duì)象的幾種方式?
javascript創(chuàng)建對(duì)象簡(jiǎn)單的說(shuō),無(wú)非就是使用內(nèi)置對(duì)象或各種自定義對(duì)象,當(dāng)然還可以用JSON;但寫法有很多種,也能混合使用。
- 對(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)在賣"+this.price+"萬(wàn)元");
}
var camry =new Car("凱美瑞",27);
camry.sell();
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);//得到被繼承的屬性 }
持續(xù)更新........................
摘錄自 妙味課堂