function Foo() { var i = 0; return function() { console.log(i++); } }var f1 = Foo(), f2 = Foo(); f1(); f1(); f2();
解析: 輸出爲:0,1,0 Foo函數返回一個匿名函數,這個匿名函數對Foo的內部變量 i 存在引用,因此只要指向匿名函數的變量不釋放(如 f1),i 的值就一直存在。故每次執行 f1() 都會增長 i 的值。而 f1,f2 指向的是兩個不一樣閉包,因此二者執行時互不影響。javascript
(function() { var a = b = 5; })(); console.log(b); console.log(a);
上面的輸出結果是:5,undefined 注意連續賦值,b其實是全局變量,若是啓用了嚴格模式,代碼就會引起ReferenceError的錯誤:B沒有定義(b is not defined)。請記住,嚴格模式,則須要明確指定,才能實現全局變量聲明。好比,你應該寫:php
(function() { 'use strict'; var a = window.b = 5; })(); console.log(b);
A. document.getElementById("button1").readolny= true;
B. document.getElementById("button1").setAttribute(「readolny」,」true」);
C. document.getElementById("button1").disabled = true;
D. document.getElementById("button1").setAttribute(「disabled」,」true」);css
解析:這題的標準答案是C,毋庸置疑,答案D雖然也能達到預期的效果,可是實際上無論第二個參數爲何值都會值都會使按鈕處於禁用狀態,及時設置成布爾型的false.因此這個答案存在異議,不算是標準答案html
A.document.getElementById('button1').style.backgroundColor="red";
B.document.getElementById('button1').style.backgroundcolor="red";
C.document.getElementById('button1').style.backGroundColor="red";
D.document.getElementById('button1').style.bgcolor="red";
前端
解析:正確答案是A,js裏樣式設置直接把css寫法的的「-」去掉,再改寫爲駝峯寫法便可。java
int totalBlank = 0; int blankNum = 0; int taglen = page.taglst.size();A for(int i = 1; i < taglen-1; ++i) { //check blankB while(page.taglst[i] == "<br>" && i < taglen) {C ++totalBlank;D ++i; }E if(totalBlank > 10)F blankNum += totalBlank;G totalBlank = 0; }
注意:如下代碼中taglen是html文件中存在元素的個數,a.html中taglen的值是15,page.taglst[i]取的是a.html中的元素,例如page.taglst[1]的值是
a.html的文件以下:node
<!--這裏有一個空行--><html><title>test</title><body><div>aaaaaaa</div></body></html><br><br><br><br><br>
答案爲B,由於
while(page.taglst[i] == "<br>" && i < taglen)
這個判斷,先執行page.taglst[i] == "<br>"
這個判斷,若是這個判斷返回值爲true,再執行i < taglen
這個判斷。當i=taglen
的時候,執行page.taglst[i] == "<br>"
這個判斷就會越界,因此B處,最早出現越界面試
var f = function g(){ return 23; };typeof g();
答案:會發生錯誤ReferenceError: g is not defined
由於function g(){ return 23; }是函數表達式,事實上只有事一個名字,不是一個函數聲明,函數其實是綁定到變量f,不是g。正則表達式
var x = 1;if (function f(){} ){ x += typeof f;}console.log(x);//1undefined
這裏有個難點,if 中的 function f(){} 要如何處理?函數聲明的實際規則以下:函數聲明只能出如今程序或函數體內。從句法上講,它們 不能出如今Block(塊)({ … })中,例如不能出如今 if、while 或 for 語句中。由於 Block(塊) 中只能包含Statement語句, 而不能包含函數聲明這樣的源元素。另外一方面,仔細看一看規則也會發現,惟一可能讓表達式出如今Block(塊)中情形,就是讓它做爲表達式語句的一部分。可是,規範明確規定了表達式語句不能以關鍵字function開頭。而這實際上就是說,函數表達式一樣也不能出如今Statement語句或Block(塊)中(由於Block(塊)就是由Statement語句構成的)。關於這題的結果的內在原理並非很懂,彷彿if語句不存在直接執行了裏面的語句,又或者由於沒法解釋就把括號內的一坨直接當成了字符串爲真,f仍是沒有定義。chrome
var length = 10;function fn() { console.log(this.length);}var obj = { length: 5, method: function(fn) { fn(); arguments[0](); }};obj.method(fn, 1);
正確結果是輸出:10 2
解析:牢記->JavaScript中函數調用一共有四種方式:方法調用模式、函數調用模式、構造器調用模式、apply/call調用模式.第一次調用fn()時是普通函數調用模式this指向全局window。咱們知道取對象屬於除了點操做符還能夠用中括號,因此第二次執行時至關於arguments調用方法,this指向arguments,而這裏傳了兩個參數,故輸出arguments長度爲2。
function fn(a) { console.log(a); ①var a = 2; ②function a() {} console.log(a); }fn(1);輸出:function a() {} 2 //chrome測試
咱們知道var和function是會提早聲明的,並且函數聲明是優先於var聲明的(若是同時存在的話),這裏的優於能夠理解爲晚於變量聲明後(先提高變量再緊跟着函數聲明,函數聲明提高至關於整個函數塊包括代碼一塊兒移動到前面了),若是函數名和變量名相同,函數聲明就能覆蓋變量聲明,因此提早聲明後輸出的a是個function,而後代碼往下執行a進行從新賦值了,故第二次輸出是2。即便把第一句和第二句交換一下輸出結果不變
var a = 10;a.pro = 10;console.log(a.pro + a);var s = 'hello';s.pro = 'world';console.log(s.pro + s);
答案:NaN undefinedhello
解析:給基本類型數據加屬性不報錯,可是引用的話返undefined,undefined+10返回NaN,這裏運用的規則是:對於加性操做符,若是一個操做數是樹值,另外一個操做數是undefined、null、布爾值時,則先調用Number()將非樹值轉換成數值,undefined會轉化成NaN,null轉換成0,布爾值轉化成1或者0.而undefined和字符串相加時轉變成了字符串,因此結果是undefiendhello
var s = 'aaabbbcccaaabbbaaa';var obj = {};var maxn = -1;var letter;for(var i = 0; i < s.length; i++) { if(obj[s[i]]) { obj[s[i]]++; if(obj[s[i]] > maxn) { maxn = obj[s[i]]; letter = s[i]; } } else { obj[s[i]] = 1; if(obj[s[i]] > maxn) { maxn = obj[s[i]]; letter = s[i]; } }}alert(letter + ': ' + maxn);
var s = 'aaabbbcccaaabbbaaabbbbbbbbbb';var a = s.split('');a.sort();s = a.join('');var pattern = /(\w)\1*/g;var ans = s.match(pattern);ans.sort(function(a, b) { return a.length < b.length;});;console.log(ans[0][0] + ': ' + ans[0].length);
<!-- 實現一段腳本,使得點擊對應連接alert出相應的編號 -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><body> <a href='#'> 第一個連接 </a> </br> <a href='#'> 第二個連接 </a> </br> <a href='#'> 第三個連接 </a> </br> <a href='#'> 第四個連接 </a> </br> <script type="text/javascript"> var lis = document.links; for(var i = 0, length = lis.length; i < length; i++) { lis[i].index = i+1; lis[i].onclick = function() { alert(this.index); }; } </script></body>
<!-- 實現一段腳本,使得點擊對應連接alert出相應的編號 -->
meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><body> <a href='#'> 第一個連接 </a> </br> <a href='#'> 第二個連接 </a> </br> <a href='#'> 第三個連接 </a> </br> <a href='#'> 第四個連接 </a> </br> <script type="text/javascript"> var lis = document.links; for(var i = 0, length = lis.length; i < length; i++) { (function(i) { lis[i].onclick = function() { alert(i + 1); }; })(i); } </script></body>
function JSClass() { this.m_Text = 'division element'; this.m_Element = document.createElement('div'); this.m_Element.innerHTML = this.m_Text; this.m_Element.addEventListener('click', this.func); // this.m_Element.onclick = this.func;}JSClass.prototype.Render = function() { document.body.appendChild(this.m_Element);}JSClass.prototype.func = function() { alert(this.m_Text);};var jc = new JSClass();jc.Render(); // add divjc.func(); // 輸出 division element
click添加的div元素division element會輸出underfined,爲何?
答案:division element undefined
解析:第一次輸出很好理解,func()做爲對象的方法調用,因此輸出division element,點擊添加的元素時,this其實已經指向this.m_Element,也就是事件的目標元素(事件對象的currentTarget屬性值-或者說是註冊事件處理程序的元素),由於是this.m_Element調用的addEventListener函數,因此內部的this全指向它了,而這個元素並無m_Text屬性,因此輸出undefined。
function parseQueryString(url) { var obj = {}; var a = url.split('?'); if(a.length === 1) return obj; var b = a[1].split('&'); for(var i = 0, length = b.length; i < length; i++) { var c = b[i].split('='); obj[c[0]] = c[1]; } return obj;} ``` 下面給出一個實際經常使用的更嚴謹的寫法(來自JavaScript高級程序設計): ```javascript function getQueryStringArgs() { //取得查詢字符串並去掉開頭的問號(這裏使用了BOM中的location對象) var qs=(location.search.length>0?location.search.substring(1):""), //保存數據的對象 args={}, //取得每一項 items=qs.length?qs.split('&'):[], item=null, name=null, value=null, i=0, len=items.length; for (i = 0; i <len; i++) { item=items[i].split("="); name=decodeURIComponent(item[0]); value=decodeURIComponent(item[1]); if (name.length) { args[name]=value; } } return args;}
還能夠藉助正則表達式
function getQueryObject(url) { url = url == null ? window.location.href : url; var search = url.substring(url.lastIndexOf("?") + 1); var obj = {}; var reg = /([^?&=]+)=([^?&=]*)/g; search.replace(reg, function (rs, $1, $2) { var name = decodeURIComponent($1); var val = decodeURIComponent($2); val = String(val); obj[name] = val; return rs; }); return obj;}
function foo1(){ return { bar: "hello" };}function foo2(){ return { bar: "hello" };}
在編程語言中,基本都是使用分號(;)將語句分隔開,這能夠增長代碼的可讀性和整潔性。而在JS中,如若語句各佔獨立一行,一般能夠省略語句間的分號(;),JS 解析器會根據可否正常編譯來決定是否自動填充分號:
var test = 1 + 2console.log(test); //3
在上述狀況下,爲了正確解析代碼,就不會自動填充分號了,可是對於 return
、break
、continue
等語句,若是後面緊跟換行,解析器必定會自動在後面填充分號(;),因此上面的第二個函數就變成了這樣:
function foo2(){ return; { bar: "hello" };}
因此第二個函數是返回 undefined
。
var arr1 = "john".split('');var arr2 = arr1.reverse(); var arr3 = "jones".split(''); arr2.push(arr3);console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));
答案是array 1: length=5 last=j,o,n,e,s
array 2: length=5 last=j,o,n,e,s
解析:
- 數組的reverse方法會將原數組反轉後返回,arr1指向反轉後的數組
- JS中當從一個變量向另外一個變量複製引用類型的值時,會將存儲在變量對象中的值複製一份放到爲新變量分配的空間中。不一樣的是,這個值的副本其實是一個指針,而這個指針指向存儲在堆中的一個對象。複製操做結束後,兩個變量實際上將引用同一個對象。所以,改變其中一個變量,就會影響另外一個變量,以下圖所示:
因此arr2和arr1實際上引用的是同一個對象。
var a={}, b={key:'b'}, c={key:'c'};a[b]=123;a[c]=456;console.log(a[b]);
輸出是 456,參考原文的解釋:
javascript中給對象添加和訪問屬性時會隱式的把屬性名轉換成字符串,由於b和c都是都是對象,轉換成字符串都是[object Object],所以兩次其實是設置了同一屬性a[[object Object]]
函數接受兩個參數:
原文利用 深度優先搜索(Depth-First-Search) 給了一個實現:
function Traverse(p_element,p_callback) { p_callback(p_element); var list = p_element.children; for (var i = 0; i < list.length; i++) { Traverse(list[i],p_callback); // recursive call }}
[1 < 2 < 3, 3 < 2 < 1]
這個題會讓人誤覺得是 2 > 1 && 2 < 3 其實不是的.
這個解題思路是
1 < 2 => true; true < 3 => 1 < 3 => true; 3 < 2 => false; false < 1 => 0 < 1 => true;
答案是 [true, true]
var lowerCaseOnly = /^[a-z]+$/;console.log([lowerCaseOnly.test(null), lowerCaseOnly.test()])
執行時會使用toString方法將參數轉化成字符串「null」,"undefined",所以都返回true
function captureOne(re, str) { var match = re.exec(str); return match && match[1];}var numRe = /num=(\d+)/ig, wordRe = /word=(\w+)/i, a1 = captureOne(numRe, "num=1"), a2 = captureOne(wordRe, "word=1"), a3 = captureOne(numRe, "NUM=2"), a4 = captureOne(wordRe, "WORD=2");[a1 === a2, a3 === a4]
由於第一個正則有一個 g 選項 它會‘記憶’他所匹配的內容, 等匹配後他會從上次匹配的索引繼續, 而第二個正則不會,因此 a1 = '1'; a2 = '1'; a3 = null; a4 = '2'
function Foo() { getName = function () { alert (1); }; return this;}Foo.getName = function () { alert (2);};Foo.prototype.getName = function () { alert (3);};var getName = function () { alert (4);};//聲明提高,可是賦值卻留在原地function getName() { alert (5);}//會總體提高//請寫出如下輸出結果:Foo.getName();//2getName();//4Foo().getName();//1getName();//1new Foo.getName();//2new Foo().getName();//3new new Foo().getName();//3
答案參考:一道常被人輕視的前端JS面試題
外部函數沒法查看內部函數的內部細節,但內部函數能夠查看其外層的函數細節,直至全局細節。當須要從局部函數查找某一屬性或方法時,若是當前做用域沒有找到,就會上溯到上層做用域查找,直至全局函數,這種組織形式就是做用域鏈。
一、建立一個空對象,而且 this 變量引用該對象,同時還繼承了該函數的原型。
二、屬性和方法被加入到 this 引用的對象中。
三、新建立的對象由 this 所引用,而且最後隱式的返回 this 。
var obj = {};obj.__proto__ = Base.prototype;Base.call(obj);
function commafy(num) { num = num + ''; var reg = /(-?d+)(d{3})/; if(reg.test(num)){ num = num.replace(reg, '$1,$2'); } return num;}
var $= function(selector) { var reg = /^([#\.])?(\w+)$/ig; var match = reg.exec(selector); var result = []; if (!match) {//若是沒有匹配 return; }else if (match[1]==="#") {//若是是ID return document.getElementById(match[2]); }else{//若是是類或者標籤選擇器 if (typeof document.querySelectorAll==='function') {//若是這個函數存在能夠直接處理類和標籤選擇器 return document.querySelectorAll(match[0]); }else{//若是上面牛逼的函數沒有,則要對類和標籤選擇器分別處理 if (match[1]) {//若是是類選擇器 return getElementsByClassName(match[2]);//自定義的類選擇函數 }else{ return getElementsByTagName(match[0]); } } }};/*自定義一個經過類名獲取元素的函數*/var getElementsByClassName=document.getElementsByClassName||function(className){ var tags=document.getElementsByTagName("*"); var result=[]; for (var i = 0,len=tags.length; i < len; i++) { if (tags[i].nodeType===1) { if (tags[i].className.split(/\s+/).indexOf(className)!==-1) {//由於可能存在多個類名以空格分隔,因此這樣處理一下比較符合要求 result.push(tags[i]); } } } return result;};
/** * 對象克隆 * 支持基本數據類型及對象 * 遞歸方法 */function clone(obj) { var o; switch (typeof obj) { case "undefined": break; case "string": o = obj + ""; break; case "number": o = obj - 0; break; case "boolean": o = obj; break; case "object": // object 分爲兩種狀況 對象(Object)或數組(Array) if (obj === null) { o = null; } else { if (Object.prototype.toString.call(obj).indexOf("Array")! == -1) { o = []; for (var i = 0; i < obj.length; i++) { o.push(clone(obj[i])); } } else { o = {}; for (var k in obj) { o[k] = clone(obj[k]); } } } break; default: o = obj; break; } return o;}
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); }); } }
上述代碼的本意是定義一個跨瀏覽器兼容的事件監聽程序,主要有三個問題:一是addListener函數不該該在條件語句裏聲明和賦值,這樣作封裝性和移植性很很差;二是沒有考慮DOM0級的兼容性;三是使用的能力檢測不是很規範。改進以下:
var addListener=function(el,type,listener,useCapture){ if (el.addEventListener) { el.addEventListener(type,handle,useCapture); }else if(el.attachEvent){ el.attachEvent("on"+type,listener); }else{ el["on"+type]=listener; }}
既然都這樣問了,確定是不安全的
應該根據實際狀況肯定是否能夠實施,或者在添加以前肯定是否該方法是否存在
僞數組(類數組):沒法直接調用數組方法或指望length屬性有什麼特殊的行爲,但仍能夠對真正數組遍歷方法來遍歷它們。典型的是函數的argument參數,還有像調用
getElementsByTagName
,document.childNodes
之類的,它們都返回NodeList對象都屬於僞數組。可使用Array.prototype.slice.call(fakeArray)
將數組轉化爲真正的Array對象。要注意的是在IE8及之前,NodeList實現爲一個COM對象,要想將其轉換層數組,必須手動枚舉全部成員。下面的實現兼容性較強。
function converToArray(fakeArray){ var array = null; try{ array = Array.prototype.slice.call(fakeArray,0); }catch(ex){ array = new Array(); for( var i = 0 ,len = nodes.length; i < len ; i++ ) { array.push(fakeArray[i]) } } return array; }
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())
document.ready=(function (){ var fns = []; //爲了支持多個事件處理函數,將添加的事件處理函數緩存在數組中 //執行全部緩存的事件處理函數 var run = function(){ for (var i = 0; i < fns.length; i++){ fns[i](); } } ; if (document.addEventListener) {//標準瀏覽器 document.addEventListener("DOMContentLoaded",function(){ // 註銷事件,避免反覆觸發 document.removeEventListener("DOMContentLoaded",arguments.callee,false); run();//執行函數 },false); }else if (document.attachEvent) {//IE瀏覽器 document.attachEvent('onreadystatechange',function(){ if (document.readyState=="interactive" || document.readyState=="complete") { document.detachEvent("onreadystatechange",arguments.callee); run(); } }); } //返回添加事件處理程序到緩存數組的函數,利用了閉包的特性 return function(fn){ fns.push(fn); } })(); //下面是測試代碼document.ready(function(){ alert("1");});document.ready(function(){ alert("2");});
參考:
Running code when the document is ready
《JavaScript高級程序設計-第三版》P390頁
萬能的紅寶書《JavaScript高級程序設計》在第16章介紹了HTML5原生支持的拖放,在第22章也實現了不須要HTML5特性支持的拖放,咱們能夠直接參考。
若是瀏覽器支持HTML5,爲了使頁面元素具備拖放功能,只須要設置元素的draggable="true"
便可。下面主要介紹在不支持HTML5狀況下的原生拖放。
var DragDrop=function () { var dragging=null, diffx=0, diffy=0; function handleEvent(event){ event=event||window.event; var target=event.target||event.srcElement; switch(event.type){ case "mousedown": //若是該元素添加了draggable類,就認爲其能夠拖拽 if (target.className.indexOf("draggable")>-1) { dragging=target; diffx=event.clientX-target.offsetLeft; diffy=event.clientY-target.offsetTop; } break; case "mousemove": if (dragging!==null) { dragging.style.left=(event.clientX-diffx)+"px"; dragging.style.top=(event.clientY-diffy)+"px"; } break; case "mouseup": dragging=null; break; } } return{ enable:function(){//這裏偷懶未考慮事件監聽的兼容性 document.addEventListener("mousedown",handleEvent,false); document.addEventListener("mousemove",handleEvent,false); document.addEventListener("mouseup",handleEvent,false); }, disable:function(){ document.removeEventListener("mousedown",handleEvent,false); document.removeEventListener("mousemove",handleEvent,false); document.removeEventListener("mouseup",handleEvent,false); } }}();
//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
var obj = { a : 1, func : function () { (function () { this.a = 2; }).call(this); } }; obj.func();
obj.a不變,匿名函數裏的this指向全局對象(window),至關於給window加了一個名爲a的屬性。
解決方案
var obj = { a : 1, func : function () { (function () { this.a = 2; }).call(this); } }; obj.func(); console.log(obj.a);
var obj = { a : 1, func : function () { var self=this; (function () { self.a = 2; })(); } }; obj.func(); console.log(obj.a);
function remove(arr, item) { return arr.filter(function(x) { return x !== item; }); }
function remove(arr, item) { var newArr = []; arr.forEach(function(e) { if(e !== item) { newArr.push(e); } }) return newArr; }
function removeWithoutCopy(arr, item) { for(var i = 0; i < arr.length; i++) { if(item === arr[i]) { arr.splice(i, 1); i--; // for循環逆序的話,i--能夠省略 } } return arr;}
function removeWithoutCopy(arr, item) { var pos = arr.indexOf(item); while(pos !== -1) { arr.splice(pos, 1); pos = arr.indexOf(item); } return arr;}
十進制轉換成其餘進制
/** * [convert description] * @param {[number]} num [description] * @param {[number]} base [轉換後的基數] * @return {[type]} [description] */function convert(num, base) { return num.toString(base);}console.log(convert(16,2))//10000
其餘進制轉化成十進制
function convertFromD(num,base) { return parseInt(num,base);}console.log(convertFromD(11000,2))//24
1.下面的表達式在瀏覽器中的結果是console仍是window
console.log.apply(console,this);
解析:筆試時竟然腦子短路判成了console,首先這裏傳入參數this時,這個this指向Window無疑,這裏的this是實參,在console.log函數中打印的就是這個Window,並無由於執行環境上下文是console而覆蓋參數。原本單獨執行console.log(),log方法就是做爲console對象的方法來調用,如今傳進去的環境仍是console.log,上面的語句實際上就是console.log(this)
2.輸出的字符串前統一加上(app)
這樣的字符串(考察arguments->args;apply)
function log(){ var args = Array.prototype.slice.call(arguments); args.unshift('(app)'); console.log.apply(console, args);};
參考:
(因爲不少題都是臨時收集,不少沒注意出處,請諒解)
你有必要知道的 25 個 JavaScript 面試題
javascript-puzzlers
BAT及各大互聯網公司2014前端筆試面試題–JavaScript篇