1.JS找字符串中出現最多的字符
例如:求字符串'nininihaoa'中出現次數最多字符
方法一:
var str = "nininihaoa"; var o = {}; for (var i = 0, length = str.length; i < length; i++) { var char = str.charAt(i); if (o[char]) { o[char]++; //次數加1 } else { o[char] = 1; //若第一次出現,次數記為1 } } console.log(o); //輸出的是完整的對象,記錄着每一個字符及其出現的次數 //遍歷對象,找到出現次數最多的字符的次數 var max = 0; for (var key in o) { if (max < o[key]) { max = o[key]; //max始終儲存次數最大的那個 } } for (var key in o) { if (o[key] == max) { //console.log(key); console.log("最多的字符是" + key); console.log("出現的次數是" + max); } }
結果如圖所示:
方法二,當然還可以使用reduce方法來實現:
var arrString = 'abcdaabc'; arrString.split('').reduce(function(res, cur) { res[cur] ? res[cur] ++ : res[cur] = 1 return res; }, {})
想詳細了解reduce()方法,可以參考:《JS數組reduce()方法詳解及高級技巧》
2.JS實現九九乘法表
jQuery實現方式:
var sum=0; var wite; for (var i = 1; i < 10; i++){ var div=$('<div class="class'+i+'"></div>'); $("body").append(div); for(var j = i; j > 0; j--){ sum = j * i; wite = (j+"X"+i+"="+sum); div.prepend($('<span style="padding-right:10px">'+wite+'</span>')); } }
實現結果如圖所示:
原生js實現方式:
css代碼:
html,body,ul,li { padding: 0; margin: 0; border: 0; } ul { width: 900px; overflow: hidden; margin-top: 4px; font-size: 12px; line-height: 36px; } li { float: left; width: 90px; margin: 0 4px; display: inline-block; text-align: center; border: 1px solid #333; background:yellowgreen; }
js代碼:
for(var i = 1; i <= 9; i++){ var myUl = document.createElement('ul'); for(var j = 1; j <= i; j++){ var myLi = document.createElement('li'); var myText = document.createTextNode(j + " × " + i + " = " + i*j); myLi.appendChild(myText); myUl.appendChild(myLi); } document.getElementsByTagName('body')[0].appendChild(myUl); }
原生js實現效果如圖所示
3.前端面試:這幾道前端面試題很繞嗎?做對了幾道?
第一題
var fun = function(){ this.name = 'peter'; return { name: 'jack' }; } var p = new fun(); //請問p.name是:
第二題
var fun = function(){ this.name = 'peter'; return 'jack'; } var p = new fun(); //請問p.name是:
第三題
var fun = function(){} fun.prototype = { info : { name : 'peter', age : 25 } } var a = new fun(); var b = new fun(); a.info.name = 'jack'; b.info.name = 'tom'; //請問a.info.name和b.info.name分別是:
第四題
var fun = function(){ this.info = { name : 'peter', age : 25 } } var a = new fun(); var b = new fun(); a.info.name = 'jack'; b.info.name = 'tom'; //請問a.info.name和b.info.name分別是:
第五題
var fun = function(){} fun.prototype = { name : 'peter', age : 25 } var a = new fun(); var b = new fun(); a.name = 'jack'; b.name = 'tom'; //請問a.name和b.name分別是:
第六題
var fun = function(){ this.info = { name : 'peter', age : 25 } } fun.prototype = { info : { name : 'peter', age : 25 } } var a = new fun(); var b = new fun(); a.info.name = 'jack'; b.info.name = 'tom'; //請問a.info.name和b.info.name分別是:
解答:
1,2題考察的是構造函數的返回值的問題。
每個函數都有返回值,如果使用了return
語句,則返回return
后跟的值,如果沒有使用return
,則默認返回undefined
.
特別的,如果這個函數是構造函數,則默認返回this
對象,如果構造函數內使用了return
語句,並且return
后跟的是一個對象,則這個構造函數返回的是這個對象,否則返回this
.
所以1題中的p = {name: 'jack'}
,而2題中的p = {name: 'peter'}
.
3, 4, 5, 6題都是考察prototype
的知識。
3.兩個都輸出tom。首先你要知道原型模式的執行流程:
1.先查找構造函數實例里的屬性或方法,如果有,就立即返回。
2.如果構造函數的實例沒有,就去它的原型對象里找,如果有,就立即返回
4 .a.info.name
為jack
,b.info.name
為tom
。原因我想你從第三題已經得出來了。
5.a.name
輸出jack,b.name
輸出tom。原因我想你從第三題已經得出來了。
6.a.info.name
為jack
,b.info.name
為tom
。原因我想你從第三題已經得出來了。
注意:第三題 a.info.name這段代碼,首先去訪問了實例對象本身是否有info這個對象,發現沒有就去原型上查找了,發現原型上有,所以地址共享了得到的值都是Tom;第五題是有區別的,a.name實例本身沒有,給當前a這個實例對象執行賦值操作,沒有去訪問原型上的name。就相當於第三題先訪問了原型上的info對象,第五題沒有訪問過程,只是在實例上添加了name屬性值。
4.通過示例搞懂JS閉包
例1
function sayHello(name) { var text = 'Hello ' + name; var sayAlert = function() { console.log(text); } sayAlert(); } sayHello("Bob") // 輸出"Hello Bob"
在sayHello()函數中定義並調用了sayAlert()函數;sayAlert()作為內層函數,可以訪問外層函數sayHello()中的text變量。
例2
function sayHello2(name) { var text = 'Hello ' + name; // 局部變量 var sayAlert = function() { console.log(text); } return sayAlert; } var say2 = sayHello2("Jane"); say2(); // 輸出"Hello Jane"
例3
function buildList(list) { var result = []; for(var i = 0; i < list.length; i++) { var item = 'item' + list[i]; result.push( function() { console.log(item + ' ' + list[i]); } ); } return result; } var fnlist = buildList([1,2,3]); for (var j = 0; j < fnlist.length; j++) { fnlist[j](); }
得到的結果:連續輸出3個"item3 undefined"
解析:通過執行buildList函數,返回了一個result,那么這個result存放的是3個匿名函數。然而這三個匿名函數其實就是三個閉包,因為它可以訪問到父函數的局部變量。所以閉包內的保留的i是最終的值為3.所以list[3]肯定是undefined. item變量值為item3.
改成如下代碼:
function buildList(list) { var result = []; for(var i = 0; i < list.length; i++) { var item = 'item' + list[i]; result.push( (function(i) { console.log(item + ' ' + list[i]); })(i) ); } return result; } var fnlist = buildList([1,2,3]);
得到的結果:
item1 1
item2 2 item3 3
解釋:這兒雖然傳遞了一個數組進去,但是返回的是三個自執行的函數。
例4
function newClosure(someNum, someRef) { var anArray = [1,2,3]; var num = someNum; var ref = someRef; return function(x) { num += x; anArray.push(num); console.log('num: ' + num + "; " + 'anArray ' + anArray.toString() + "; " + 'ref.someVar ' + ref.someVar); } } closure1 = newClosure(40, {someVar: "closure 1"}); closure2 = newClosure(1000, {someVar: "closure 2"}); closure1(5); // 打印"num: 45; anArray 1,2,3,45; ref.someVar closure 1" closure2(-10); // 打印"num: 990; anArray 1,2,3,990; ref.someVar closure 2"
每次調用newClosure()都會創建獨立的閉包,它們的局部變量num與ref的值並不相同。
例5
function sayAlice() { var sayAlert = function() { console.log(alice); } var alice = 'Hello Alice'; return sayAlert; } var sayAlice2 = sayAlice(); sayAlice2(); // 輸出"Hello Alice"
alice變量在sayAlert函數之后定義,這並未影響代碼執行。因為返回函數sayAlice2所指向的閉包會包含sayAlice()函數中的所有局部變量,這自然包括了alice變量,因此可以正常打印”Hello Alice”。
例6
function setupSomeGlobals() { var num = 666; gAlertNumber = function() { console.log(num); } gIncreaseNumber = function() { num++; } gSetNumber = function(x) { num = x; } } setupSomeGlobals(); gAlertNumber(); // 輸出666 gIncreaseNumber(); gAlertNumber(); // 輸出667 gSetNumber(5); gAlertNumber(); // 輸出5
解釋:首先gAlertNumber,gIncreaseNumber,gSetNumber是三個全局變量,並且其三個值都是匿名函數,然而這三個匿名函數本身都是閉包。他們操作的num都是保存在內存中的同一個num,所有會得出上面的結果。
下面看一個dom操作,使用閉包的例子:
// 這個代碼是錯誤的,因為變量i從來就沒背locked住 // 相反,當循環執行以后,我們在點擊的時候i才獲得數值 // 因為這個時候i操真正獲得值 // 所以說無論點擊那個連接,最終顯示的都是I am link #10(如果有10個a元素的話) var elems = document.getElementsByTagName('a'); for (var i = 0; i < elems.length; i++) { elems[i].addEventListener('click', function (e) { e.preventDefault(); alert('I am link #' + i); }, 'false'); } // 這個是可以用的,因為他在自執行函數表達式閉包內部 // i的值作為locked的索引存在,在循環執行結束以后,盡管最后i的值變成了a元素總數(例如10) // 但閉包內部的lockedInIndex值是沒有改變,因為他已經執行完畢了 // 所以當點擊連接的時候,結果是正確的 var elems = document.getElementsByTagName('a'); for (var i = 0; i < elems.length; i++) { (function (lockedInIndex) { elems[i].addEventListener('click', function (e) { e.preventDefault(); alert('I am link #' + lockedInIndex); }, 'false'); })(i); } // 你也可以像下面這樣應用,在處理函數那里使用自執行函數表達式 // 而不是在addEventListener外部 // 但是相對來說,上面的代碼更具可讀性 var elems = document.getElementsByTagName('a'); for (var i = 0; i < elems.length; i++) { elems[i].addEventListener('click', (function (lockedInIndex) { return function (e) { e.preventDefault(); alert('I am link #' + lockedInIndex); }; })(i), 'false'); }
5.JS重復輸出一個給定的字符串
如下:
重復輸出一個給定的字符串(str
第一個參數)n 次 (num
第二個參數),如果第二個參數num
不是正數的時候,返回空字符串。
function repeatStringNumTimes(str, num) { return str; } repeatStringNumTimes("abc", 3);
提供測試情況:
repeatStringNumTimes("*", 3) //應該返回 "***". repeatStringNumTimes("abc", 3) //應該返回 "abcabcabc". repeatStringNumTimes("abc", 4) //應該返回 "abcabcabcabc". repeatStringNumTimes("abc", 1) //應該返回 "abc". repeatStringNumTimes("*", 8) //應該返回 "********". repeatStringNumTimes("abc", -2) //應該返回 "".
解題思路
我將介紹三種方法:
- 使用 `while` 循環
- 使用遞歸
- 使用ES6 `repeat()`
方法1:通過 `while` 循環重復輸出一個字符串
function repeatStringNumTimes(string, times) { var repeatedString = ""; while (times > 0) { repeatedString += string; times--; } return repeatedString; } repeatStringNumTimes("abc", 3);
不過這里還可以有幾個變種:
對於老前端來說,首先一個可能會將字符串拼接,修改為 數組join()
拼接字符串,例如:
function repeatStringNumTimes(string, times) { var repeatedArr = []; // while (times > 0) { repeatedArr.push(string); times--; } return repeatedArr.join(""); } repeatStringNumTimes("abc", 3)
很多老前端都有用數組join()
拼接字符串的“情懷”,因為很早以前普遍認為數組join()
拼接字符串比字符串+
拼接速度要快得多。不過現在未必,例如,V8 下+
拼接字符串,要比數組join()
拼接字符串快。我用這兩個方法測試了3萬次重復輸出,只相差了幾毫秒。
另一個變種可以用 for 循環:
function repeatStringNumTimes(string, times) { var repeatedString = ""; for(var i = 0; i < times ;i++) { repeatedString += string; } return repeatedString; } repeatStringNumTimes("abc", 3)
方法2:通過條件判斷和遞歸重復輸出一個字符串
遞歸是一種通過重復地調用函數本身,直到它達到達結果為止的迭代操作的技術。為了使其正常工作,必須包括遞歸的一些關鍵特征。
function repeatStringNumTimes(string, times) { if(times < 0) return ""; if(times === 1) return string; else return string + repeatStringNumTimes(string, times - 1); } repeatStringNumTimes("abc", 3);
方法3:使用ES6 `repeat()` 方法重復輸出一個字符串
這個解決方案比較新潮,您將使用 String.prototype.repeat() 方法:
repeat() 方法構造並返回一個新字符串,該字符串包含被連接在一起的指定數量的字符串的副本。 這個方法有一個參數 count
表示重復次數,介於0和正無窮大之間的整數 : [0, +∞) 。表示在新構造的字符串中重復了多少遍原字符串。重復次數不能為負數。重復次數必須小於 infinity,且長度不會大於最長的字符串。
function repeatStringNumTimes(string, times) { if (times > 0) return string.repeat(times); else return ""; } repeatStringNumTimes("abc", 3);
您可以使用三元表達式作為 if/else 語句的快捷方式,如下所示:
function repeatStringNumTimes(string, times) { return times > 0 ? string.repeat(times) : ""; } repeatStringNumTimes("abc", 3);
轉載地址:http://www.css88.com/archives/7045
6.函數聲明相關
var x=1, y=0, z=0; function add(n){ n=n+1; } y=add(x); z=x+y; console.log("y1:"+y); console.log("z1:"+z); function add(n){ n=n+3; } y=add(x); z=x+y; console.log("y2:"+y); console.log("z2:"+z);
求y,z的值。
結果為:
y1:undefined
z1:NaN
y2:undefined
z2:NaN
變化一下:
var x=1, y=0, z=0; function add(n){ return n=n+1; } y=add(x); z=x+y; console.log("y1:"+y); console.log("z1:"+z); function add(n){ return n=n+3; } y=add(x); z=x+y; console.log("y2:"+y); console.log("z2:"+z);
求y,z的值
答案:
y1:4
z1:5 y2:4 z2:5
7.作用域范圍(Scope)
思考以下代碼:
(function(){ var a = b = 5; })(); console.log(b);
控制台(console)
會打印出什么?
答案
上述代碼會打印出5
。
這個問題的陷阱就是,在立即執行函數表達式(IIFE)中,有兩個命名,但是其中變量是通過關鍵詞var來聲明的。這就意味着a是這個函數的局部變量。與此相反,b是在全局作用域下的。
這個問題另一個陷阱就是,在函數中他沒有使用"嚴格模式
" ('use strict';)。如果 嚴格模式 開啟,那么代碼就會報出未捕獲引用錯誤(Uncaught ReferenceError
):b沒有定義。記住,嚴格模式要求你在需要使用全局變量時,明確地引用該變量。因此,你需要像下面這么寫:
(function(){ 'use strict' var a = window.b = 5; })(); console.log(b);
再看如下一個例子:
var a = 6; setTimeout(function () { alert(a); a = 666; }, 1000); a = 66;
結果:66
8.創建 “原生(native)” 方法
在 String
對象上定義一個 repeatify
函數。這個函數接受一個整數參數,來明確字符串需要重復幾次。這個函數要求字符串重復指定的次數。舉個例子:
console.log('hello'.repeatify(3));
應該打印出hellohellohello
.
答案:
String.prototype.repeatify = String.prototype.repeatify || function(times) { var str = ''; for (var i = 0; i < times; i++) { str += this; } return str; };
在這里,另一個關鍵點是,看你怎樣避免重寫可能已經定義了的方法。這可以通過在定義自己的方法之前,檢測方法是否已經存在。
String.prototype.repeatify = String.prototype.repeatify || function(times){ /*code here*/ };
當你需要為舊瀏覽器實現向后兼容的函數時,這一技巧十分有用。
9.變量提升(Hoisting)
執行以下代碼的結果是什么?為什么?
function test() { console.log(a); console.log(foo()); var a = 1; function foo() { return 2; } } test();
答案:
這段代碼的執行結果是undefined
和 2
。
這個結果的原因是,變量和函數都被提升(hoisted) 到了函數體的頂部。因此,當打印變量a
時,它雖存在於函數體(因為a
已經被聲明),但仍然是undefined
。換言之,上面的代碼等同於下面的代碼:
function test() { var a; function foo() { return 2; } console.log(a); console.log(foo()); a = 1; } test();
再看如下代碼:
(function() { console.log(typeof foo); console.log(typeof bar); var foo = 'hello', bar = function() { return 'world'; }; function foo() { return 'hello'; } }());
結果:
function undefined
10. 在javascript中,this是如何工作的
以下代碼的結果是什么?請解釋你的答案。
var fullname = 'John Doe'; var obj = { fullname: 'Colin Ihrig', prop: { fullname: 'Aurelio De Rosa', getFullname: function() { return this.fullname; } } }; console.log(obj.prop.getFullname()); var test = obj.prop.getFullname; console.log(test());
答案:
這段代碼打印結果是:Aurelio De Rosa
和 John Doe
。原因是,JavaScript中關鍵字this
所引用的是函數上下文,取決於函數是如何調用的,而不是怎么被定義的。
在第一個console.log()
,getFullname()
是作為obj.prop
對象的函數被調用。因此,當前的上下文指代后者,並且函數返回這個對象的fullname
屬性。相反,當getFullname()
被賦值給test
變量時,當前的上下文是全局對象window
,這是因為test
被隱式地作為全局對象的屬性。基於這一點,函數返回window
的fullname
,在本例中即為第一行代碼設置的。
11.call() 和 apply()
修復前一個問題,讓最后一個console.log()
打印輸出Aurelio De Rosa
.
答案:
這個問題可以通過運用call()
或者apply()
方法強制轉換上下文環境。
console.log(test.call(obj.prop));
12.閉包(Closures)
考慮下面的代碼:
var nodes = document.getElementsByTagName('button'); for (var i = 0; i < nodes.length; i++) { nodes[i].addEventListener('click', function() { console.log('You clicked element #' + i); }); }
請問,如果用戶點擊第一個和第四個按鈕的時候,控制台分別打印的結果是什么?為什么?
答案:
兩次打印都是nodes.length的值。
那么修復上題的問題,使得點擊第一個按鈕時輸出0,點擊第二個按鈕時輸出1,依此類推。
有多種辦法可以解決這個問題,下面主要使用兩種方法解決這個問題。
第一個解決方案使用立即執行函數表達式(IIFE)再創建一個閉包,從而得到所期望的i的值。實現此方法的代碼如下:
var nodes = document.getElementsByTagName('button'); for (var i = 0; i < nodes.length; i++) { nodes[i].addEventListener('click', (function(i) { return function() { console.log('You clicked element #' + i); } })(i)); }
另一個解決方案不使用IIFE,而是將函數移到循環的外面。這種方法由下面的代碼實現:
function handlerWrapper(i) { return function() { console.log('You clicked element #' + i); } } var nodes = document.getElementsByTagName('button'); for (var i = 0; i < nodes.length; i++) { nodes[i].addEventListener('click', handlerWrapper(i)); }
代碼片段一:
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()());
結果:The Window
代碼片段二:
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFunc()());
結果:My Object
文章地址:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
13.數據類型問題
考慮如下代碼:
console.log(typeof null); console.log(typeof {}); console.log(typeof []); console.log(typeof undefined);
答案:
object
object
object
undefined
14.事件循環
下面代碼運行結果是什么?請解釋。
function printing() { console.log(1); setTimeout(function() { console.log(2); }, 1000); setTimeout(function() { console.log(3); }, 0); console.log(4); } printing();
答案:
1 4 3 2
想知道為什么輸出順序是這樣的,你需要弄了解setTimeout()
做了什么,以及瀏覽器的事件循環原理。瀏覽器有一個事件循環用於檢查事件隊列,處理延遲的事件。UI事件(例如,點擊,滾動等),Ajax回調,以及提供給setTimeout()
和setInterval()
的回調都會依次被事件循環處理。因此,當調用setTimeout()
函數時,即使延遲的時間被設置為0
,提供的回調也會被排隊。回調會呆在隊列中,直到指定的時間用完后,引擎開始執行動作(如果它在當前不執行其他的動作)。因此,即使setTimeout()
回調被延遲0
毫秒,它仍然會被排隊,並且直到函數中其他非延遲的語句被執行完了之后,才會執行。
15.算法問題
寫一個isPrime()
函數,當其為質數時返回true
,否則返回false
。
答案:
我認為這是面試中最常見的問題之一。然而,盡管這個問題經常出現並且也很簡單,但是從被面試人提供的答案中能很好地看出被面試人的數學和算法水平。
首先, 因為JavaScript不同於C或者Java,因此你不能信任傳遞來的數據類型。如果面試官沒有明確地告訴你,你應該詢問他是否需要做輸入檢查,還是不進行檢查直接寫函數。嚴格上說,應該對函數的輸入進行檢查。
第二點要記住:負數不是質數。同樣的,1和0也不是,因此,首先測試這些數字。此外,2是質數中唯一的偶數。沒有必要用一個循環來驗證4,6,8。再則,如果一個數字不能被2整除,那么它不能被4,6,8等整除。因此,你的循環必須跳過這些數字。如果你測試輸入偶數,你的算法將慢2倍(你測試雙倍數字)。可以采取其他一些更明智的優化手段,我這里采用的是適用於大多數情況的。例如,如果一個數字不能被5整除,它也不會被5的倍數整除。所以,沒有必要檢測10,15,20等等。
最后一點,你不需要檢查比輸入數字的開方還要大的數字。我感覺人們會遺漏掉這一點,並且也不會因為此而獲得消極的反饋。但是,展示出這一方面的知識會給你額外加分。
現在你具備了這個問題的背景知識,下面是總結以上所有考慮的解決方案:
function isPrime(number) { // If your browser doesn't support the method Number.isInteger of ECMAScript 6, // you can implement your own pretty easily if (typeof number !== 'number' || !Number.isInteger(number)) { // Alternatively you can throw an error. return false; } if (number < 2) { return false; } if (number === 2) { return true; } else if (number % 2 === 0) { return false; } var squareRoot = Math.sqrt(number); //平方根,比如Math.sqrt(9)為3 for(var i = 3; i <= squareRoot; i += 2) { if (number % i === 0) { return false; } } return true; }
其中代碼中用到了Number.isInteger(),該方法是ES6方法,用來判斷一個值是否為整數。
例如:
Number.isInteger(25) // true Number.isInteger(25.0) // true Number.isInteger(25.1) // false Number.isInteger("15") // false Number.isInteger(true) // false
需要注意的是,在JavaScript內部,整數和浮點數是同樣的儲存方法,所以25和25.0被視為同一個值。
16.記騰訊一次糟糕的筆試面試經歷(轉)
JS考察
1、基本數據類型:undefined、null、String、Number、boolean。
2、有以下兩個函數,定義一個對象使其擁有這兩個函數屬性。
function mobile(){ return 'mobile'; } function phone(){ return 'phone'; } var a = {}; a.mobile = mobile(); a.phone = phone(); console.log(a);
3、(考察了對象變量和堆內存)
var a = {n:10,m:20}; var b = a; b.n = 30; console.log(a.n); console.log(b);
結果:
30
Object {n: 30, m: 20}
4、(考察閉包)
var x = 20; var a = { x : 15, fn : function(){ var x = 30; return function(){ return this.x; }; } }; console.log(a.fn()); console.log((a.fn())()); console.log(a.fn()()); console.log(a.fn()() == (a.fn())()); console.log(a.fn().call(this)); console.log(a.fn().call(a));
結果:
1)、function(){return this.x;}
2)、20
3)、20
4)、true
5)、20
6)、15
5、(數組去重復項)
var arr = ['a','g','q','d','a','e','q']; Array.prototype.unique = function(){ for(var i = 0; i < this.length; i++){ for(var j = i+1; j < this.length; j++){ if(this[i] == this[j]){ this.splice(j,1); } } } return this; }; console.log(arr.unique());
此方法有缺陷,比如var arr = ['a','a','a','g','q','d','a','e','q']; 那么得到的結果:["a", "a", "g", "q", "d", "e"]。知道原因吧?不知道請查看數組去重的方法《JS實現數組去重方法總結》
6、編寫一個函數fn(Number n),將數字轉為大寫輸出,如輸入123,輸出一百二十三
function fn(n){ if(!/^([1-9]\d*)/.test(n)){ return '非法數據'; } var unit = '千百十億千百十萬千百十個'; if(n.length > unit.length){ return '數據過長'; } var newStr = ''; var nlength = n.length; unit = unit.substr(unit.length - nlength); for(var i = 0; i < nlength; i++){ newStr += '零一二三四五六七八九'.charAt(n[i]) + unit.charAt(i); } newStr = newStr.substr(0,newStr.length-1); newStr = newStr.replace(/零(千|百|十)/g,'零').replace(/(零)+/g,'零').replace(/零(億|萬)/g,'$1'); return newStr; } console.log(fn('205402002103'));
CSS
1、考察了盒子模型
2、內聯元素、塊元素
3、css3的貝塞爾曲線(張鑫旭大神的解說)
4、彈性盒子flexbox
綜合考察
1、js跨域問題
算法考察
1、有36輛自動賽車和6條跑道,沒有計時器的前提下,最少用幾次比賽可以篩選出最快的三輛賽車?
2、一面牆,單獨工作時,A花18小時砌好,B花24小時,C花30小時,現A, B, C的順序輪流砌,每人工作1小時換班,完工時,B總共干了多少小時?
A. 9小時
B. 8小時
C. 7小時
D. 6小時48分
答案:B,C
原因:
按照A,BC輪流砌,沒有說明誰先開始。
1/18 + 1/24 + 1/30 = 47/360;
共同完成7小時:7*47/360 = 329/360,還差31/360;
如果A先砌:則B砌了7小時44分鍾。
如果B先砌:則B砌了8小時。
如果C先砌:則B砌了7小時。
17.語義化標簽
1)tite與h1的區別
2)b與strong的區別
3)i與em的區別
PS:不要小看這些題,80%人答不上來
title與h1的區別
定義:title是網站標題,h1是文章主題
作用:title概括網站信息,可以直接告訴搜索引擎和用戶這個網站是關於什么主題和內容的,是顯示在網頁Tab欄里的;h1突出文章主題,面對用戶,更突出其視覺效果,指向頁面主體信息,是顯示在網頁中的。
b與strong的區別
定義:b(bold)是實體標簽,用來給文字加粗,而strong是邏輯標簽,作用是加強字符語氣
區別:b標簽只是加粗的樣式,沒有實際含義,常用來表達無強調或着重意味的粗體文字,比如文章摘要中的關鍵詞、評測文章中的產品名稱、文章的導言; 而strong表示標簽內字符重要,用以強調,其默認格式是加粗,但是可以通過CSS添加樣式,使用別的樣式強調。
建議:為了符合CSS3的規范,b應盡量少用而改用strong
i與em的區別
定義:i(italic)是實體標簽,用來使字符傾斜,而em(emphasis)是邏輯標簽,作用是強調文本內容
區別:i標簽只是斜體的樣式,沒有實際含義,常用來表達無強調或着重意味的斜體,比如生物學名、術語、外來語(比如「de facto」這樣的英語里常用的拉丁語短語);而em表示標簽內字符重要,用以強調,其默認格式是斜體,但是可以通過CSS添加樣式
建議:為了符合CSS3的規范,i應盡量少用而改用em
下面擴展一些其它的標簽屬性區別:
img中的alt與title屬性
alt屬性是在你的圖片因為某種原因不能加載時在頁面顯示的提示信息,它會直接輸出在原本加載圖片的地方
title屬性是在你鼠標懸停在該圖片上時顯示一個小提示,鼠標離開就沒有了,有點類似jQuery的hover
src與href的區別
定義:href指定網絡資源的位置建立鏈接或關系,用在link和a等元素上。src將外部資源嵌入到當前標簽所在位置,如img圖片和js腳本等
區別:我們在可替換的元素上使用src,然而把href用於在涉及的文檔和外部資源之間建立一個關系。 瀏覽器解析src屬性時,會暫停其他資源的下載和處理,直至將該資源加載,編譯,執行完畢。 瀏覽器解析到href的時候會識別該鏈接內容,對其進行下載不會停止對當前文檔的處理
18.事件綁定相關
addEventListener,第三個參數是用來表示事件是以事件冒泡還是事件捕獲這個各位都知道!但是他問的問題是:
我們給一個dom同時綁定兩個點擊事件,一個用捕獲,一個用冒泡,你來說下會執行幾次事件,然后會先執行冒泡還是捕獲!!!
來吧,誰能說出來。。。。
19.CSS選擇器問題
考察優先級問題,反正會出很多莫名其妙的變形,比如將style標簽寫在body后與body前有什么區別,比如同一dom應用多個class其應該如何表現,比如class a定義顏色為blue,class b定義顏色為red,同時應用到dom上,dom作何顯示。。。
好吧各位去回答吧。。。。。
20.一段關於JS中this應用奇葩代碼引發的思考
function DemoFunction(){ this.init = function(){ var func = (function(va){ this.va = va; return function(){ va += this.va; return va; } })(function(va1, va2){ var va3 = va1 + va2; return va1; }(1,2)); console.log(func(20)); this.func = func; console.log(this.func(100)); } } var a = new DemoFunction(); a.init();
結果:
2
NAN
首先我們得有如下幾個概念:
-
執行上下文:每次當控制器轉到ECMAScript可執行代碼時,即會進入一個可執行上下文,參考文獻:深入理解JavaScript系列(11):執行上下文(Execution Contexts)
-
this:this的創建是在 “進入執行上下文” 時創建的,在代碼執行過程中是不可變的,參考文獻:深入理解JavaScript系列(13):This? Yes,this!
-
自執行函數:准確來說應該叫:立即調用函數表達式。因為他聲明后即執行,參考文獻:深入理解JavaScript系列(4):立即調用的函數表達式
詳細解釋此段代碼
一、首先看DemoFunction的構造函數
這是代碼的重點,第一層代碼可以縮減為如下:
function DemoFunction(){ this.init = function(){ //省略代碼.... } }
表示為DemoFunction的實例提供init方法(聲明:此處有誤導成份,方法應盡可能放在原型鏈接上,也就是prototype上。),對外公開的接口。
二、在init方法中,再次省略代碼如下:
var func = (function(va){ this.va = va; return function(){ va += this.va; return va; } })(/*省略代碼...*/); //省略代碼....
上面代碼介紹:
-
首先定義了一個立即執行函數,並把此函數的執行結果賦值給func。
-
需要注意立即執行函數中this.va=va這行代碼,由於立即執行函數沒有調用者,所以在進入可執行上下文時,this會被賦值為Global(瀏覽器中為window對象)。
-
更需要注意立即執行函數,返回的是一個匿名函數,也是一個閉包,在這里一定要注意一個問題:this是在進入可執行上下文時創建的。
三、在init方法中,注意如下代碼:
var func = (function(va){ this.va = va; return function(){ va += this.va; return va; } })(function(va1, va2){ var va3 = va1 + va2; return va1; }(1,2)); //省略代碼....
va的實際參數是一個自執行匿名函數,這個匿名函數接受了兩個參數va1,va2,但只返回了va1。以此為據,那么可以確定va的值也就為1。接着就執行this.va=va這句代碼,由於當前this為window,所以參數va的值被賦值到了window的一個叫va的屬性上。
四、在init方法中,加上輸出語句:
var func = (function(va){ this.va = va; return function(){ va += this.va; return va; } })(function(va1, va2){ var va3 = va1 + va2; return va1; }(1,2)); console.log(func(20)); this.func = func; console.log(this.func(100)); }
結果分析:
-
第一個console.log輸出的是func(20),這里一定要注意調用者是沒有具體指定的,此時默認的就是Global(也就是widnow對象),因此輸出為:2
-
第二個console.log輸出的是this.func(100),可以看到this.func與func是指向同一個函數的引用,但此時的調用者則指定為this,也就是當前對象的實例,因此輸出為:NaN。原因:this(當前對象的實例)作為調用者,在func的函數中va += this.va這句代碼中的this是指向當前對象的實例,但當前對象的實例上是沒有va屬性的。但是va是有值的,當前值為2了。是因為閉包把va值存到內存中了。那么如何讓第二次得到的值也是2呢,結果很簡單,如下:
function DemoFunction(){ this.va = 0; this.init = function(){ var func = (function(va){ this.va = va; return function(){ va += this.va; return va; } })(function(va1, va2){ var va3 = va1 + va2; return va1; }(1,2)); console.log(func(20)); this.func = func; console.log(this.func(100)); } } var a = new DemoFunction(); a.init();
21.列舉你工作中遇到的IE6 BUG,談談解決方案
a.雙倍邊距bug:
例如:當給父元素內第一個浮動元素設置margin-left或margin-right的時候,margin屬性會加倍,此時需要添加屬性display:inline.
這樣能避免雙倍邊距
b當浮動元素與非浮動元素相鄰時,這個3像素的Bug就會出現,它會偏移3像素。我的解決辦法是給非浮動元素加上浮動就可以了
c.當子元素浮動未知高度時,使父容器適應子元素的高度bug
overflow:auto;——-讓父容器自適應子元素的高度
在IE6會自動擴展父層元素的高度,而IE8和FF等瀏覽器加上overflow:auto后,即可清除浮動。
22.如何用CSS分別單獨定義IE6、7、8的width屬性
所有瀏覽器 通用
height: 100px;
IE6 認:
_height: 100px;
IE6 ,IE7 都認:
*height: 100px;
IE7
*+height: 100px;
23.CSS中哪些屬性不可以從父元素繼承
例如border(邊框)、margin(邊距)、padding(補白)和背景
24.你如何理解HTML結構的語意化
HTML結構是頁面的骨架, 一個頁面就好像一幢房子, HTML結構就是鋼筋混泥土的牆,一幢房子如果沒有鋼筋混泥土的牆那就是一堆費磚頭, 不能住人,不能辦公。css是裝飾材料, css如果沒有html結構那就是一堆木板,一同油漆,沒有了實際使用價值。當我們提到“語義標記”的時候,我們所說的HTML應該是完全脫離表現信息的,其中的標簽應該都是語義化地定義了文檔的結構。
意義:
- 這樣有利於讀取設備將根據自身條件合適地顯示頁面
- 搜索引擎的爬蟲也根據語義化的結構進行搜索
- 便於團隊開發和維護
25.做好SEO需要考慮什么
SEO就是搜索引擎的優化
1、了解搜索引擎如何抓取網頁和如何索引網頁
你需要知道一些搜索引擎的基本工作原理,各個搜索引擎之間的區別
2、Meta標簽優化
主要包括主題(Title),網站描述(Description),和關鍵詞(Keywords)。還有一些其它的隱藏文字比如Author(作者),Category(目錄),Language(編碼語種)等。
3、如何選取關鍵詞並在網頁中放置關鍵詞
搜索就得用關鍵詞。關鍵詞分析和選擇是SEO最重要的工作之一。首先要給網站確定主關鍵詞(一般在5個上下),然后針對這些關鍵詞進行優化,包括關鍵詞密度(Density),相關度(Relavancy),突出性(Prominency)等等。
4、了解主要的搜索引擎
雖然搜索引擎有很多,但是對網站流量起決定作用的就那么幾個。比如英文的主要有Google,Yahoo,Bing等;中文的有百度,搜狗,有道等。 不同的搜索引擎對頁面的抓取和索引、排序的規則都不一樣。還要了解各搜索門戶和搜索引擎之間的關系,比如AOL網頁搜索用的是Google的搜索技 術,MSN用的是Bing的技術。
5、主要的互聯網目錄
6、你得學會用最少的廣告投入獲得最多的點擊。
7、搜索引擎提交
8、鏈接交換和鏈接廣泛度(Link Popularity)
跟獲取你的訪問量有很大的關系
9、標簽的合理使用
比如盡量少用iframe,搜索引擎不會抓取到iframe里的內容,重要內容不要放在框架中。
不可使用display:none;的方法讓文字隱藏,因為搜索引擎會過濾掉display:none;里邊的內容,就不會被蜘蛛檢索了。可以設置text-indent為負數,偏離瀏覽器之外,然后再利用overflow:hidden屬性進行隱藏
26.我們知道可以以外鏈的方式引入CSS文件,請談談外鏈引入CSS有哪些方式,這些方式的性能有區別嗎
a.行內樣式
缺點:通用性差,效果特殊,優點:使用在CSS命令較少,並且不常改動的地方,使用這種方法反而是很好的選擇。
b.內嵌樣式:css寫在head標簽里面
優點:直接在HTML文檔中,運用這樣式比較快。缺點:代碼臃腫,不利於維護
c.鏈接樣式:引入外部的css文件
比較易於維護和美觀的一種方式
d.導入樣式
優點:一次性導入多個css文件。用於css文件數量龐大的系統中
27.CSS Sprite是什么,談談這個技術的優缺點
CSS Sprite其實就是把網頁中一些背景圖片整合到一張圖片文件中,再利用CSS的“background-image”,“background- repeat”,“background-position”的組合進行定位
CSS Sprites能減少圖片的字節,加快網頁的加載速度。缺點是開發和維護都是比較麻煩的。
28.以CSS3標准定義一個webkit內核瀏覽器識別的圓角(尺寸隨意)
border:30px;-webkit-border-radius:40px;
29.如何觸發這Doctype的標准模式和混雜模式?區分它們有何意義?
在標准模式中,瀏覽器根據規范呈現頁面。在混雜模式中,頁面以一種比較寬松的向后兼容的方式顯示。
觸發混亂模式:
IE6的觸發:
DOCTYPE前加入XML聲明<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
IE7的觸發:
在XML聲明和DOCTYPE之間加入HTML注釋<?xml version="1.0" encoding="utf-8"?><!– … and keep IE7 in quirks mode –><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
IE6和IE7都可以觸發:
在HTML4.01的DOCTYPE文檔頭部加入HTML注釋<!– quirks mode –><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
各個瀏覽器的混雜模式,基本就是各個瀏覽器的私有模式,不相互兼容。所以,除非是為了兼容的問題,才采用混雜模式
30.行內元素有哪些?塊級元素有哪些?CSS的盒模型?
行內元素有:a b span I img input select strong(input用於定義表單中的各個具體的表單元素)
塊級元素有:div ul ol li dl dt dd
盒模型:margin border padding content
31.前端頁面有哪三層構成,分別是什么?作用是什么?
網頁分成三個層次,即:結構層、表示層、行為層。
網頁的結構層:由HTML 或XHTML 之類的標記語言負責創建,即HTML的語義化。,說白了就是一些標簽
網頁的表示層:說白了就是CSS
網頁的行為層:說白了就是Javascript 語言和DOM 主宰的領域。
32.有沒有關注HTML5和CSS3?如有請簡單說一些您對它們的了解情況!
IE9以上開始支持
HTML5標簽的改變:<header>,<footer>, <dialog>, <aside>, <figure>, <section> 等
CSS3實現圓角,陰影(text-shadow)對文字加特效(text-overflow,word-wrap,font-size-adjust),增加了更多的CSS選擇器(全局選擇器,組合選擇器,繼承選擇器,偽類選擇器等)
33.怎樣添加、移除、移動、復制、創建和查找節點
(1)創建新節點
createDocumentFragment() //創建一個DOM片段
createElement_x() //創建一個具體的元素
createTextNode() //創建一個文本節點
(2)添加、移除、替換、插入
appendChild()
removeChild()
replaceChild()
insertBefore()
(3)查找
getElementsByTagName() //通過標簽名稱
getElementsByName() //通過元素的Name屬性的值
getElementById() //通過元素Id,唯一性
34.面向對象編程:b怎么繼承a
function A(name,age){ this.name=name?name:'小剛'; this.age=age?age:30; this.say=function(){ alert(this.name+"今年"+this.age+"歲了"); } } function B(){} B.prototype=new A(); var C= new B(); C.say();
35.請編寫一個JavaScript函數 parseQueryString,它的用途是把URL參數解析為一個對象
function parseQueryString(url) { var pos; var obj = {}; if (pos = url.indexOf("?") != -1) { var urlstring = url.substring(pos + 1, url.lenght – 1); var urlArr = urlstring.split("&"); var keyValue = []; for (var i = 0; i < urlArr.lenght; i++) { keyValue = urlArr[i].split("="); obj[keyValue[0]] = keyValue[1]; } } return obj; } var objUrl = parseQueryString(url);
36.在工作中,對瀏覽器的兼容性怎么看待的
在工作中會經常遇到一些瀏覽器的兼容性問題,考慮的主要有2塊方面的兼容性問題,一個是css樣式的兼容性,另一個是js的兼容性問題。
(1).對於css樣式來說,比如IE與火狐兩大瀏覽器,它們對自身的瀏覽器都有默認的padding,margin等值,我們只需要在寫樣式的時候先清除它們默認樣式的值,引入一個reset.css樣式有能很大程度上解決一些常見問題,除此之外當然還有其它的樣式問題,比如IE6的雙邊距問題,解決辦法對IE6寫樣式display:inline;就能解決問題,還比如當子元素浮動未知高度時,使父容器自適應子元素的高度bug,解決辦法就是在父容器樣式里面加上overflow:auto就能解決(這個問題IE6中能適應子元素的高度,但是IE8跟火狐等其它瀏覽器不行,需要加上剛才的代碼才能實現自適應),還比如當一個浮動元素跟一個非浮動元素相鄰時就會出現3像素的bug,解決辦法其實很簡單給非浮動元素加上浮動就可以解決。
(2).對於js代碼來說,也有一些常見的瀏覽器兼容性問題,就拿瀏覽器事件處理來說,IE事件處理程序需要2個方法來實現,attachEvent()和detachEvent()兩個方法來實現,它們里邊的參數只有2個,比如attachEvent()方法的兩個參數:事件處理程序名字與事件處理程序函數。其它瀏覽器用到的是addEventListener()和removeEventListener()兩個方法,但是它們的參數有3個,拿addEventListener()方法舉例,第一個參數,要處理的事件名,比如onclick,但是不需要加上on,參數里面只需要click,第二個參事件處理程序的函數,最后一個參數是布爾值。布爾值如果是true,表示在捕獲階段調用事件處理程序;如果是false,表示在冒泡階段調用事件處理程序。
37.new 操作符具體干了什么?
new共經歷了四個過程。
var fn = function () { }; var fnObj = new fn();
1、創建了一個空對象
var obj = new object();
2、設置原型鏈
obj._proto_ = fn.prototype;
3、讓fn的this指向obj,並執行fn的函數體
var result = fn.call(obj);
4、判斷fn的返回值類型,如果是值類型,返回obj。如果是引用類型,就返回這個引用類型的對象。
if (typeof(result) == "object"){ fnObj = result; } else { fnObj = obj; }
38.[] 和 Array 調用 slice 方法引起的問題
問題表示:在某些場景下,需要將函數的 arguments 參數作為一個數組調用,但是 arguments 是一個奇異對象,所以試着將 arguments 轉化為一個數組;
function argToArr(){ return [].slice.call(arguments, 0); } console.log(argToArr(1,2,3)); //[1,2,3] function argToArr(){ return Array.slice.call(arguments, 0); } console.log(argToArr(1,2,3)); //[]
問:這是為什么呢?
另外還有一個問題,是關於 Array 是怎么找到 slice 方法的?
Array 本身是沒有 slice 方法,它的方法在 Array.prototype 中,而我們在調用 slice 方法的時候,如果在 Array 本身沒有找到 slice 方法的話,會通過它的原型鏈往上查找,而 Array.proto 並沒有指向 Array.prototype,而是指向 Function(),那么它是怎么找到 slice 方法的呢?
解釋:
第二段代碼報錯
是因為Array是構造函數
,不是對
象,打開控制台,輸入 typeof Array,結果是 function
你也說了slice()方法在其原型對象中,而[]就是Array的原型對象,在控制台中輸入 Array.prototype,結果是[],所以第一段代碼可以順利執行。
第二段代碼如下修改就可以了:
functionargToArr(){
return Array.prototype.slice.call(arguments, 0); // 改這一行 } console.log(argToArr(1,2,3));
其實你的本質問題就在於誤認為Array是數組對象,然而它是構造函數。
39.使用localStorage存儲數據,存儲位置在哪里?
這個是瀏覽器隔離的,每個瀏覽器都會把localStorage存儲在自己的UserData中,如chrome一般就是
C:\Users\你的計算機名\AppData\Local\Google\Chrome\User Data\Profile\Local Storage
如果要在瀏覽器查看,打開調試工具,在application選項卡下可以查看。
40.([] + {}).length ?
[]
+ {}
運算,首先是調用對象的 valueOf
方法,如果返回一個基本類型,則以該基本類型參與運算;否則調用 toString
方法,返回基本類型則參與運算。
數組和對象的 valueOf
(默認)返回自身,因此不是基本類型,接着調用 toString
,空數組返回空字符串,普通對象始終返回字符串 [object Object]
。故視為兩個字符串的拼接,結果為字符串 [object Object]
,其長度為 15。
一個例外是Date
的實例,其實例首先調用 toString
,接着才調用valueOf
。
可以這樣驗證:
([]).toString() // "" ({}).toString() // "[object Object]" ([]+{}) // "[object Object]"
41.渲染優化
1.禁止使用iframe(阻塞父文檔onload事件);
*iframe會阻塞主頁面的Onload事件; *搜索引擎的檢索程序無法解讀這種頁面,不利於SEO; *iframe和主頁面共享連接池,而瀏覽器對相同域的連接有限制,所以會影響頁面的並行加載。 使用iframe之前需要考慮這兩個缺點。如果需要使用iframe,最好是通過javascript 動態給iframe添加src屬性值,這樣可以繞開以上兩個問題。 2.禁止使用gif圖片實現loading效果(降低CPU消耗,提升渲染性能); 3、使用CSS3代碼代替JS動畫(盡可能避免重繪重排以及回流); 4、對於一些小圖標,可以使用base64位編碼,以減少網絡請求。但不建議大圖使用,比較耗費CPU; 小圖標優勢在於: 1.減少HTTP請求; 2.避免文件跨域; 3.修改及時生效; 5、頁面頭部的<style></style> 會阻塞頁面;(因為 Renderer進程中 JS線程和渲染線程是互斥的); 6、頁面頭部<script</script> 會阻塞頁面;(因為 Renderer進程中 JS線程和渲染線程是互斥的); 7、頁面中空的 href 和 src 會阻塞頁面其他資源的加載 (阻塞下載進程); 8、網頁Gzip,CDN托管,data緩存 ,圖片服務器; 9、前端模板 JS+數據,減少由於HTML標簽導致的帶寬浪費,前端用變量保存AJAX請求結果,每次操作本地變量,不用請求,減少請求次數 10、用innerHTML代替DOM操作,減少DOM操作次數,優化javascript性能。 11、當需要設置的樣式很多時設置className而不是直接操作style。 12、少用全局變量、緩存DOM節點查找的結果。減少IO讀取操作。 13、避免使用CSS Expression(css表達式)又稱Dynamic properties(動態屬性)。 14、圖片預加載,將樣式表放在頂部,將腳本放在底部 加上時間戳。 15、 避免在頁面的主體布局中使用table,table要等其中的內容完全下載之后才會顯示出來,顯示比div+css布局慢。 對普通的網站有一個統一的思路,就是盡量向前端優化、減少數據庫操作、減少磁盤IO。 向前端優化指的是,在不影響功能和體驗的情況下,能在瀏覽器執行的不要在服務端執行, 能在緩存服務器上直接返回的不要到應用服務器,程序能直接取得的結果不要到外部取得, 本機內能取得的數據不要到遠程取,內存能取到的不要到磁盤取,緩存中有的不要去數據庫查詢。 減少數據庫操作指減少更新次數、緩存結果減少查詢次數、將數據庫執行的操作盡可能的讓你的程序完成(例如join查詢), 減少磁盤IO指盡量不使用文件系統作為緩存、減少讀寫文件次數等。程序優化永遠要優化慢的部分,換語言是無法“優化”的。
42.事件的各個階段
1:捕獲階段 ---> 2:目標階段 ---> 3:冒泡階段
document ---> target目標 ----> document
由此,addEventListener的第三個參數設置為true和false的區別已經非常清晰了:
true表示該元素在事件的“捕獲階段”(由外往內傳遞時)響應事件;
false表示該元素在事件的“冒泡階段”(由內向外傳遞時)響應事件。
43.快速的讓一個數組亂序
var arr = [1,2,3,4,5,6,7,8,9,10]; arr.sort(function(){ return Math.random() - 0.5; }) console.log(arr);
44.最快捷的數組求最大值
var arr = [ 1,5,1,7,5,9]; Math.max(...arr) // 9
45.更短的數組去重寫法
[...new Set([2,"12",2,12,1,2,1,6,12,13,6])] // [2, "12", 12, 1, 6, 13]
詳細可參考:《JS實現數組去重方法整理》