1.1、請解釋一下什么是閉包
當函數可以記住並訪問所在的作用域時,就產生了閉包,即使函數是在當前作用域之外執行。閉包有如下特性:
a. JavaScript允許你使用在當前函數以外定義的變量
b. 即使外部函數已經返回,當前函數仍然可以引用在外部函數所定義的變量
c. 閉包可以更新外部變量的值
d. 用閉包模擬私有方法
由於閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題。
在定時器、事件監聽器、Ajax請求、跨窗口通信、Web Workers或者任何其他的異步(或者同步)任務中,只要使用了回調函數,實際上就是在使用閉包!
1.2、call 和 apply 的區別是什么?
call 和 apply 就是為了改變函數體內部 this 的指向。
區別是從第二個參數起,call 需要把參數按順序傳遞進去,而 apply 則是把參數放在數組里。
當參數明確時用call與apply都行, 當參數不明確時可用apply給合arguments
1.3、如何使用原生 Javascript 代碼深度克隆一個對象(注意區分對象類型)
用遞歸的方式克隆
function deepClone(obj) { var o,i,j,k; if(typeof(obj)!="object" || obj===null)return obj; if(obj instanceof(Array)) { o=[]; i=0;j=obj.length; for(;i<j;i++) { if(typeof(obj[i])=="object" && obj[i]!=null) { o[i]=arguments.callee(obj[i]); } else { o[i]=obj[i]; } } } else { o={}; for(i in obj) { if(typeof(obj[i])=="object" && obj[i]!=null) { o[i]=arguments.callee(obj[i]); } else { o[i]=obj[i]; } } } return o; } var scheduleClone = deepClone(schedule) scheduleClone.data[0].contactList.phone[0] = 99999999999 console.log('方法1 深度克隆') console.log(scheduleClone) console.log(JSON.stringify(schedule)) console.log(JSON.stringify(scheduleClone))
用js原生的json序列化的方式
var scheduleClone2 = JSON.parse(JSON.stringify(schedule)); console.log(scheduleClone2) scheduleClone2.data[0].contactList.phone[0] = 8888888 console.log(JSON.stringify(schedule)) console.log(JSON.stringify(scheduleClone2))
1.4、 jQuery中 $(′.class′)和$('div.class') 哪個效率更高?
jQuery內部使用Sizzle引擎,處理各種選擇器。Sizzle引擎的選擇順序是從右到左,所以這條語句是先選.class,
第二個會直接過濾出div標簽,而第一個就不會過濾了,將所有相關標簽都列出。
1.5、實現輸出document對象中所有成員的名稱和類型
for(o in document){ type = Object.prototype.toString.call(document[o]); if(type =='[object Function]'){ console.log(o+":"+typeof o) } }
1.6、獲得一個DOM元素的絕對位置
offsetTop:返回當前元素相對於其 offsetParent 元素的頂部的距離
offsetLeft:返回當前元素相對於其 offsetParent 元素的左邊的距離
getBoundingClientRect():返回值是一個DOMRect對象,它包含了一組用於描述邊框的只讀屬性——left、top、right和bottom,屬性單位為像素
1.7、如何利用JS生成一個table?
首先是用createElement創建一個table,再用setAttribute設置table的屬性,
然后用for循環設置tr和td的內容,用appendChild拼接內容,設置td的時候還用到innerHTML和style.padding。
var row, cell, table = document.createElement("table"); table.setAttribute("border", 1); for (var i = 0; i < 10; i++) { row = document.createElement("tr"); table.appendChild(row); for (var j = 0; j < 10; j++) { cell = document.createElement("td"); cell.style.padding = "10px"; cell.innerHTML = "單元格內容"; row.appendChild(cell); } }; table.appendChild(row); document.body.appendChild(table);
1.8、實現預加載一張圖片,加載完成后顯示在網頁中並設定其高度為50px,寬度為50px
var img = new Image(); img.src = "1.jpg"; img.onload = function() { img.height = 50; img.width = 50; } document.body.appendChild(img);
1.9、假設有一個4行tr的table,將table里面tr順序顛倒
先是通過table.tBodies[0].rows獲取到當前tbody中的行,接下來是兩種方法處理。獲取到的行沒有reverse這個方法。
1.第一種是將這些行push到另外一個數組中
var tableTemple=document.querySelector("#tableTemple"); var table_rows=tableTemple.tBodies[0].rows; var newAry=[]; for(var i=0;i<table_rows.length;i++){ newAry.unshift(table_rows[i]); }; for(i in newAry){ tableTemple.tBodies[0].appendChild(newAry[i]); }
2.第二種是用Array.prototype.slice.call()將那些行變成數組,接着用reverse倒敘,table再appendChild。
var tableTemple=document.querySelector("#tableTemple"); var table_rows=tableTemple.tBodies[0].rows; var trs = Array.prototype.slice.call(table_rows, 0);Array.prototype.slice.call(arguments)//能將具有length屬性的對象轉成數組 trs.reverse(); for(var i=0;i<trs.length;i++){ tableTemple.tBodies[0].appendChild(trs[i]); }
1.10、模擬一個HashTable類,一個類上注冊四個方法:包含有add、remove、contains、length方法
先是在構造函數中定義一個數組,然后用push模擬add,splice模擬remove。
四個方法都放在了prototype上面。
function HashTable(){ this.value=[]; }; HashTable.prototype.add=function(val){ this.value.push(val); }; HashTable.prototype.remove=function(index){ this.value.splice(index,1) } HashTable.prototype.contains=function(val){ var aValue=this.value; for(i in aValue){ if(val==aValue[i]){ return true; } } return false; } HashTable.prototype.length = function() { return this.value.length; }
1.11、Ajax讀取一個XML文檔並進行解析的實例
a. 初始化一個HTTP請求,IE以ActiveX對象引入。 后來標准瀏覽器提供了XMLHttpRequest類,它支持ActiveX對象所提供的方法和屬性
b. 發送請求,可以調用HTTP請求類的open()和send()方法
c. 處理服務器的響應,通過http_request.onreadystatechange = nameOfTheFunction。來指定函數
//創建請求對象 function create() { if (window.XMLHttpRequest) { // Mozilla, Safari, ... http_request = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE http_request = new ActiveXObject("Microsoft.XMLHTTP"); } return http_request; } //發起請求 function makeRequest(url) { http_request = create(); http_request.onreadystatechange = alertContents; http_request.open('GET', url, true); http_request.send(null); } //響應函數 function alertContents() { if (http_request.readyState == 4) { if (http_request.status == 200) { var xmldoc = http_request.responseXML; var root_node = xmldoc.getElementsByTagName('root').item(0); alert(root_node.firstChild.data); } else { alert('There was a problem with the request.'); } } }
1.12、JS如何實現面向對象和繼承機制?
創建對象方法:
a. 利用json創建對象
b. 使用JavaScript中的Object類型
c. 通過創建函數來生成對象
繼承機制:
a. 構造函數綁定,使用call或apply方法,將父對象的構造函數綁定在子對象上
b. prototype模式,繼承new函數的模式
c. 直接繼承函數的prototype屬性,對b的一種改進
d. 利用空對象作為中介
e. 在ECMAScript5中定義了一個新方法Object.create(),用於創建一個新方法
f. 拷貝繼承,把父對象的所有屬性和方法,拷貝進子對象,實現繼承。
1.13、JS模塊的封裝方法,比如怎樣實現私有變量,不能直接賦值,只能通過公有方法
a. 通過json生成對象的原始模式,多寫幾個就會非常麻煩,也不能反映出它們是同一個原型對象的實例
b. 原始模式的改進,可以寫一個函數,解決代碼重復的問題。同樣不能反映出它們是同一個原型對象的實例
c. 構造函數模式,就是一個普通函數,不過內部使用了this變量,但是存在一個浪費內存的問題。
d. Prototype模式,每一個構造函數都有一個prototype屬性,指向另一個對象。這個對象的所有屬性和方法,都會被構造函數的實例繼承,可以把那些不變的屬性和方法,直接定義在prototype對象上。Prototype模式的驗證方法:isPrototypeOf()、hasOwnProperty()和in運算符。
//通過json生成對象的原始模式 var cat1 = {}; // 創建一個空對象 cat1.name = "大毛"; // 按照原型對象的屬性賦值 cat1.color = "黃色"; var cat2 = {}; cat2.name = "二毛"; cat2.color = "黑色"; //原始模式的改進 function Cat2(name,color){ return { name:name, color:color } } //構造函數模式 function Cat3(name,color){ this.name=name; this.color=color; } //Prototype模式 function Cat4(name,color){ this.name = name; this.color = color; } Cat4.prototype.type = "貓科動物"; Cat4.prototype.eat = function(){alert("吃老鼠")}; //Prototype驗證方法 var human = function() {} var socrates = Object.create(human); console.log(human.isPrototypeOf(socrates)); //=> true //console.log(socrates.prototype.isPrototypeOf(human)); console.log(socrates instanceof human); //=> false //console.log(socrates.prototype)
1.14、對this指針的理解,可以列舉幾種使用情況?
this實際上是在函數被調用時發生的綁定,它指向什么完全取決於函數在哪里被調用。
this指的是:調用函數的那個對象。
a. 純粹的函數調用,屬於全局性調用,因此this就代表全局對象Global。
b. 作為對象方法的調用,這時this就指這個上級對象。
c. 作為構造函數調用,就是通過這個函數new一個新對象(object)。這時,this就指這個新對象。
d. apply與call的調用,它們的作用是改變函數的調用對象,它的第一個參數就表示改變后的調用這個函數的對象。
//a. 純粹的函數調用 function test1() { this.x = 1; console.log(this.x); } test1(); // 1 var y = 1; function test2() { console.log(this.y); } test2(); // 1 var z = 1; function test3() { this.z = 2; } test3(); console.log(z); //2 //b. 作為對象方法的調用 function test4() { console.log(this.x); } var o = {}; o.x = 10; o.m = test4; o.m(); // 10 //c.作為構造函數調用 function test5() { this.x = 1; } var o = new test5(); console.log(o.x); // 1 //d.apply與call的調用 var x = 0; function test6() { console.log(this.x); } var o = {}; o.x = 1; o.m = test6; o.m.apply(o); //1
1.15、在JavaScript中,常用的綁定事件的方法有哪些?
a. 在DOM元素中直接綁定,DOM元素,可以理解為HTML標簽,onXXX="JavaScript Code"。
b. 在JavaScript代碼中綁定,elementObject.onXXX=function(){},通稱為DOM0事件系統。
c. 綁定事件監聽函數,標准瀏覽器使用 addEventListener() ,IE11以下版本attachEvent() 來綁定事件監聽函數,通稱為DOM2事件系統。
1.16、解釋下javascript的冒泡和捕獲
<div id="click1">
<div id="click2">
<div id="click3">事件</div>
</div>
</div>
a. Netscape主張元素1的事件首先發生,這種事件發生順序被稱為捕獲型
b. 微軟則保持元素3具有優先權,這種事件順序被稱為冒泡型
c. W3C選擇了一個擇中的方案。任何發生在w3c事件模型中的事件,首是進入捕獲階段,直到達到目標元素,再進入冒泡階段
事件監聽函數addEventListener()的第三個參數就是控制方法是捕獲還是冒泡
click1 = document.getElementById("click1"); click2 = document.getElementById("click2"); click3 = document.getElementById("click3"); click1.addEventListener("click", returnTarget, false); click2.addEventListener("click", returnTarget, false); click3.addEventListener("click", returnTarget, false); function returnTarget(event) { console.log(event.currentTarget.id); //event.stopPropagation(); }
1.17、jQuery的特點
a. 一款輕量級的js庫
b. 豐富快速的DOM選擇器
c. 鏈式表達式
d. 事件、樣式、動畫等特效支持
e. Ajax操作封裝,支持跨域
f. 跨瀏覽器兼容
g. 插件擴展開發
1.18、Ajax有哪些好處和弊端?
優點:
a. 無刷新更新數據
b. 異步與服務器通信
c. 前端和后端負載平衡
d. 基於標准被廣泛支持
e. 界面與應用分離
缺點:
a. AJAX干掉了Back和History功能,即對瀏覽器機制的破壞
b. AJAX的安全問題
c. 對搜索引擎支持較弱
d. 違背URL和資源定位的初衷
參考《AJAX工作原理及其優缺點》
1.19、null和undefined的區別?
null:
a. null是一個表示"無"的對象,轉為數值時為0
b. null表示"沒有對象",即該處不應該有值。
undefined:
a. undefined是一個表示"無"的原始值,轉為數值時為NaN。
b. undefined表示"缺少值",就是此處應該有一個值,但是還沒有定義。
1.20、new操作符具體干了什么呢?
a. 一個新對象被創建。它繼承自函數原型
b. 構造函數被執行。執行的時候,相應的傳參會被傳入
c. 上下文(this)會被指定為這個新實例
d. 如果構造函數返回了一個“對象”,那么這個對象會取代整個new出來的結果
1.21、js延遲加載的方式有哪些?
a. 將script節點放置在最后</body>之前
b. 使用script標簽的defer和async屬性,defer屬性為延遲加載,是在頁面渲染完成之后再進行加載的,而async屬性則是和文檔並行加載
c. 通過監聽onload事件,動態添加script節點
d. 通過ajax下載js腳本,動態添加script節點
1.22、如何解決跨域問題?
a. JSONP(JSON with Padding),填充式JSON
b. iframe跨域
c. HTML5的window.postMessage方法跨域
d. 通過設置img的src屬性,進行跨域請求
e. 跨域資源共享(CORS),服務器設置Access-Control-Allow-OriginHTTP響應頭之后,瀏覽器將會允許跨域請求
1.23、documen.write和 innerHTML的區別
write:
a. 改變 HTML 輸出流
b. 當在文檔加載之后使用 document.write(),這會覆蓋該文檔。例如onload事件中
c. 輸入css的style標簽能改變樣式,例如document.write("<style>b{color:red;font-weight:bold;}</style>");
innerHTML:
a. 改變 HTML 內容
b. 輸入css的style標簽不能改變樣式。也是能改變樣式的
1.24、哪些操作會造成內存泄漏?
a. 當頁面中元素被移除或替換時,若元素綁定的事件仍沒被移除,在IE中不會作出恰當處理,此時要先手工移除事件,不然會存在內存泄露。
b. 在IE中,如果循環引用中的任何對象是 DOM 節點或者 ActiveX 對象,垃圾收集系統則不會處理。
c. 閉包可以維持函數內局部變量,使其得不到釋放。
d. 在銷毀對象的時候,要遍歷屬性中屬性,依次刪除,否則會泄漏。
1.25、JavaScript中的變量聲明提升?
函數聲明和變量聲明總是被JavaScript解釋器隱式地提升到包含他們的作用域的最頂端。
function優先聲明於var。
函數表達式中只會提升名稱,函數體只有在執行到賦值語句時才會被賦值。
function foo() {
bar();
var x = 1;
}
function foo() {//等同於
var x;
bar();
x = 1;
}
function test() {
foo(); // TypeError "foo is not a function"
bar(); // "this will run!"
var foo = function () { }// 函數表達式被賦值給變量'foo'
function bar() { }// 名為'bar'的函數聲明
}
1.26、如何判斷當前腳本運行在瀏覽器還是node環境中?
通過判斷Global對象是否為window,如果是window,當前腳本運行在瀏覽器中
1.27、什么是 "use strict"
ECMAscript 5添加了第二種運行模式:"嚴格模式"(strict mode)
設立"嚴格模式"的目的,主要有以下幾個:
a. 消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為;
b. 消除代碼運行的一些不安全之處,保證代碼運行的安全;
c. 提高編譯器效率,增加運行速度;
d. 為未來新版本的Javascript做好鋪墊。
注:經過測試IE6,7,8,9均不支持嚴格模式
1.28、eval是做什么的?
eval()函數可計算某個字符串,並執行其中的的 JavaScript 代碼。
eval()是一個頂級函數並且跟任何對象無關。
如果字符串表示了一個表達式,eval()會對表達式求值。如果參數表示了一個或多個JavaScript聲明, 那么eval()會執行聲明。
1.29、JavaScript原型,原型鏈 ?
原型:
a. 原型是一個對象,其他對象可以通過它實現屬性繼承。
b. 一個對象的真正原型是被對象內部的[[Prototype]]屬性(property)所持有。瀏覽器支持非標准的訪問器__proto__。
c. 在javascript中,一個對象就是任何無序鍵值對的集合,如果它不是一個主數據類型(undefined,null,boolean,number,string),那它就是一個對象。
原型鏈:
a. 因為每個對象和原型都有一個原型(注:原型也是一個對象),對象的原型指向對象的父,而父的原型又指向父的父,我們把這種通過原型層層連接起來的關系稱為原型鏈。
b. 這條鏈的末端一般總是默認的對象原型。
a.__proto__ = b;
b.__proto__ = c;
c.__proto__ = {}; //default object
{}.__proto__.__proto__; //null
1.30、JQuery與jQuery UI 有啥區別?
jQuery是一個js庫,主要提供的功能是選擇器,屬性修改和事件綁定等等。
jQuery UI則是在jQuery的基礎上,利用jQuery的擴展性,設計的插件。提供了一些常用的界面元素,諸如對話框、拖動行為、改變大小行為等等
1.31、jQuery的源碼看過嗎?能不能簡單說一下它的實現原理?
jQuery給我們帶來了一個簡潔方便的編碼模型(1>創建jQuery對象;2>直接使用jQuery對象的屬性/方法/事件),
一個強悍的dom元素查找器($),插件式編程接口(jQuery.fn),以及插件初始化的”配置”對象思想
1.32、jQuery 中如何將數組轉化為json字符串
在jQuery1.8.3中有個方法“parseJSON”,在這個方法中會做從string轉換為json。
如果當前瀏覽器支持window.JSON,那就直接調用這個對象中的方法。
如果沒有就使用( new Function( "return " + data ) )();執行代碼返回。
1.33、請寫出console.log中的內容
var msg = 'hello';//頂級作用域window下有個變量msg
function great(name, attr) {
var name = 'david';
var greating = msg + name + '!';
var msg = '你好';
for (var i = 0; i < 10; i++) {
var next = msg + '你的id是' + i * 2 + i;
}
console.log(arguments[0]);// david
console.log(arguments[1]);// undefined
console.log(greating);// undefineddavid!
console.log(next);// 你好你的id是189
}
great('Tom')
1.35、請說明下下面代碼的執行過程
var t=true;
window.setTimeout(function(){
t=false;
},1000);
while(t){
console.log(1);
}
alert('end');
a. JavaScript引擎是單線程運行的,瀏覽器無論在什么時候都只且只有一個線程在運行JavaScript程序
b. setTimeout是異步線程,需要等待js引擎處理完同步代碼(while語句)之后才會執行,while語句直接是個死循環,js引擎沒有空閑,不會執行下面的alert,也不會插入setTimeout。我在chrome中執行在線代碼,最后瀏覽器是終止死循環執行alert。
c. JavaScript的工作機制是:當線程中沒有執行任何同步代碼的前提下才會執行異步代碼,setTimeout是異步代碼,所以setTimeout只能等js空閑才會執行,但死循環是永遠不會空閑的,所以setTimeout也永遠不會執行。
1.36、輸出今天的日期,以YYYY-MM-DD的方式,比如今天是2014年9月26日,則輸出2014-09-26
function getCurrentDate(){
var timeStr = '-';
var curDate = new Date();
var curYear =curDate.getFullYear(); //獲取完整的年份(4位,1970-????)
var curMonth = curDate.getMonth()+1; //獲取當前月份(0-11,0代表1月)
var curDay = curDate.getDate(); //獲取當前日(1-31)
var curWeekDay = curDate.getDay(); //獲取當前星期X(0-6,0代表星期天)
var curHour = curDate.getHours(); //獲取當前小時數(0-23)
var curMinute = curDate.getMinutes(); // 獲取當前分鍾數(0-59)
var curSec =curDate.getSeconds(); //獲取當前秒數(0-59)
var Current= curYear+timeStr+curMonth+timeStr+curDay+' '+curHour+':'+curMinute+':'+curSec;
console.log(Current);
return Current;
}
getCurrentDate()
1.37、Javascript中callee和caller的作用?
arguments.callee屬性包含當前正在執行的函數。
Function.caller返回一個對函數的引用,該函數調用了當前函數。
1.39、JS異步編程方式有幾種?
a. 回調函數
b. 事件監聽
c. 發布訂閱
d. promise
1.40、請說說在JavaScript引用類型和值類型的理解
值類型:存儲在棧(stack)中的簡單數據段,也就是說,它們的值直接存儲在變量訪問的位置。即Undefined、Null、Boolean、Number 和 String。
引用類型:存儲在堆(heap)中的對象,也就是說,存儲在變量處的值是一個指針(point),指向存儲對象的內存處。即對象、數組
var a = {n:1};
var b = a;
a.x = a = {n:2};
console.log(a.x);// --> undefined
console.log(b.x);// --> [object Object]
1.41、請解釋一下JavaScript中的作用域和作用域鏈
變量的作用域(scope):程序源代碼中定義這個變量的區域。
作用域鏈:是一個對象列表或鏈表,這組對象定義了這段代碼“作用域中”的變量。查找變量會從第一個對象開始查找,有則用,無則查找鏈上的下一個對象。
1.42、用例子來講事件委托過程
<input type="button" name="" id="btn" value="添加" /> <ul id="ul1"> <li>111</li> <li>222</li> <li>333</li> <li>444</li> </ul>
window.onload = function(){ var oBtn = document.getElementById("btn"); var oUl = document.getElementById("ul1"); var aLi = oUl.getElementsByTagName('li'); var num = 4; //事件委托,添加的子元素也有事件 oUl.onmouseover = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == 'li'){ target.style.background = "red"; } }; oUl.onmouseout = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == 'li'){ target.style.background = "#fff"; } }; //添加新節點 oBtn.onclick = function(){ num++; var oLi = document.createElement('li'); oLi.innerHTML = 111*num; oUl.appendChild(oLi); }; }
1.43、數組去重
方法1:ES6 Set函數
var strAry=[1, 2, 3, 4, 4]; var set = new Set(strAry); console.log(set)//[1,2,3,4]
方法2:利用對象屬性存在的特性,如果沒有該屬性則存入新數組。
var strAry=[1, 2, 3, 4, 4];
function unique(arr) {
var obj={} var newArr=[] for (let i = 0; i < arr.length; i++) { if (!obj[arr[i]]) { obj[arr[i]] = 1 newArr.push(arr[i]) } } return newArr }
console.log(unique(strAry))
方法2:利用數組的indexOf下標屬性來查詢。
var strAry=[1, 2, 3, 4, 4];
function unique4(arr) { var newArr = [] for (var i = 0; i < arr.length; i++) { if (newArr.indexOf(arr[i])===-1) { newArr.push(arr[i]) } } return newArr } console.log(unique4(strAry))
1.44、跨域解決方案
1、 通過jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域資源共享(CORS)
7、 nginx代理跨域
8、 nodejs中間件代理跨域
9、 WebSocket協議跨域