WEB前端常見面試題彙總:(一)

1.JS找字符串中出現最多的字符

例如:求字符串'nininihaoa'中出現次數最多字符javascript

方法一:css

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); } } 

結果如圖所示:html

方法二,固然還可使用reduce方法來實現:前端

var arrString = 'abcdaabc'; arrString.split('').reduce(function(res, cur) { res[cur] ? res[cur] ++ : res[cur] = 1 return res; }, {})

想詳細瞭解reduce()方法,能夠參考:《JS數組reduce()方法詳解及高級技巧java

2.JS實現九九乘法表

jQuery實現方式:node

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>')); } } 

實現結果如圖所示:css3

原生js實現方式:
css代碼:web

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 爲jackb.info.nametom。緣由我想你從第三題已經得出來了。

5.a.name輸出jack,b.name輸出tom。緣由我想你從第三題已經得出來了。

6.a.info.name 爲jackb.info.nametom。緣由我想你從第三題已經得出來了。

注意:第三題 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) //應該返回 "".

解題思路

我將介紹三種方法:

  1. 使用 `while` 循環
  2. 使用遞歸
  3. 使用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被隱式地做爲全局對象的屬性。基於這一點,函數返回windowfullname,在本例中即爲第一行代碼設置的。

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考察

一、基本數據類型:undefined、null、String、Number、boolean。
二、有如下兩個函數,定義一個對象使其擁有這兩個函數屬性。

function mobile(){ return 'mobile'; } function phone(){ return 'phone'; } var a = {}; a.mobile = mobile(); a.phone = phone(); console.log(a);

三、(考察了對象變量和堆內存)

 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}

四、(考察閉包)

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

五、(數組去重複項)

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實現數組去重方法總結

六、編寫一個函數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

一、考察了盒子模型
二、內聯元素、塊元素
三、css3的貝塞爾曲線(張鑫旭大神的解說
四、彈性盒子flexbox

綜合考察

一、js跨域問題

算法考察

一、有36輛自動賽車和6條跑道,沒有計時器的前提下,最少用幾回比賽能夠篩選出最快的三輛賽車?

二、一面牆,單獨工做時,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

首先咱們得有以下幾個概念:

詳細解釋此段代碼

1、首先看DemoFunction的構造函數

這是代碼的重點,第一層代碼能夠縮減爲以下:

function DemoFunction(){ this.init = function(){ //省略代碼....  } }

表示爲DemoFunction的實例提供init方法(聲明:此處有誤導成份,方法應儘量放在原型連接上,也就是prototype上。),對外公開的接口。

2、在init方法中,再次省略代碼以下:

var func = (function(va){ this.va = va; return function(){ va += this.va; return va; } })(/*省略代碼...*/); //省略代碼....

上面代碼介紹:

  • 首先定義了一個當即執行函數,並把此函數的執行結果賦值給func。

  • 須要注意當即執行函數中this.va=va這行代碼,因爲當即執行函數沒有調用者,因此在進入可執行上下文時,this會被賦值爲Global(瀏覽器中爲window對象)。

  • 更須要注意當即執行函數,返回的是一個匿名函數,也是一個閉包,在這裏必定要注意一個問題:this是在進入可執行上下文時建立的。

3、在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的屬性上。

4、在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分別單獨定義IE六、七、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就是搜索引擎的優化

一、瞭解搜索引擎如何抓取網頁和如何索引網頁
你須要知道一些搜索引擎的基本工做原理,各個搜索引擎之間的區別

二、Meta標籤優化
主要包括主題(Title),網站描述(Description),和關鍵詞(Keywords)。還有一些其它的隱藏文字好比Author(做者),Category(目錄),Language(編碼語種)等。

三、如何選取關鍵詞並在網頁中放置關鍵詞
搜索就得用關鍵詞。關鍵詞分析和選擇是SEO最重要的工做之一。首先要給網站肯定主關鍵詞(通常在5個上下),而後針對這些關鍵詞進行優化,包括關鍵詞密度(Density),相關度(Relavancy),突出性(Prominency)等等。

四、瞭解主要的搜索引擎
雖然搜索引擎有不少,可是對網站流量起決定做用的就那麼幾個。好比英文的主要有Google,Yahoo,Bing等;中文的有百度,搜狗,有道等。 不一樣的搜索引擎對頁面的抓取和索引、排序的規則都不同。還要了解各搜索門戶和搜索引擎之間的關係,好比AOL網頁搜索用的是Google的搜索技 術,MSN用的是Bing的技術。

五、主要的互聯網目錄

六、你得學會用最少的廣告投入得到最多的點擊。

七、搜索引擎提交

八、連接交換和連接普遍度(Link Popularity)
跟獲取你的訪問量有很大的關係

九、標籤的合理使用
好比儘可能少用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();

一、建立了一個空對象

var obj = new object();

二、設置原型鏈

obj._proto_ = fn.prototype;

三、讓fn的this指向obj,並執行fn的函數體

var result = fn.call(obj);

四、判斷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.修改及時生效; 五、頁面頭部的<style></style> 會阻塞頁面;(由於 Renderer進程中 JS線程和渲染線程是互斥的); 六、頁面頭部<script</script> 會阻塞頁面;(由於 Renderer進程中 JS線程和渲染線程是互斥的); 7、頁面中空的 href 和 src 會阻塞頁面其餘資源的加載 (阻塞下載進程); 8、網頁Gzip,CDN託管,data緩存 ,圖片服務器; 九、前端模板 JS+數據,減小因爲HTML標籤致使的帶寬浪費,前端用變量保存AJAX請求結果,每次操做本地變量,不用請求,減小請求次數 10、用innerHTML代替DOM操做,減小DOM操做次數,優化javascript性能。 11、當須要設置的樣式不少時設置className而不是直接操做style。 12、少用全局變量、緩存DOM節點查找的結果。減小IO讀取操做。 13、避免使用CSS Expression(css表達式)又稱Dynamic properties(動態屬性)。 14、圖片預加載,將樣式表放在頂部,將腳本放在底部 加上時間戳。 1五、 避免在頁面的主體佈局中使用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實現數組去重方法整理

相關文章
相關標籤/搜索