轉載https://yeaseonzhang.github.io/
“金三銀四,金九銀十”,用來形容求職最好的幾個月。但是隨着行業的飽和,初中級前端er就業形勢不容樂觀。
行業狀態不可控,我們能做的當然只是讓自己變得更加具有競爭力。
今年自己也用了幾個月的時間來準備筆記面試,鞏固基礎知識。特此將自己在這個過程總結的題目分享出來,希望對於求職和準備求職的同學有所幫助。
CSS
列舉不同的清除浮動的技巧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
/* 1.添加新元素 */ <div class="outer"> <div class="div1"></div> <div class="div2"></div> <div class="div3"></div> <div class="clearfix"></div> </div> .clearfix { clear: both; } /* 2.爲父元素增加樣式 */ .clearfix { overflow: auto; zoom: 1; // 處理兼容性 } /* 3.:after 僞元素方法 (作用於父元素) */ .outer { zoom: 1; &:after { display: block; height: 0; clear: both; content: '.'; visibillity: hidden; } }
|
一像素邊框
使用sass
語法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
/* 定義 */ @mixin border-1px ($color) { position: relative; &:after { display: block; position: absolute; left: 0; bottom: 0; width: 100%; border-top: 1px solid $color; context: ''; } } @media (-webkit-min-device-pixel-radio: 1.5), (min-device-pixel-radio: 1.5) { border-1px { &:after { -webkit-transform: scaleY(0.7); transform: scaleY(0.7); } } } @media (-webkit-min-device-pixel-radio: 2), (min-device-pixel-radio: 2) { border-1px { &:after { -webkit-transform: scaleY(0.5); transform: scaleY(0.5); } } } /* 使用方式 */ @inclue border-1px(rgba(7, 17, 27, .1));
|
形成BFC(Block
Formatting Context)的幾種方式
BFC全稱”Block Formatting Context”, 中文爲“塊級格式化上下文”。BFC元素特性表現原則就是,內部子元素再怎麼翻江倒海,翻雲覆雨都不會影響外部的元素。
float
爲 left|right
overflow
爲 hidden|auto|scroll
display
爲 table-cell|table-caption|inline-block
position
爲 absolute|fixed
佈局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
/* HTML */ <div class="container"> <div class="left">Left silder</div> <div class="content">Main content</div> </div> /* CSS */ .container { overflow: hidden; } .left { float: left; width: 200px; margin-bottom: -9999px; padding-bottom: 9999px; background-color: #eee; } .content { margin-left: 200px; margin-bottom: -9999px; padding-bottom: 9999px; background-color: #ccc; } .left, .content { min-height: 200px; height: auto !important; }
|
JS
async
與defer
區別
異步(async) 腳本將在其加載完成後立即執行,而 延遲(defer) 腳本將等待 HTML 解析完成後,並按加載順序執行。
location.replace()
與location.asign()
區別
location.replace()
的url不會出現在history中
new
操作符
- 創建一個空對象,並且
this
變量引用該對象,同時還繼承了 該函數的原型 - 屬性和方法被加入到
this
引用的對象中 - 新創建的對象由
this
所引用,並且最後隱式的返回this
AMD
CMD CommonJS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
/* AMD是RequireJS對模塊化的定義 * CMD是seaJS對模塊化的定義 * CommonJS是Node對模塊化的規範 **/ /* AMD 依賴關係前置 */ define(['./a', './b'], function (a, b) { a.something(); b.something(); }) /* CMD 按需加載,依賴就近 */ define(function (require, exports, module) { var a = require('./a'); a.something(); var b = require('./b'); b.something(); })
|
DOM
操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
// 創建節點 createDocumentFragment() createElement() createTextNode() // 添加 移除 替換 插入 appendChild() removeChild() replaceChild() insertBefore() // 查找 getElementsByTagName() getElementsByName() getElementsByClassName() getElementById() querySelector() querySelectorAll()
|
JS設置css樣式的幾種方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
/* 1.直接設置style屬性 */ element.style.height = '100px'; /* 2.直接設置屬性 */ element.setAttribute('height', '100px'); /* 3.使用setAttribute設置style屬性 */ element.setAttribute('style', 'height: 100px !important'); /* 4.使用setProperty設置屬性,通過第三個參數設置important */ element.style.setProperty('height', '300px', 'important'); /* 5.設置cssText */ element.style.cssText += 'height: 100px !important';
|
阻止默認行爲
1 2 3 4 5 6 7 8 9 10
|
function stopDefault( e ) { // 阻止默認瀏覽器動作(W3C) if ( e && e.preventDefault ) { e.preventDefault(); } else { // IE中阻止函數器默認動作的方式 window.event.returnValue = false; } return false; }
|
阻止冒泡
1 2 3 4 5 6 7 8 9 10
|
function stopBubble(e) { // 如果提供了事件對象,則這是一個非IE瀏覽器 if ( e && e.stopPropagation ) { // 因此它支持W3C的stopPropagation()方法 e.stopPropagation(); } else { // 否則,我們需要使用IE的方式來取消事件冒泡 window.event.cancelBubble = true; } }
|
Ajax交互過程
- 創建XMLHttpRequest對象,也就是創建一個異步調用對象.
- 創建一個新的HTTP請求,並指定該HTTP請求的方法、URL及驗證信息.
- 設置響應HTTP請求狀態變化的函數.
- 發送HTTP請求.
- 獲取異步調用返回的數據.
- 使用JavaScript和DOM實現局部刷新.
考察知識點最廣的JS面試題
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
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(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName(); new new Foo().getName();
|
具體講解參見一道常被人輕視的前端JS面試題
JS數組深淺拷貝
1 2 3 4 5 6 7 8
|
var arr = ['old', 1, true, null, undefined]; var new_arr = arr.slice(); new_arr[0] = 'new'; console.log(arr) // ["old", 1, true, null, undefined] console.log(new_arr) // ["new", 1, true, null, undefined]
|
1 2 3 4 5 6 7 8
|
var arr = ['old', 1, true, null, undefined]; var new_arr = arr.concat(); new_arr[0] = 'new'; console.log(arr) // ["old", 1, true, null, undefined] console.log(new_arr) // ["new", 1, true, null, undefined]
|
以上兩種方法只是淺拷貝,如果數組元素是基本類型,就會拷貝一份新的;但是如果數組元素是對象或者數組,就只會拷貝引用(類似指針),修改其中一個就會影響另外一個。
1 2 3 4 5 6 7 8 9
|
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}]; var new_arr = arr.concat(); new_arr[0] = 'new'; new_arr[3][0] = 'new1'; console.log(arr) // ["old", 1, true, ['new1', 'old2'], {old: 1}] console.log(new_arr) // ["new", 1, true, ['new1', 'old2'], {old: 1}]
|
1 2 3 4 5 6 7 8 9
|
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}]; var new_arr = JSON.parse(JSON.stringify(arr)); new_arr[0] = 'new'; new_arr[3][0] = 'new1'; console.log(arr) // ["old", 1, true, ['old1', 'old2'], {old: 1}] console.log(new_arr) // ["new", 1, true, ['new1', 'old2'], {old: 1}]
|
簡單粗暴,但是問題是不能拷貝函數,不推薦。
然後我們來手動實現深淺拷貝。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
var shallowCopy = function (obj) { // 判斷是否是數組或者對象 if (typeof obj !== 'object') { return } var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj; }
|
1 2 3 4 5 6 7 8 9 10 11 12
|
var deepCopy = function (obj) { if (typeof obj !== 'object') { return } var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj }
|
數組去重
1 2 3 4 5 6
|
function unique (arr) { var res = arr.filter(function (item, index, array) { return array.indexOf(item) === index; }) return res; }
|
1 2 3 4 5
|
function unique (arr) { return arr.concat().sort().filter(function (item, index, array) { return !index || item !== array[index - 1]; }) }
|
1 2 3
|
function uniqu3 (arr) { return [... new Set(arr)]; }
|
找出數組中的最大值
1 2 3 4 5 6 7
|
var arr = [6, 4, 1, 8, 2, 11, 3]; function max (prev, next) { return Math.max(prev, next) } console.log(arr.reduce(max));
|
1 2 3
|
var arr = [6, 4, 1, 8, 2, 11, 3]; console.log(Math.max.apply(null, arr));
|
1 2 3 4 5 6 7
|
var arr = [6, 4, 1, 8, 2, 11, 3]; function max (arr) { return Math.max(...arr); } console.log(max(arr));
|
打亂數組的方法
1 2 3 4 5 6 7 8
|
var arr = []; for (var i = 0; i < 100; i++) { arr[i] = i; } arr.sort(function () { return 0.5 - Math.random(); });
|
數組扁平化
1 2 3 4 5 6 7 8 9 10 11 12
|
var arr = [1, [2, [3, 4]]]; function flatten(arr) { while (arr.some(item => Array.isArray(item))) { arr = [].concat(...arr); } return arr; } console.log(flatten(arr))
|
排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
// 冒泡 function bubbleSort2(arr) { var len = arr.length; for (var i = 0; i <= len - 1; i++) { for (var j = 0; j <= len - i; j++) { if (arr[j + 1] < arr[j]) { var temp; temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } return arr; } // 快速排序 function quickSort(arr) { if(arr.length == 0) { return []; // 返回空數組 } var cIndex = Math.floor(arr.length / 2); var c = arr.splice(cIndex, 1); var l = []; var r = []; for (var i = 0; i < arr.length; i++) { if(arr[i] < c) { l.push(arr[i]); } else { r.push(arr[i]); } } return quickSort(l).concat(c, quickSort(r)); }
|
數字格式化 1234567890
-> 1,234,567,890
1 2 3 4 5 6
|
function formatNum (num) { return num.replace(/\B(?=(\d{3})+(?!\d))/g, ','); } var num = '1234567890'; var res = formatNum(num); console.log(res);
|
打亂數組的方法
1 2 3 4 5 6 7 8
|
var arr = []; for (var i = 0; i < 100; i++) { arr[i] = i; } arr.sort(function () { return 0.5 - Math.random(); })
|
尾調用優化
即只保存內層函數的調用幀(只有開啓嚴格模式,纔會生效),只有不再用到外層函數的內部變量,內層函數的調用幀纔會取代外層函數的調用幀,否則無法進行“尾調用優化。
1 2 3 4 5 6 7 8 9 10 11 12
|
function factorial(n) { if (n === 1) return 1; return n * factorial(n-1); } factorial(5) /* 優化尾遞歸 */ function factorial(n, total) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5, 1)
|
柯里化
實現add(1,2)
和add(1)(2)
均輸出3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
function add () { let sum = 0; Array.prototype.forEach.call(arguments, function (item, index){ if (typeof item !== 'number') { return false; } else { sum += item; } }) var tmp = function () { Array.prototype.forEach.call(arguments, function (item, index){ if (typeof item !== 'number') { return false; } else { sum += item; } }) return tmp; } tmp.toString = function () { return sum } return tmp; } add(1, 2); // 3 add(1)(2); // 3 add(1, 2, 3)(1, 4)(2, 2)(1) // 16
|
ES8
新特性
1 2
|
str.padStart(targetLength [, padString]) str.padEnd(targetLength [, padString])
|
1 2
|
Object.values(obj) Object.entries(obj)
|
getOwnPropertyDescriptors
函數
1
|
Object.getOwnPropertyDescriptors(obj)
|
1
|
function es8(var1, var2, var3,) {}
|
- 異步函數
由async
關鍵詞定義的函數聲明瞭一個可以異步執行的函數,返回一個AsyncFunction
類型的對象。
1 2 3 4 5 6 7 8 9 10 11 12
|
fucntion fetchTextByPromise () { return new Promise(resolve => { setTimeout(() => { resolve('es8'); }, 2000); }); } async function sayHello () { const externalFetchedText = await fetchTextByPromise(); console.log(`Hello, ${externalFetchedText}`); } sayHello();
|
數據類型判斷
1 2 3 4 5 6 7 8 9 10
|
var class2type = {}; 'Boolean Number String Function Array Date RegExp Object Error Null Undefined'.split(' ').map((item, index) => { class2type['[object ' + item + ']'] = item.toLowerCase(); }) function type (obj) { return typeof obj === 'object' || typeof obj === 'function' ? class2type[{}.toString.call(obj)] || 'object' : typeof obj; }
|
防抖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
/* * func:需要調用的函數 * wait: 防抖時間 * immediate:布爾值,是否立即執行 **/ var debounce = function (func, wait, immediate) { var timeout; return function () { var context = this; var args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { // 是否立即執行func var callNow = !timeout; timeout = setTimeout(function () { timeout = null; }, wait); if (callNow) { func.apply(context, args); } } else { timeout = setTimeout(function () { func.apply(context, args); }, wait); } } }
|
簡單的字符串模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
var TemplateEngine = function(tpl, data) { var re = /<%([^%>]+)?%>/g, match; while(match = re.exec(tpl)) { tpl = tpl.replace(match[0], data[match[1]]) } return tpl; } var template = '<p>Hello, my name is <%name%>. I\'m <%age%> years old.</p>'; console.log(TemplateEngine(template, { name: "Yeaseon", age: 24 })); // '<p>Hello, my name is Yeaseon. I\'m 24 years old.</p>';
|
apply
、call
和bind
在嚴格模式下,未指定環境對象而調用函數,則this 值不會轉型爲window。除非明確把函數添加到某個對象或者調用apply()或call(),否則this 值將是undefined。
在非嚴格模式下,call、apply的第一個參數傳遞爲null或undefined時,函數體內的this會指向默認的宿主對象,在瀏覽器中則是window。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
var obj = { x: 81 }; var foo = { getX: function () { return this.x; } } console.log(foo.getX.bind(obj)()); console.log(foo.getX.apply(obj)); console.log(foo.getX.call(obj));
|
很明顯,bind
方法後面多了一對括號。也就是說,當你希望改變上下文環境之後並且立即執行,而是回調執行的時候(多用於事件監聽器函數),使用bind()
方法,而apply/call
則會立即執行函數。
- 定義一個 log 方法,讓它可以代理 console.log 方法。
1 2 3 4 5
|
function log(){ console.log.apply(console, arguments); }; log(1); //1 log(1,2); //1 2
|
- 給每一個 log 消息添加一個”(app)”的前輟,比如:
1
|
log("hello world"); //(app)hello world
|
1 2 3 4 5
|
function log(){ var args = Array.prototype.slice.call(arguments); args.unshift('(app)'); console.log.apply(console, args); };
|
1 2 3 4 5
|
function bind (fn, context) { return function () { return fn.apply(context, argument); } }
|
創建對象
1 2 3 4 5 6 7 8 9 10 11 12
|
function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var person1 = createPerson("Nicholas", 29, "Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor");
|
1 2 3 4 5 6 7 8 9 10
|
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");
|
1 2 3 4 5 6 7 8 9 10
|
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); person1.sayName(); //"Nicholas"
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; } Person.prototype = { constructor : Person, sayName : function(){ alert(this.name); } } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); alert(person1.friends); //"Shelby,Count,Van" alert(person2.friends); //"Shelby,Count" alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true
|
JS實現Jquery的extend()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
function extend() { // 默認不進行深拷貝 var deep = false; var name, options, src, copy, clone, copyIsArray; var length = arguments.length; // 記錄要複製的對象的下標 var i = 1; // 第一個參數不傳佈爾值的情況下,target 默認是第一個參數 var target = arguments[0] || {}; // 如果第一個參數是布爾值,第二個參數是 target if (typeof target == 'boolean') { deep = target; target = arguments[i] || {}; i++; } // 如果target不是對象,我們是無法進行復制的,所以設爲 {} if (typeof target !== "object" && !isFunction(target)) { target = {}; } // 循環遍歷要複製的對象們 for (; i < length; i++) { // 獲取當前對象 options = arguments[i]; // 要求不能爲空 避免 extend(a,,b) 這種情況 if (options != null) { for (name in options) { // 目標屬性值 src = target[name]; // 要複製的對象的屬性值 copy = options[name]; // 解決循環引用 if (target === copy) { continue; } // 要遞歸的對象必須是 plainObject 或者數組 if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { // 要複製的對象屬性值類型需要與目標屬性值相同 if (copyIsArray) { copyIsArray = false; clone = src && Array.isArray(src) ? src : []; } else { clone = src && isPlainObject(src) ? src : {}; } target[name] = extend(deep, clone, copy); } else if (copy !== undefined) { target[name] = copy; } } } } return target; };
|
自定義事件(通過觀察者模式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
function EventTarget () { this.handlers = {}; } EventTarget.prototype = { constructor: EventTarget, addHandler: function (type, handler) { if (typeof this.handlers[type] == 'undefined') { this.handlers[type] = []; } this.handlers[type].push(handler) }, fire: function (event) { if (!event.target) { event.target = this; } if (this.handlers[event.type] instanceof Array) { var handlers = this.handlers[event.type]; for (var i = 0, len = handlers.length; i < len; i++) { handlers[i](event); } } }, removeHandler: function (type, handler) { if (this.handlers[type] instanceof Array) { var handlers = this.handlers[type]; for (var i = 0, len = handlers.length; i < len; i++) { if (handlers[i] === handler) { break; } } handlers.splice(i, 1); } } }
|
安全
跨域的幾種方法
- 主域相同的跨域
-
window.postMessage
-
JSONP
跨域(只支持GET
)
1 2 3 4 5 6 7 8 9 10 11
|
function todo(data){ console.log('The author is: '+ data.name); } var script = document.createElement('script'); document.body.appendChild(script); /* 服務器收到這個請求以後,會將數據放在回調函數的參數位置返回。 */ todo({"name": "fewjq"}); /* 由於<script>元素請求的腳本,直接作爲代碼運行。這時,只要瀏覽器定義了todo函數,該函數就會立即調用。作爲參數的JSON數據被視爲JavaScript對象。*/
|
websocket
跨域
XSS
和 CSRF
性能
CSS
優化
- 正確的時機調用CSS
- 使用媒體查詢標記
<link>
,選擇性加載 - 減少css文件數量
- 壓縮css代碼
渲染