互聯網中級Javascript面試題
1.實現一個函數clone,可以對JavaScript中的5種主要的數據類型(包括Number、String、Object、Array、Boolean)進行值復制
考察點1:對於基本數據類型和引用數據類型在內存中存放的是值還是指針這一區別是否清楚
考察點2:是否知道如何判斷一個變量是什么類型的
考察點3:遞歸算法的設計
// 方法一:
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;
}
//方法二:
/**
* 克隆一個對象
* @param Obj
* @returns
*/
function clone(Obj) {
var buf;
if (Obj instanceof Array) {
buf = []; //創建一個空的數組
var i = Obj.length;
while (i--) {
buf[i] = clone(Obj[i]);
}
return buf;
} else if (Obj instanceof Object) {
buf = {}; //創建一個空對象
for (var k in Obj) { //為這個對象添加新的屬性
buf[k] = clone(Obj[k]);
}
return buf;
} else { //普通變量直接賦值
return Obj;
}
}
2.如何消除一個數組里面重復的元素?
var arr = [1, 2, 3, 3, 4, 4, 5, 5, 6, 1, 9, 3, 25, 4];
function deRepeat() {
var newArr = [];
var obj = {};
var index = 0;
var l = arr.length;
for (var i = 0; i < l; i++) {
if (obj[arr[i]] == undefined) {
obj[arr[i]] = 1;
newArr[index++] = arr[i];
} else if (obj[arr[i]] == 1)
continue;
}
return newArr;
}
var newArr2 = deRepeat(arr);
alert(newArr2); //輸出1,2,3,4,5,6,9,25
3.小賢是一條可愛的小狗(Dog),它的叫聲很好聽(wow),每次看到主人的時候就會乖乖叫一聲(yelp)。從這段描述可以得到以下對象:
function Dog() {
this.wow = function() {
alert(’Wow’);
}
this.yelp = function() {
this.wow();
}
}
小芒和小賢一樣,原來也是一條可愛的小狗,可是突然有一天瘋了(MadDog),一看到人就會每隔半秒叫一聲(wow)地不停叫喚(yelp)。請根據描述,按示例的形式用代碼來實。(繼承,原型,setInterval)
答案:
function MadDog() {
this.yelp = function() {
var self = this;
setInterval(function() {
self.wow();
}, 500);
}
}
MadDog.prototype = new Dog();
//for test
var dog = new Dog();
dog.yelp();
var madDog = new MadDog();
madDog.yelp();
4.下面這個ul,如何點擊每一列的時候alert其index?(閉包)
<ul id=”test”>
<li>這是第一條</li>
<li>這是第二條</li>
<li>這是第三條</li>
</ul>
答案:
// 方法一:
var lis=document.getElementById('2223').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
lis[i].index=i;
lis[i].onclick=function(){
alert(this.index);
};
}
//方法二:
var lis=document.getElementById('2223').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
lis[i].index=i;
lis[i].onclick=(function(a){
return function() {
alert(a);
}
})(i);
}
5.編寫一個JavaScript函數,輸入指定類型的選擇器(僅需支持id,class,tagName三種簡單CSS選擇器,無需兼容組合選擇器)可以返回匹配的DOM節點,需考慮瀏覽器兼容性和性能。
答案:
var query = function(selector) {
var reg = /^(#)?(\.)?(\w+)$/img;
var regResult = reg.exec(selector);
var result = [];
//如果是id選擇器
if(regResult[1]) {
if(regResult[3]) {
if(typeof document.querySelector === "function") {
result.push(document.querySelector(regResult[3]));
}
else {
result.push(document.getElementById(regResult[3]));
}
}
}
//如果是class選擇器
else if(regResult[2]) {
if(regResult[3]) {
if(typeof document.getElementsByClassName === 'function') {
var doms = document.getElementsByClassName(regResult[3]);
if(doms) {
result = converToArray(doms);
}
}
//如果不支持getElementsByClassName函數
else {
var allDoms = document.getElementsByTagName("*") ;
for(var i = 0, len = allDoms.length; i < len; i++) {
if(allDoms[i].className.search(new RegExp(regResult[2])) > -1) {
result.push(allDoms[i]);
}
}
}
}
}
//如果是標簽選擇器
else if(regResult[3]) {
var doms = document.getElementsByTagName(regResult[3].toLowerCase());
if(doms) {
result = converToArray(doms);
}
}
return result;
}
function converToArray(nodes){
var array = null;
try{
array = Array.prototype.slice.call(nodes,0);//針對非IE瀏覽器
}catch(ex){
array = new Array();
for( var i = 0 ,len = nodes.length; i < len ; i++ ) {
array.push(nodes[i])
}
}
return array;
}
6.請評價以下代碼並給出改進意見。
if(window.addEventListener){
var addListener = function(el,type,listener,useCapture){
el.addEventListener(type,listener,useCapture);
};
}
else if(document.all){
addListener = function(el,type,listener){
el.attachEvent("on"+type,function(){
listener.apply(el);
});
}
}
評價:
不應該在if和else語句中聲明addListener函數,應該先聲明;
不需要使用window.addEventListener或document.all來進行檢測瀏覽器,應該使用能力檢測;
由於attachEvent在IE中有this指向問題,所以調用它時需要處理一下
改進如下:
function addEvent(elem, type, handler) {
if (elem.addEventListener) {
elem.addEventListener(type, handler, false);
} else if (elem.attachEvent) {
elem['temp' + type + handler] = handler;
elem[type + handler] = function() {
elem['temp' + type + handler].apply(elem);
};
elem.attachEvent('on' + type, elem[type + handler]);
} else {
elem['on' + type] = handler;
}
}
7.給String對象添加一個方法,傳入一個string類型的參數,然后將string的每個字符間價格空格返回,例如:
addSpace("hello world") // -> 'h e l l o w o r l d'
String.prototype.spacify = function() {
return this.split('').join(' ');
};
接着上述答題,那么問題來了
1)直接在對象的原型上添加方法是否安全?尤其是在Object對象上。(這個我沒能答出?希望知道的說一下。)
2)函數聲明與函數表達式的區別?
答案:在Javscript中,解析器在向執行環境中加載數據時,對函數聲明和函數表達式並非是一視同仁的,解析器會率先讀取函數聲明,並使其在執行任何代碼之前可用(可以訪問),至於函數表達式,則必須等到解析器執行到它所在的代碼行,才會真正被解析執行。(函數聲明提升)
8.定義一個log方法,讓它可以代理console.log的方法。
可行的方法一:
function log(msg) {
console.log(msg);
}
log("hello world!") // hello world!
如果要傳入多個參數呢?顯然上面的方法不能滿足要求,所以更好的方法是:
function log() {
console.log.apply(console, arguments);
};
那么問題來了,apply和call方法的異同?
答案:
對於apply和call兩者在作用上是相同的,即是調用一個對象的一個方法,以另一個對象替換當前對象。將一個函數的對象上下文從初始的上下文改變為由 thisObj 指定的新對象。
但兩者在參數上有區別的。對於第一個參數意義都一樣,但對第二個參數: apply傳入的是一個參數數組,也就是將多個參數組合成為一個數組傳入,而call則作為call的參數傳入(從第二個參數開始)。 如 func.call(func1,var1,var2,var3)對應的apply寫法為:func.apply(func1,[var1,var2,var3]) 。
9.在Javascript中什么是偽數組?如何將偽數組轉化為標准數組?
答案:
偽數組(類數組):無法直接調用數組方法或期望length屬性有什么特殊的行為,但仍可以對真正數組遍歷方法來遍歷它們。典型的是函數的argument參數,還有像調用getElementsByTagName,document.childNodes之類的,它們都返回NodeList對象都屬於偽數組。可以使用Array.prototype.slice.call(fakeArray)將數組轉化為真正的Array對象。
假設接第八題題干,我們要給每個log方法添加一個"(app)"前綴,比如'hello world!' ->'(app)hello world!'。方法如下:
function log() {
var args = Array.prototype.slice.call(arguments); //為了使用unshift數組方法,將argument轉化為真正的數組
args.unshift('(app)');
console.log.apply(console, args);
};
10.對作用域上下文和this的理解,看下列代碼:
var User = {
count: 1,
getCount: function() {
return this.count;
}
};
console.log(User.getCount()); // what?
var func = User.getCount;
console.log(func()); // what?
問兩處 console 輸出什么?為什么?
答案是 1 和 undefined。
func 是在 winodw 的上下文中被執行的,所以會訪問不到 count 屬性。
那么問題來了,如何確保Uesr總是能訪問到func的上下文,即正確返回1。
答案:正確的方法是使用Function.prototype.bind。兼容各個瀏覽器完整代碼如下:
Function.prototype.bind = Function.prototype.bind || function(context) {
var self = this;
return function() {
return self.apply(context, arguments);
};
}
var func = User.getCount.bind(User);
console.log(func());
11.原生JS的window.onload與Jquery的$(document).ready(function(){})有什么不同?如何用原生JS實現Jq的ready方法?
window.onload()方法是必須等到頁面內包括圖片的所有元素加載完畢后才能執行。
$(document).ready()是DOM結構繪制完畢后就執行,不必等到加載完畢。
/*
* 傳遞函數給whenReady()
* 當文檔解析完畢且為操作准備就緒時,函數作為document的方法調用
*/
var whenReady = (function() { //這個函數返回whenReady()函數
var funcs = []; //當獲得事件時,要運行的函數
var ready = false; //當觸發事件處理程序時,切換為true
//當文檔就緒時,調用事件處理程序
function handler(e) {
if (ready) return; //確保事件處理程序只完整運行一次
//如果發生onreadystatechange事件,但其狀態不是complete的話,那么文檔尚未准備好
if (e.type === 'onreadystatechange' && document.readyState !== 'complete') {
return;
}
//運行所有注冊函數
//注意每次都要計算funcs.length
//以防這些函數的調用可能會導致注冊更多的函數
for (var i = 0; i < funcs.length; i++) {
funcs[i].call(document);
}
//事件處理函數完整執行,切換ready狀態, 並移除所有函數
ready = true;
funcs = null;
}
//為接收到的任何事件注冊處理程序
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', handler, false);
document.addEventListener('readystatechange', handler, false); //IE9+
window.addEventListener('load', handler, false);
} else if (document.attachEvent) {
document.attachEvent('onreadystatechange', handler);
window.attachEvent('onload', handler);
}
//返回whenReady()函數
return function whenReady(fn) {
if (ready) {
fn.call(document);
} else {
funcs.push(fn);
}
}
})();
如果上述代碼十分難懂,下面這個簡化版:
function ready(fn){
if(document.addEventListener) { //標准瀏覽器
document.addEventListener('DOMContentLoaded', function() {
//注銷事件, 避免反復觸發
document.removeEventListener('DOMContentLoaded',arguments.callee, false);
fn(); //執行函數
}, false);
}else if(document.attachEvent) { //IE
document.attachEvent('onreadystatechange', function() {
if(document.readyState == 'complete') {
document.detachEvent('onreadystatechange', arguments.callee);
fn(); //函數執行
}
});
}
};
12.(設計題)想實現一個對頁面某個節點的拖曳?如何做?(使用原生JS)
回答出概念即可,下面是幾個要點
給需要拖拽的節點綁定mousedown, mousemove, mouseup事件
mousedown事件觸發后,開始拖拽
mousemove時,需要通過event.clientX和clientY獲取拖拽位置,並實時更新位置
mouseup時,拖拽結束
需要注意瀏覽器邊界的情況
13.function setcookie(name, value, days) { //給cookie增加一個時間變量
var exp = new Date();
exp.setTime(exp.getTime() + days * 24 * 60 * 60 * 1000); //設置過期時間為days天
document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
}
function getCookie(name) {
var result = "";
var myCookie = "" + document.cookie + ";";
var searchName = "+name+" = ";
var startOfCookie = myCookie.indexOf(searchName);
var endOfCookie;
if (satrtOfCookie != -1) {
startOfcookie += searchName.length;
endOfCookie = myCookie.indexOf(";", startOfCookie);
result = (myCookie.substring(startOfCookie, endOfCookie));
}
return result;
}
(function() {
var oTips = document.getElementById('tips'); //假設tips的id為tips
var page = {
check: function() { //檢查tips的cookie是否存在並且允許顯示
var tips = getCookie('tips');
if (!tips || tips == 'show') return true; //tips的cookie不存在
if (tips == "never_show_again") return false;
},
hideTip: function(bNever) {
if (bNever) setcookie('tips', 'never_show_again', 365);
oTips.style.display = "none"; //隱藏
},
showTip: function() {
oTips.style.display = "inline"; //顯示,假設tips為行級元素
},
init: function() {
var _this = this;
if (this.check()) {
_this.showTip();
setcookie('tips', 'show', 1);
}
oTips.onclick = function() {
_this.hideTip(true);
};
}
};
page.init();
})();
14.說出以下函數的作用是?空白區域應該填寫什么?
//define
(function(window) {
function fn(str) {
this.str = str;
}
fn.prototype.format = function() {
var arg = ______;
return this.str.replace(_____, function(a, b) {
return arg[b] || "";
});
}
window.fn = fn;
})(window);
//use
(function() {
var t = new fn('<p><a href="{0}">{1}</a><span>{2}</span></p>');
console.log(t.format('http://www.alibaba.com', 'Alibaba', 'Welcome'));
})();
答案:訪函數的作用是使用format函數將函數的參數替換掉{0}這樣的內容,返回一個格式化后的結果:
第一個空是:arguments
第二個空是:/\{(\d+)\}/ig
15.用面向對象的Javascript來介紹一下自己。(沒答案哦親,自己試試吧)
答案: 對象或者Json都是不錯的選擇哦。
16.講解原生Js實現ajax的原理。
Ajax 的全稱是Asynchronous JavaScript and XML,其中,Asynchronous 是異步的意思,它有別於傳統web開發中采用的同步的方式。
Ajax的原理簡單來說通過XmlHttpRequest對象來向服務器發異步請求,從服務器獲得數據,然后用javascript來操作DOM而更新頁面。
XMLHttpRequest是ajax的核心機制,它是在IE5中首先引入的,是一種支持異步請求的技術。簡單的說,也就是javascript可以及時向服務器提出請求和處理響應,而不阻塞用戶。達到無刷新的效果。
XMLHttpRequest這個對象的屬性有:
onreadystatechang 每次狀態改變所觸發事件的事件處理程序。
responseText 從服務器進程返回數據的字符串形式。
responseXML 從服務器進程返回的DOM兼容的文檔數據對象。
status 從服務器返回的數字代碼,比如常見的404(未找到)和200(已就緒)
status Text 伴隨狀態碼的字符串信息
readyState 對象狀態值
0 (未初始化) 對象已建立,但是尚未初始化(尚未調用open方法)
1 (初始化) 對象已建立,尚未調用send方法
2 (發送數據) send方法已調用,但是當前的狀態及http頭未知
3 (數據傳送中) 已接收部分數據,因為響應及http頭不全,這時通過responseBody和responseText獲取部分數據會出現錯誤,
4 (完成) 數據接收完畢,此時可以通過通過responseXml和responseText獲取完整的回應數據
下面簡單封裝一個函數:
ajax({
url: "./TestXHR.aspx", //請求地址
type: "POST", //請求方式
data: { name: "super", age: 20 }, //請求參數
dataType: "json",
success: function (response, xml) {
// 此處放成功后執行的代碼
},
fail: function (status) {
// 此處放失敗后執行的代碼
}
});
function ajax(options) {
options = options || {};
options.type = (options.type || "GET").toUpperCase();
options.dataType = options.dataType || "json";
var params = formatParams(options.data);
//創建 - 非IE6 - 第一步
if (window.XMLHttpRequest) {
var xhr = new XMLHttpRequest();
} else { //IE6及其以下版本瀏覽器
var xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
//接收 - 第三步
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
var status = xhr.status;
if (status >= 200 && status < 300) {
options.success && options.success(xhr.responseText, xhr.responseXML);
} else {
options.fail && options.fail(status);
}
}
}
//連接 和 發送 - 第二步
if (options.type == "GET") {
xhr.open("GET", options.url + "?" + params, true);
xhr.send(null);
} else if (options.type == "POST") {
xhr.open("POST", options.url, true);
//設置表單提交時的內容類型
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(params);
}
}
//格式化參數
function formatParams(data) {
var arr = [];
for (var name in data) {
arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name]));
}
arr.push(("v=" + Math.random()).replace("."));
return arr.join("&");
}
上述代碼大致表述了ajax的過程,釋義自行google,問題未完,那么知道什么是Jsonp和pjax嗎?
答案:
Jsonp:(JSON with Padding)是一種跨域請求方式。主要原理是利用了script 標簽可以跨域請求的特點,由其 src 屬性發送請求到服務器,服務器返回 js 代碼,網頁端接受響應,然后就直接執行了,這和通過 script 標簽引用外部文件的原理是一樣的。JSONP由兩部分組成:回調函數和數據,回調函數一般是由網頁端控制,作為參數發往服務器端,服務器端把該函數和數據拼成字符串返回。
pjax:pjax是一種基於ajax+history.pushState的新技術,該技術可以無刷新改變頁面的內容,並且可以改變頁面的URL。(關鍵點:可以實現ajax無法實現的后退功能)pjax是ajax+pushState的封裝,同時支持本地存儲、動畫等多種功能。目前支持jquery、qwrap、kissy等多種版本。