前端面試題合集

面臨找工做了,準備收集一些面試題目,供本身以及你們一塊兒學習吧~javascript

面試題目來源於網絡,做者只是整理~css


1、問題一: 關於 const 和 let 聲明的變量不在 window 上的疑問?

關於 const 和 let 聲明的變量不在 window 上的疑問?
複製代碼

回答區:html

在ES5中規定頂層對象的屬性和全局變量是等價的。經過 var 聲明的變量或者函數既是全局屬性又是頂層對象的屬性。前端

var a = 10;
var f = function(){}

console.log(window.a)  // 10
console.log(window.f)  // ƒ (){}
複製代碼

ES6中規定,var 聲明的全局函數和全局變量, 依舊是頂層對象的屬性,可是經過let或者const聲明的全局函數和全局變量,卻再也不是頂層對象的屬性。vue

const b = 20;
let ff = function(){};

console.log(window.b)  // undefined
console.log(window.ff)  // undefined
複製代碼

經過上圖也能夠看到,在全局做用域中,用 let 和 const 聲明的全局變量並無在全局對象中,只是一個塊級做用域(Script)中html5

const b = 20;
let ff = function(){};

console.log(b)  
console.log(ff) 
複製代碼

2、問題二: ['1', '2', '3'].map(parseInt)

['1', '2', '3'].map(parseInt) 解析
複製代碼

回答區java

map函數的格式:node

array.map( function( currentValue, index, arr ),  thisValue )

第一個參數:  必須。函數,數組中的每一個元素都會執行這個函數
            currentValue	必須。當前元素的值
            index	        可選。當前元素的索引值
            arr	                可選。當前元素屬於的數組對象
第二個參數: 可選。對象做爲該執行回調時使用,傳遞給函數,用做 "this" 的值。
           若是省略了 thisValue,或者傳入 null、undefined,那麼回調函數的 this 爲全局對象。
複製代碼

上題能夠理解爲執行下面的三條:react

parseInt('1', 0) //radix爲0時,且string參數不以「0x」和「0」開頭時,按照10爲基數處理。這個時候返回1
parseInt('2', 1) //基數爲1(1進制)表示的數中,最大值小於2,因此沒法解析,返回NaN
parseInt('3', 2) //基數爲2(2進制)表示的數中,最大值小於3,因此沒法解析,返回NaN
複製代碼

parseInt()詳解css3

主要解釋下面的計算方法:

parseInt("10");			//返回 10
parseInt("19",10);		//返回 19 (10+9)
parseInt("11",2);		//返回 3 (2+1)
parseInt("17",8);		//返回 15 (8+7)
parseInt("1f",16);		//返回 31 (16+15)
parseInt("010");		//未定:返回 10 或 8
複製代碼
parseInt(string,radix); 
複製代碼

規則以下:

其中的基數  radix.(不表明着進制) 不少人都誤覺得它表明着要轉換的進制數。
string要轉換的字符串,string 以 "0x" 開頭,parseInt() 會把 string 的其他部分解析爲十六進制的整數。

若是 string 以 0 開頭,那麼會把其後的字符解析爲八進制或十六進制的數字。若是 string 以 1 ~ 9 的數字開頭,parseInt() 將把它解析爲十進制的整數。
複製代碼

知道上面的規則後:

parseInt("10");	  

// 默認radix爲10,string爲數字開頭,則解析爲10進制的整數,則parseInt("10")=1*10^1+0*10^0=10;不變,其中10爲基數]

parseInt('11',2);
// radix 爲2, string爲數字開頭,則 parseInt('11',2) =1*2^1+1*2^0=3; 其中2爲基數

parseInt('1f',16);
// string爲1f,解析爲16進制。radix爲16,則=1*16^1+15*16^0=31;其中16爲基數,f=15;

parseInt("17",6)=1,parseInt('17'9)=16;
//當解析17時,1屬於6進制範圍,7不屬於6進制範圍,當string的數字小於radix時(7<6),它會只解析到它的上一位,
// parseInt('17',6) = parseInt('1',6) = 1;
複製代碼

例題:

var a=["1", "2", "3", "4", "5"]; a.map(parseInt); 


parseInt('1',0) // 1
parseInt('2',1) // nan
parseInt('3',2) // 由於2進制範圍爲(0-2) 3不在2進制範圍,因此NaN
parseInt('4',3) // 由於3進制範圍爲(0-2) 4不在3進制範圍,因此NaN
parseInt('5',4) // NaN

複製代碼

3、問題三: 說說你對 Set、Map、WeakSet 和 WeakMap 的認識

說說你對 Set、Map、WeakSet 和 WeakMap 的認識
複製代碼

Set

Set能夠理解成是一個數據結構,是一種集合類型的數據結構,相似與數組。內部的成員都是惟一的,不重複,無序。

new Set([iterable])
複製代碼

舉例子認識一下:

var s = new Set();
[1,2,3,4,5,4,3,2].forEach((x) => {
    s.add(x)   
})


// 數組去重
var arr = [1,2,3,4,5, 4,5,6,7,1];
[...new Set(arr)] // [1, 2, 3, 4, 5, 6, 7]
複製代碼

向set中添加內容的時候不會進行類型轉換,好比 5 和 「5」 會存爲兩個值。其內部使用了相似與===的機制,可是由不同,由於 NaN === NaN 返回false,可是set中卻會只存一次,認爲是相同的。

let set = new Set();
let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}

let set1 = new Set()
set1.add(5)
set1.add('5')
console.log([...set1])	// [5, "5"]
複製代碼

Set的屬性和方法

(1)Set的屬性

  • constructor: 構造函數
  • size:元素數量
let set = new Set([1, 2, 3, 2, 1])

console.log(set.length)	// undefined
console.log(set.size)	// 3
複製代碼

(2)Set的實例方法

  • add(value):新增,至關於 array裏的push
  • delete(value):存在即刪除集合中value
  • has(value):判斷集合中是否存在 value
  • clear():清空集合

set的基本操做:

let set = new Set()
set.add(1).add(2).add(1)

set.has(1)	// true
set.has(3)	// false
set.delete(1)	
set.has(1)	// false
複製代碼

Array.from 方法能夠將 Set 結構轉爲數組

var set = new Set([1,2,3,4,5]);

var array = Array.form(set);
console.log(array);

var arr = [...set];
複製代碼

最重要的就是遍歷方法了:

  • keys():返回一個包含集合中全部鍵的迭代器
  • values():返回一個包含集合中全部值得迭代器
  • entries():返回一個包含Set對象中全部元素得鍵值對迭代器
  • forEach(callbackFn, thisArg):用於對集合成員執行callbackFn操做,若是提供了 thisArg 參數,回調中的this會是這個參數,沒有返回值
var set = new Set([1,2,3,4]);
console.log(set.keys());

for (let i of set.keys()) {
    console.log(i);
}
// SetIterator {1, 2, 3, 4}
// 1
// 2
// 3
// 4

for (let i of set.values()) {
    console.log(i);
}
// 1
// 2
// 3
// 4

for (let i of set.entries()) {
    console.log(i);
}
// 返回鍵值對
// [1, 1]
// [2, 2]
// [3, 3]
// [4, 4]

set.forEach((value, key) => {
    console.log(key + ' : ' + value)
})	
// 1 : 1
// 2 : 2
// 3 : 3
// 4 : 4
複製代碼

set默認使用的是values以後的迭代器。

  • 使用map、filter
var set = new Set([1,2,3,4]);

[...set].map((item) => {
    return item * 2;
})
// 2,4,6,8

[...set].filter((item) => {
    return item >= 4;
})
// 4
複製代碼

所以,Set 很容易實現交集(Intersect)、並集(Union)、差集(Difference)

let set1 = new Set([1, 2, 3])
let set2 = new Set([4, 3, 2])

let intersect = new Set([...set1].filter(value => set2.has(value)))
let union = new Set([...set1, ...set2])
let difference = new Set([...set1].filter(value => !set2.has(value)))

console.log(intersect)	// Set {2, 3}
console.log(union)		// Set {1, 2, 3, 4}
console.log(difference)	// Set {1}
複製代碼

WeakSet

WeakSet容許你存放弱引用的對象。

WeakSet 與 Set的區別:

一、WeakSet 只能儲存對象引用,不能存放值,而 Set 對象均可以
二、WeakSet 存放的都是弱引用的對象,垃圾回收機制不會由於這些弱引用而放棄對該對象的回收。
三、WeakSet 對象裏有多少個成員元素,取決於垃圾回收機制有沒有運行,運行先後成員個數可能不一致,遍歷結束以後,有的成員可能取不到了(被垃圾回收了),WeakSet 對象是沒法被遍歷的(ES6 規定 WeakSet 不可遍歷),也沒有辦法拿到它包含的全部元素
複製代碼
var set = new WeakSet([[1,2], [3,4]]);

console.log(set)
複製代碼

  • add(value):在WeakSet 對象中添加一個元素value
  • has(value):判斷 WeakSet 對象中是否包含value
  • delete(value):刪除元素 value
var ws = new WeakSet()
var obj = {}
var foo = {}

ws.add(window)
ws.add(obj)

ws.has(window)	// true
ws.has(foo)	// false

ws.delete(window)	// true
ws.has(window)	// false
複製代碼

Map

Map是一種數據結構,存儲鍵值對。

Map構造函數的參數

var map = new Map([['a', 1], ['b', 2]]);

// Map(2) {"a" => 1, "b" => 2}

const set = new Set([
  ['foo', 1],
  ['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1
// Map(2) {"foo" => 1, "bar" => 2}
複製代碼

任何具備 Iterator 接口、且每一個成員都是一個雙元素的數組的數據結構均可以看成Map構造函數的參數。

let map = new Map();

map.set(-0, 123);
map.get(+0) // 123

map.set(true, 1);
map.set('true', 2);
map.get(true) // 1

map.set(undefined, 3);
map.set(null, 4);
map.get(undefined) // 3

map.set(NaN, 123);
map.get(NaN) // 123
複製代碼

Map 的屬性及方法

(1)屬性

  • constructor
  • size
const map = new Map([
  ['name', 'An'],
  ['des', 'JS']
]);

map.size // 2
複製代碼

(2)方法

  • set(key, value):向字典中添加新元素
  • get(key):經過鍵查找特定的數值並返回
  • has(key):判斷字典中是否存在鍵key
  • delete(key):經過鍵 key 從字典中移除對應的數據
  • clear():將這個字典中的全部元素刪除

遍歷方法

  • Keys():將字典中包含的全部鍵名以迭代器形式返回
  • values():將字典中包含的全部數值以迭代器形式返回
  • entries():返回全部成員的迭代器
  • forEach():遍歷字典的全部成員
const map = new Map([
            ['name', 'An'],
            ['des', 'JS']
        ]);
console.log(map.entries())	// MapIterator {"name" => "An", "des" => "JS"}
console.log(map.keys()) // MapIterator {"name", "des"}
console.log(map.values()
複製代碼

const reporter = {
  report: function(key, value) {
    console.log("Key: %s, Value: %s", key, value);
  }
};

let map = new Map([
    ['name', 'An'],
    ['des', 'JS']
])
map.forEach(function(value, key, map) {
  this.report(key, value);
}, reporter);
// Key: name, Value: An
// Key: des, Value: JS

forEach的第二個參數爲reporter對象,那麼this就只想這個對象。
複製代碼

Map轉爲其餘類型

Map 轉 Array

const map = new Map([[1, 1], [2, 2], [3, 3]])
console.log([...map])	// [[1, 1], [2, 2], [3, 3]]
複製代碼

Array 轉 Map

const map = new Map([[1, 1], [2, 2], [3, 3]])
console.log(map)	// Map {1 => 1, 2 => 2, 3 => 3}

複製代碼

Map 轉 Object

由於 Object 的鍵名都爲字符串,而Map 的鍵名爲對象,因此轉換的時候會把非字符串鍵名轉換爲字符串鍵名。

function mapToObj(map) {
    let obj = Object.create(null)
    for (let [key, value] of map) {
        obj[key] = value
    }
    return obj
}
const map = new Map().set('name', 'An').set('des', 'JS')
mapToObj(map)  // {name: "An", des: "JS"}

複製代碼

Object 轉 Map

function objToMap(obj) {
    let map = new Map()
    for (let key of Object.keys(obj)) {
        map.set(key, obj[key])
    }
    return map
}

objToMap({'name': 'An', 'des': 'JS'}) // Map {"name" => "An", "des" => "JS"}

複製代碼

Map 轉 JSON

function mapToJson(map) {
    return JSON.stringify([...map])
}

let map = new Map().set('name', 'An').set('des', 'JS')
mapToJson(map)	// [["name","An"],["des","JS"]]

複製代碼

JSON 轉 Map

function jsonToStrMap(jsonStr) {
  return objToMap(JSON.parse(jsonStr));
}

jsonToStrMap('{"name": "An", "des": "JS"}') // Map {"name" => "An", "des" => "JS"}

複製代碼

WeakMap

注意,WeakMap 弱引用的只是鍵名,而不是鍵值。鍵值依然是正常引用。

WeakMap 中,每一個鍵對本身所引用對象的引用都是弱引用,在沒有其餘引用和該鍵引用同一對象,這個對象將會被垃圾回收(相應的key則變成無效的),因此,WeakMap 的 key 是不可枚舉的。

屬性:

  • constructor:構造函數 方法:

  • has(key):判斷是否有 key 關聯對象

  • get(key):返回key關聯對象(沒有則則返回 undefined)

  • set(key):設置一組key關聯對象

  • delete(key):移除 key 的關聯對象

let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap();

myWeakmap.set(myElement, {timesClicked: 0});

myElement.addEventListener('click', function() {
  let logoData = myWeakmap.get(myElement);
  logoData.timesClicked++;
}, false);

複製代碼

總結:

- Set 集合

成員惟1、無序且不重複
[value, value],鍵值與鍵名是一致的(或者說只有鍵值,沒有鍵名)
能夠遍歷,方法有:add、delete、has

- WeakSet

成員都是對象
成員都是弱引用,能夠被垃圾回收機制回收,能夠用來保存DOM節點,不容易形成內存泄漏
不能遍歷,方法有add、delete、has

- Map 字典

本質上是鍵值對的集合,相似集合
能夠遍歷,方法不少能夠跟各類數據格式轉換

- WeakMap

只接受對象做爲鍵名(null除外),不接受其餘類型的值做爲鍵名
鍵名是弱引用,鍵值能夠是任意的,鍵名所指向的對象能夠被垃圾回收,此時鍵名是無效的
不能遍歷,方法有get、set、has、delete
複製代碼

4、問題四: 請描述下狀態碼304?

請描述下狀態碼304?
複製代碼

回答區:

304 表示客戶端有緩衝,而且服務端的資源未更新,能夠直接使用客戶端的,不須要再向服務器獲取
複製代碼

經常使用狀態碼:

5、問題五: 寫出5種css隱藏元素的辦法

寫出5種css隱藏元素的辦法
複製代碼

回答區

opacity: 0;

visibility: hidden;

display: none;

position: absolute; top: -9999px; left: -9999px;

clip-path: polygon(0px 0px,0px 0px,0px 0px,0px 0px); // 剪切掉
複製代碼

clip-path 案例1

clip-path 案例2

6、問題六: cookies 與session 有什麼區別?

cookies 與session 有什麼區別?
複製代碼

回答區

Cookies 機制

Http是無狀態的協議,所以沒法從網絡鏈接上來肯定用戶的身份,因此就誕生的Cookies。

客戶端第一次訪問服務器的時候,服務器就會生成一個cookie,並頒發給客戶端,這就是你的憑據了。當客戶端下次訪問的時候就會攜帶這個Cookies,服務器檢查該Cookie,以此來辨認用戶狀態。服務器還能夠根據須要修改Cookie的內容。

cookie的內容主要包括name(名字)value(值)maxAge(失效時間)path(路徑),domain(域)secure.

Cookies 怎麼使用在server端的呢?

一、用戶初次訪問,服務器在response的header中設置`Set-Cookie`,具體值由server端具體決定,這些內容包含一些銘感信息,並不安全。
二、客戶端收到響應後,就會在客戶端設置cookies
三、下一次訪問接口,就會在request帶上cookie,
四、server端拿到cookie,查看cookies信息,服務端經過返回的cookie信息去判斷本次鏈接的狀態,用戶的狀態。
五、Cookies能夠用在用戶登錄後狀態的保持
複製代碼

Session 機制

Session 是一種服務端機制,存儲在服務器中,用一種相似散列表的結構來保存信息。

服務器首先檢查這個客戶端裏的請求裏是否已包含了一個session標識--sessionID,若是已經包含一個sessionID,則說明之前已經爲此客戶端建立過session,服務器就按照sessionID把這個session檢索出來使用,若是客戶端請求不包含sessionID,則爲此客戶端建立一個session而且聲稱一個與此session相關聯的sessionID,

所以在客戶端只存一個sessionId,每次請求帶上這個。

session 怎麼使用在server端的呢?

客戶端第一次訪問,服務端判斷傳入的cookies中是否含有sessionId,若是有就去sessionData中去查詢是否記錄過這條id,而後找到對應信息,若是沒有就建立一個sessionId,插入到sessionData中。並在response中設置cookie,將sessionId設置進去,存到客戶端。客戶端下次的時候就會帶上了。這裏的sessionData保存在server進程內存中,這種方式並很差,咱們會用redis去解決。
複製代碼

- cookies
因爲http請求是無狀態的,須要cookie來作身份驗證
1.cookies由服務端建立,發送給瀏覽器端,存與瀏覽器端,當用戶發出請求後,帶上cookie發送給服務端作驗證。
2.來回傳遞過程當中,佔用帶寬,消耗網絡資源,而且容易被中間人獲取或瀏覽器端用戶篡改十分不安全
3.cookies大小隻有4k
4.方便js來操做數據


- session
1.session主要存在於服務端,不會被髮送到瀏覽器因此很安全
2.若是沒有設置過時時間,在會話結束後session會自動消失
3.session主要用於服務端保存請求信息的機制
4.服務端會爲每個用戶設置一個ID,高效安全。
複製代碼

7、問題七: 數組去重

數組去重
複製代碼

回答區

咱們可使用不一樣的方法來實現

// 測試數據 10萬數據

const arr = [];
// 生成[0, 100000]之間的隨機數
for (let i = 0; i < 100000; i++) {
  arr.push(0 + Math.floor((100000 - 0 + 1) * Math.random()))
}
複製代碼

【1】雙重循環法

// 數組去重
  function queue(arr) {
    var newArray = [];
    var isRepeat = false;

    for(var i = 0; i < arr.length; i++) {

      // 檢查是否重複
      isRepeat = false;
      for(var j = 0; j < newArray.length; j++) {
        if(newArray[j] === arr[i]) {
          isRepeat = true;
          break;
        }
      }

      if(!isRepeat) {
        newArray.push(arr[i]);
      }
    }
    return newArray;
  }

  var array = [1,2,3,4,5,6,2,3,4];
  var array1 = ['1xxx','3','2','3','2', 100];

  console.log(queue(array));
  console.log(queue(array1));
複製代碼
time: 3688.440185546875ms
複製代碼

【2】Array.prototype.indexOf()

// 數組去重
  Array.prototype.queue = function () {
    var newArray = [];
    this.forEach((item, index) => {
      if(newArray.indexOf(item) == -1) {
        newArray.push(item);
      }
    })
    return newArray;
  }
  var array = [1,2,3,4,5,6,2,3,4];
  var array1 = ['1xxx','3','2','3','2', 100];

  console.log(array.queue());
  console.log(array1.queue());
複製代碼
time: test2: 3766.324951171875ms
複製代碼

【3】Array.prototype.sort()

// 數組去重
    Array.prototype.queue = function () {
      var newArray = [];
      this.sort();
      for (var i = 0; i < this.length; i++) {
        if (this[i] !== newArray[newArray.length - 1]) {
          newArray.push(this[i]);
        }
      }
      return newArray;
    }
    var array = [1, 2, 3, 4, 5, 6, 2, 3, 4];
    var array1 = ['1xxx', '3', '2', '3', '2', 100];

    console.log(array.queue());
    console.log(array1.queue());
複製代碼
time: 121.6259765625ms
複製代碼

【4】Map

// 數組去重
    Array.prototype.queue = function () {
      const map = new Map();
      return this.filter((item, index) => {
        return !map.has(item) && map.set(item, 1)
      })
    }
    var array = [1, 2, 3, 4, 5, 6, 2, 3, 4];
    var array1 = ['1xxx', '3', '2', '3', '2', 100];
複製代碼

或者

// 數組去重
    Array.prototype.queue = function () {
      var newArray = [];
      const map = new Map();
      for (var i = 0; i< this.length; i++) {
        if( !map.get(this[i])) {
          map.set(this[i], 1);
          newArray.push(this[i])
        }
      }
      return newArray;
    }
    var array = [1, 2, 3, 4, 5, 6, 2, 3, 4];
    var array1 = ['1xxx', '3', '2', '3', '2', 100];

    console.log(array.queue());
    console.log(array1.queue());
複製代碼
test1: 27.89697265625ms
test2: 21.945068359375ms
複製代碼

【5】Set

// 數組去重
Array.prototype.queue = function () {
  const set = new Set(this);
  return Array.from(set);
}
var array = [1, 2, 3, 4, 5, 6, 2, 3, 4];
var array1 = ['1xxx', '3', '2', '3', '2', 100];

console.log(array.queue());
console.log(array1.queue());
複製代碼
// 數組去重
Array.prototype.queue = function () {
  return [...new Set(this)];
}
var array = [1, 2, 3, 4, 5, 6, 2, 3, 4];
var array1 = ['1xxx', '3', '2', '3', '2', 100];

console.log(array.queue());
console.log(array1.queue());
複製代碼
test1: 36.8046875ms
test2: 31.98681640625ms
複製代碼

除了考慮時間複雜度外、性能以外,還要考慮數組元素的數據類型

通過綜合考慮,最優的數組去重算法是採用Map數據結構實現的算法。

8、問題八: 將this is a pen首字母大寫

將this is a pen首字母大寫
複製代碼

回答區

var str = "this is a pen";

function ToUpper(str) {
  var arr = str.split(" ").map((item, index) => {
    return item.slice(0,1).toUpperCase() + item.slice(1)
  })

  return arr.join(" ");
}

console.log(ToUpper(str)); // This Is A Pen
複製代碼

或者

var str = "this is a pen";

function ToUpper(str) {
  // 正則匹配
  var s =  str.toLowerCase().replace(/\b\w+\b/g, function (item) {
    return item.slice(0,1).toUpperCase() + item.slice(1)
  })

  return s;
}

console.log(ToUpper(str)); // This Is A Pen
複製代碼

9、問題九: 關於this的題目

關於this的題目
複製代碼

案例1

var x = 10;
var obj = {
    x: 20,
    f: function(){
        console.log(this.x);        // 20
        var foo = function(){ 
            console.log(this.x);    
            }
        foo();                      // 10
    }
};
obj.f();
複製代碼

解析:

var x = 10;
var obj = {
    x: 20,
    f: function(){
        console.log(this.x);        // 20 這裏是隱性綁定
        var foo = function(){ 
            console.log(this.x);    
            }
        foo();                      // 10 這裏一看是光桿司令,不要誤認爲foo 在函數 f 裏,也在 f 裏執行,this必然就跟f裏面的this同樣。
    }
};
obj.f();

複製代碼

案例2

function foo(arg){
    this.a = arg; // window.a = arg
    return this  // return window
};

var a = foo(1);  // window.a = window
var b = foo(10);

console.log(a.a);    // undefined // window.a.a
console.log(b.a);    // 10 // window.a
複製代碼

案例3

var x = 10;
var obj = {
    x: 20,
    f: function(){ console.log(this.x); }
};
var bar = obj.f;
var obj2 = {
    x: 30,
    f: obj.f
}
obj.f(); // 20
bar();  // 10 
obj2.f(); // 30
複製代碼

10、問題十: 你能描述一下漸進加強和優雅降級嗎?

你能描述一下漸進加強和優雅降級嗎?
複製代碼

在咱們開發項目的時候,咱們使用的css3的新屬性挺多的。最初css3的時候不少瀏覽器不支持,當時css3標準仍是剛出來並無好的標準,各瀏覽器廠商按照草案,制定了本身的實現,只是須要在屬性前面添加不一樣瀏覽器的前綴。當標準確立後,各大瀏覽器開始逐步支持不帶前綴的css3新屬性。

下面有兩種兼容寫法:

.transition {
-webkit-transition: all .5s;
-moz-transition: all .5s;
-o-transition: all .5s;
transition: all .5s;
}
.transition {
transition: all .5s;
-o-transition: all .5s;
-moz-transition: all .5s;
-webkit-transition: all .5s;
}
複製代碼

這就引出了兩個概念:優雅降級和漸進加強。

漸近加強

漸近加強認爲項目應該注重自己,首先應該針對低版本的瀏覽器實現最基本的功能,以後在針對高級瀏覽器進行局部調整。換句話說,就是以最低要求,實現最基礎功能爲基本,向上兼容。 如下寫法就是漸近加強。

.transition {
    -webkit-transition: all .5s;
    -moz-transition: all .5s;
    -o-transition: all .5s;
    transition: all .5s; // 高瀏覽器支持
}
複製代碼

優雅降級

優雅降級則是首先在高級別瀏覽器開發,構建整個頁面,完善功能。而後針對部分低版本瀏覽器在作兼容處理。總之即是,高版本爲基準,向下兼容。

如下寫法就是漸近加強。

.transition {
    transition: all .5s;
    -o-transition: all .5s;
    -moz-transition: all .5s;
    -webkit-transition: all .5s;
}
複製代碼

漸進加強和優雅降級的區別

  • 一、兼容不一樣 一個是向上兼容,一個是向下兼容。

  • 二、成本上分析

    若是你採用漸進加強的開發流程,先作一個基本功能版,而後針對各個瀏覽器進行漸進增長,增長各類功能。相對於優雅降級來講,開發週期長,初期投入資金大。

  • 三、技術上分析

    1. 好久好久之前:瀏覽器即不寵幸前綴CSS3也不寵幸純情CSS3(border-radius);
    2. 不久以前:瀏覽器只寵幸前綴CSS3,不寵幸純情的CSS3;
    3. 如今:瀏覽器不只寵幸前綴CSS3屬性,還寵幸純情CSS3屬性;
    4. 等到之後:前綴CSS3就回鄉下帶孩子了,瀏覽器只寵幸純情CSS3屬性。

看上圖咱們可以區分帶前綴屬性和不帶前綴屬性在瀏覽器的最終處理結果。咱們如今正處於 瀏覽器不只寵幸前綴CSS3屬性,還寵幸純情CSS3屬性;的階段。

當下,webkit核心的瀏覽器不只支持border-radius屬性,也支持-webkit-border-radius屬性,這自己沒什麼,只是效果會出現不一樣。

.content{
    width: 200px;
    height: 80px;
    background: #444;
    border-radius: 30px 10px;
    -webkit-border-radius: 30px 10px;
    margin-bottom: 40px;
  }

  .content1{
    width: 200px;
    height: 80px;
    background: #444;
    -webkit-border-radius: 30px 10px;
    border-radius: 30px 10px;
  }

  <div class="content"></div>
  <div class="content1"></div>

複製代碼

按理說這兩種寫法效果應該是同樣的,可是咱們如今瀏覽器停留在操蛋的第三階段,也就是如今,既支持前綴寫法,又支持正常寫法,這樣就要出問題了。

最終效果上圖。咱們想要實現的效果是下面的,但是上面是什麼鬼?

當屬性超過一個參數值的時候,不一樣屬性產生的做用是不同的!

能夠看到,採用優雅降級的寫法,若是一個瀏覽器同時支持前綴寫法和正常寫法,後面的舊版瀏覽器樣式就覆蓋了新版樣式,出現一些奇怪的問題 ,可是用漸進加強的寫法就不存在這個問題。

建議: 在書寫css的時候採用漸近加強的寫法。 咱們網頁項目的開發最好採用優雅降級的方式開發,這樣效率更高。固然也不是絕對的,要看你用戶使用的瀏覽器的佔比了。

11、問題十一: CSS 中可讓文字垂直和水平方向上重疊的兩個屬性是什麼?

CSS 中可讓文字垂直和水平方向上重疊的兩個屬性是什麼?
複製代碼

回答區:

垂直方向: line-height
水平方向: letter-spacing
複製代碼
<style type="text/css"> h1 {letter-spacing: -4px} h4 {line-height: 0px} </style>

<body>
<h1>This is header 1</h1>
<h4>This is header 4</h4>
</body>

複製代碼

效果以下圖:

<style type="text/css"> p.small {line-height: 100%} p.small1 {line-height: 90%} </style>


<body>
<h1>正常高度</h1>
<p>
這是擁有標準行高的段落。
在大多數瀏覽器中默認行高大約是 110% 到 120%。
這是擁有標準行高的段落。
這是擁有標準行高的段落。
這是擁有標準行高的段落。
這是擁有標準行高的段落。
這是擁有標準行高的段落。
</p>
<h1>100%</h1>
<p class="small">
這個段落擁有更小的行高。
這個段落擁有更小的行高。
這個段落擁有更小的行高。
這個段落擁有更小的行高。
這個段落擁有更小的行高。
這個段落擁有更小的行高。
這個段落擁有更小的行高。
</p>

<h1>90%</h1>
<p class="small1">
這個段落擁有更小的行高。
這個段落擁有更小的行高。
這個段落擁有更小的行高。
這個段落擁有更小的行高。
這個段落擁有更小的行高。
這個段落擁有更小的行高。
這個段落擁有更小的行高。
</p>
</body>
複製代碼

效果以下圖:

12、問題十二: 如何解決使用inline-block引發的空白間隙的問題

如何解決使用inline-block引發的空白間隙的問題
複製代碼

回答區:

  • 方法一: 既然空白間隙是因爲換行或空格引發,那麼消除換行符不就解決了

  • 方法二: 設置父元素的font-size爲0,在子元素從新設置字體大小

    ie8,firefox,chrome 和 opera 瀏覽器下已經沒有問題了,可是在 低版本safari 瀏覽器下仍是有問題。

  • 方法三: 利用負margin-left(不推薦,具體負margin多少取決於字體的大小)

  • 方法四: letter-spacing(字符邊距):負值 or word-spacing(單詞邊距) 負值,負值大小仍是取決於字體

  • 方法五: 【推薦】給父元素 設置font-size:0 ;letter-spacing:-3px ,子元素從新設置font-size。

十3、問題十三: 使用css建立一個三角形

使用css建立一個三角形
複製代碼

回答區:

如下是原理圖:

.kailong{
      width: 0px;
      height: 0px;
      border-right: 50px solid transparent;
      border-left: 50px solid transparent;
      border-bottom: 50px solid red;
}
複製代碼

  • 實心三角
#demo {
      width: 100px;
      height: 100px;
      background-color: #333;
      position: relative;
    }

    #demo:after {
      border: solid transparent;
      border-left-color: #333;
      border-width: 10px;
      width: 0;
      content: " ";
      position: absolute;
      left: 100%;
      top: 10%;
    }
複製代碼

  • 空心三角
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style> #demo { width: 100px; height: 100px; border: 1px solid #333; position: relative; } /*小三角*/ #demo:after { border: solid transparent; border-left-color: #fff; border-width: 10px; content: " "; position: absolute; left: 100%; top: 20px; /*20px*/ } /*大三角*/ #demo:before { border: solid transparent; border-left-color: #000; border-width: 12px; /*10px+2px*/ content: " "; position: absolute; left: 100%; top: 18px; /*20px-2px*/ } /* 小三角遮不住大三角 */ </style>
</head>

<body>
  <div id="demo"></div>
</body>

</html>
複製代碼

十4、問題十四: 有一個長度爲 100 的數組,請求出該數組的前 10 個元素之和。

有一個長度爲 100 的數組,請求出該數組的前 10 個元素之和。
複製代碼

回答區:

var arr = [1,2,3,4,5,6,7,8,9,10,23,34,45,56,45,34];

arr.slice(0, 10).reduce((a,b) => {
    return a+b
})

// 55
複製代碼

十5、問題十五: 寫一個程序打印 1 到 100 這些數字,遇到數字爲 3 的倍數,打印 「A」 替代該數字;遇到 5 的倍數,用 「B」 代替;遇到便是 3 的倍數又是 5 的倍數,打印 「AB」。

寫一個程序打印 1 到 100 這些數字,遇到數字爲 3 的倍數,打印 「A」 替代該數字;遇到 5 的倍數,用 「B」 代替;遇到便是 3 的倍數又是 5 的倍數,打印 「AB」。
複製代碼

回答區:

for(var i = 1 ; i <= 100; i++) {
    if(i%3 == 0 && i % 5 == 0) {
      console.log("AB")
    } else if (i % 3 == 0){
      console.log("A")
    } else if (i % 5 == 0) {
      console.log("B")
    } 
  }
複製代碼

要點: 先排出要求最嚴格的

十6、問題十六: 引發內存泄漏的狀況有?

引發內存泄漏的狀況有?
複製代碼
  • 垃圾回收機

JS哪些操做會形成內存泄露

回答區:

十7、問題十七: 寫 React / Vue 項目時爲何要在組件中寫 key,其做用是什麼

寫 React / Vue 項目時爲何要在組件中寫 key,其做用是什麼
複製代碼

回答區

要理解key的用法咱們就不得不理解一下diff算法了,咱們知道,vue和react都實現了一套虛擬DOM,使咱們能夠不直接操做DOM元素,只操做數據即可以從新渲染頁面。而隱藏在背後的原理即是其高效的Diff算法。

diff算法只比較同層的節點,若是節點類型不一樣,直接幹掉前面的節點,再建立並插入新的節點,不會再比較這個節點之後的子節點了。若是節點類型相同,則會從新設置該節點的屬性,從而實現節點的更新。

好比咱們有以下狀況:

咱們但願能夠在B和C之間加一個F,Diff算法默認執行起來是這樣的:

在沒有key的狀況下,會原地複用,修改節點信息,最後還會新增一個節點。

即把C更新成F,D更新成C,E更新成D,最後再插入E,這樣效率較低。

diff算法就是利用key值去肯定咱們的節點,從而可以更快的找到正確的位置區插入新的節點。

diff算法用於比對新舊虛擬DOM對象,當咱們在比較頭尾節點無果後,會根據新節點的key去對比舊節點數組中的key,從而找到相應舊節點。若是沒找到就認爲是一個新增節點。若是找到了就去比對,而後更新節點。

總之: key是給每個vnode的惟一id,能夠依靠key,更準確, 更快的拿到oldVnode中對應的vnode節點。從而是的diff算法可以更快更高效的更新咱們的虛擬dom。 參考回答

十8、問題十八: 什麼是防抖和節流?有什麼區別?

什麼是防抖和節流?有什麼區別?
複製代碼

回答區

防抖和節流都是用來限制咱們事件觸發的頻率,從而提升瀏覽器的性能。

防抖: 觸發事件n秒以後纔會執行,可是若是在這個時間到來以前,咱們又觸發了事件,則清除掉上一次的定時器,而後重新定時。最終實現的效果就是咱們連續觸發事件時候,只會在最後觸發咱們的事件函數。

節流: 在防抖的基礎上進行改進,容許咱們在連續觸發的過程當中,在固定時間段內能夠觸發事件函數,因此其實是在稀釋函數的執行頻率。

十9、問題十九: 介紹下深度優先遍歷和廣度優先遍歷,如何實現?

介紹下深度優先遍歷和廣度優先遍歷,如何實現?有什麼區別?
複製代碼

回答區

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title></title>
</head>

<body>
  <div id="parent">
    <div class="child-1">
      <div class="child-1-1">
        <div class="child-1-1-1">
          child-1-1-1
        </div>
      </div>
    </div>
    <div class="child-2">
      child2
    </div>
    <div class="child-3">
      <div class="child-3-1">
        child-3-1
      </div>
      <div class="child-3-2">
        child-3-2
      </div>
    </div>
  </div>

  <script>
    var a = 10;


    // 深度優先搜索
    let deepTraversal2 = (node) => {
      let nodes = []
      if (node !== null) {
        nodes.push(node)
        let children = node.children
        for (let i = 0; i < children.length; i++) {
          nodes = nodes.concat(deepTraversal2(children[i]))
        }
      }
      return nodes
    }


    // 測試深度
    // var dom = document.getElementById('parent');
    // console.log(deepTraversal2(dom));



    // 廣度優先遍歷
    function widthTraversal2 (node) {
      let nodes = [];
      let stack = [];

      if(node != null) {
        stack.push(node);

        while(stack.length) {
          let item = stack.shift();
          let children = item.children
          nodes.push(item)
          for(var i = 0; i < children.length; i++) {
            stack.push(children[i])
          }
        }
      }
      return nodes;
    }

    var dom = document.getElementById('parent');
    console.log(widthTraversal2(dom));
  </script>
</body>

</html>
複製代碼

二10、問題二十: 你瞭解EventLoop嗎?

你瞭解EventLoop嗎?
複製代碼

回答區

衆所周知 JS 是門非阻塞單線程語言,由於在最初 JS 就是爲了和瀏覽器交互而誕生的。若是 JS 是門多線程的語言話,咱們在多個線程中處理 DOM 就可能會發生問題(一個線程中新加節點,另外一個線程中刪除節點),固然能夠引入讀寫鎖解決這個問題。

JS 在執行的過程當中會產生執行環境,這些執行環境會被順序的加入到執行棧中。若是遇到異步的代碼,會被掛起並加入到 Task(有多種 task) 隊列中。一旦執行棧爲空,Event Loop 就會從 Task 隊列中拿出須要執行的代碼並放入執行棧中執行,因此本質上來講 JS 中的異步仍是同步行爲。

console.log('script start')

setTimeout(function() {
  console.log('setTimeout')
}, 0)

console.log('script end')
複製代碼

以上代碼雖然 setTimeout 延時爲 0,其實仍是異步。這是由於 HTML5 標準規定這個函數第二個參數不得小於 4 毫秒,不足會自動增長。因此 setTimeout 仍是會在 script end 以後打印。

不一樣的任務源會被分配到不一樣的 Task 隊列中,任務源能夠分爲 微任務(microtask) 和 宏任務(macrotask)。在 ES6 規範中,microtask 稱爲 jobsmacrotask稱爲 task

微任務包括 process.nextTickpromise

宏任務包括scriptsetTimeoutsetIntervalsetImmediateI/OUI rendering

console.log('script start');

setTimeout(function() {
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
  console.log('Promise')
  resolve()
})
  .then(function() {
    console.log('promise1')
  })
  .then(function() {
    console.log('promise2')
  })

console.log('script end')
// script start => Promise => script end => promise1 => promise2 => setTimeout
複製代碼

首先執行同步代碼,遇到promise的話,會首先執行內部的同步代碼,而後再繼續執行同步代碼。途中遇到的settimeout和promise放入不一樣的任務隊列中,這時候因爲執行棧已經爲空,因此須要開始執行異步任務,首先查看微任務隊列,發現又promise已經能夠了,那麼就執行promise的then,把全部能夠執行的微任務都執行完成以後纔會去宏任務隊列找,發現又setTimeout能夠執行了,就執行內部的代碼。

因此正確的一次 Event loop 順序是這樣的

執行同步代碼,這屬於宏任務
執行棧爲空,查詢是否有微任務須要執行
執行全部微任務
必要的話渲染 UI
而後開始下一輪 Event loop,執行宏任務中的異步代碼
複製代碼

總結js異步執行機制

JS 主線程擁有一個 執行棧(同步任務) 和 一個 任務隊列(microtasks queue),主線程會依次執行代碼,

1 首先順序執行同步代碼

2 當遇到task任務(異步)時,會先執行必定的同步任務,而後讓主線程繼續執行下去,
而真正的task任務將交給瀏覽器內核 執行,瀏覽器內核執行結束後,會將該任務事先定義好的回調函數加入相應的任務隊列中,
同時設置事件(當回調能夠執行的時候通知)

3 當JS主線程清空執行棧以後,會按先入先出的順序讀取microtasks queue中的回調函數,並將該函數入棧,
繼續運行執行棧,直到清空執行棧,再去讀取任務隊列。

4 當microtasks queue中的任務執行完成後,會提取 macrotask queue 的一個任務加入 microtask queue, 
接着繼續執行microtask queue,依次執行下去直至全部任務執行結束。


這就是 JS的異步執行機制
複製代碼

二11、問題二十一: async await、Promise、setTimeout

async / await、Promise、setTimeout
複製代碼

回答區

async function async1(){
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}
async function async2(){
  console.log('async2')
}
console.log('script start')
setTimeout(function(){
  console.log('setTimeout') 
},0)  
async1();
new Promise(function(resolve){
  console.log('promise1')
  resolve();
}).then(function(){
  console.log('promise2')
})
console.log('script end')

/* script start async1 start async2 promise1 script end async1 end promise2 setTimeout */
複製代碼

在解這個以前咱們有幾個知識點要清楚:

一、js是單線程的。
二、promise被定義後是當即執行的,可是他的resolve是異步的。
三、promise的異步優先級高於setTimeout。由於promise是微任務
四、async會返回一個promise對象,await關鍵字會讓出線程。
複製代碼

接下在咱們分析下代碼執行流程:

1、執行console.log('script start'),輸出script start;
2、執行setTimeout,是一個異步動做,放入異步隊列中;
3、執行async1(),輸出async1 start,繼續向下執行;
4、執行async2(),輸出async2,並返回了一個promise對象,await讓出了線程,
把返回的promise加入了異步隊列,因此async1()下面的代碼也要等待上面完成後繼續執行;
5、執行 new Promise,輸出promise1,而後將resolve放入異步隊列;
6、執行console.log('script end'),輸出script end;
7、到此同步的代碼就都執行完成了,而後去異步隊列裏去獲取任務,如今隊列中有一個promise(async2返回的),resolve(new Promise的),和setTimeout,
由於promise屬於微任務,先取出promise執行,默認返回resolve,再次加入了異步隊列,如今就隊列就變成了 `resolve(async2返回的promise返回的)``resolve(new Promise的)``setTimeout`8、接下來執行resolve(async2返回的promise返回的),輸出了`async1 end`9、而後執行`resolve(new Promise的)`,輸出了`promise2`10、最後執行`setTimeout`,輸出了`settimeout`複製代碼

題目的本質,就是考察setTimeout、promise、async await的實現及執行順序,以及JS的事件循環的相關問題。

一篇講解的很是好的事件循環的文章

二12、問題二十二: Async/Await 如何經過同步的方式實現異步

Async/Await 如何經過同步的方式實現異步
複製代碼

回答區

深刻理解 async / await

二十3、問題二十三: 算法手寫題

已知以下數組:
var arr = [ 
    [1, 2, 2], 
    [3, 4, 5, 5], 
    [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 
    10
];
編寫一個程序將數組扁平化去併除其中重複部分數據,最終獲得一個升序且不重複的數組
複製代碼

回答區

最後就剩排序了。

二十4、問題二十四: JS異步解決方案的發展歷程以及優缺點。

JS異步解決方案的發展歷程以及優缺點。
複製代碼

回答區

1. 回調函數(callback)

setTimeout(() => {
    // callback 函數體
}, 1000)
複製代碼

缺點:回調地獄,不能用 try catch 捕獲錯誤,不能 return

回調地獄的根本問題在於:

缺少順序性: 回調地獄致使的調試困難,和大腦的思惟方式不符
嵌套函數存在耦合性,一旦有所改動,就會牽一髮而動全身,即(控制反轉)
嵌套函數過多的多話,很難處理錯誤
複製代碼
ajax('XXX1', () => {
    // callback 函數體
    ajax('XXX2', () => {
        // callback 函數體
        ajax('XXX3', () => {
            // callback 函數體
        })
    })
})
複製代碼
  • 優勢:解決了同步的問題(只要有一個任務耗時很長,後面的任務都必須排隊等着,會拖延整個程序的執行。)

2. Promise

Promise就是爲了解決callback的問題而產生的。

Promise 實現了鏈式調用,也就是說每次 then 後返回的都是一個全新 Promise,若是咱們在 then 中 return ,return 的結果會被 Promise.resolve() 包裝

優勢:解決了回調地獄的問題

ajax('XXX1')
  .then(res => {
      // 操做邏輯
      return ajax('XXX2')
  }).then(res => {
      // 操做邏輯
      return ajax('XXX3')
  }).then(res => {
      // 操做邏輯
  })
複製代碼

缺點:沒法取消 Promise ,錯誤須要經過回調函數來捕獲

3. Async/await

async、await 是異步的終極解決方案

優勢是:代碼清晰,不用像 Promise 寫一大堆 then 鏈,處理了回調地獄的問題

缺點:await 將異步代碼改形成同步代碼,若是多個異步操做沒有依賴性而使用 await 會致使性能上的下降。

async function test() {
  // 如下代碼沒有依賴性的話,徹底可使用 Promise.all 的方式
  // 若是有依賴性的話,其實就是解決回調地獄的例子了
  await fetch('XXX1')
  await fetch('XXX2')
  await fetch('XXX3')
}
複製代碼

async 函數返回的 Promise 對象,必須等到內部全部的 await 命令的 Promise 對象執行完,纔會發生狀態改變

二十5、問題二十五: 面試可能會問到的一些網絡請求問題

面試可能會問到的一些網絡請求問題
複製代碼

一、關於網絡請求的疑問

  • Ajax的出現解決了什麼問題
  • 原生Ajax如何使用
  • jQuery的網絡請求方式

二、Ajax的出現解決了什麼問題

在Ajax出現以前,web程序是這樣工做的:

這種交互的的缺陷是顯而易見的,任何和服務器的交互都須要刷新頁面,用戶體驗很是差,Ajax的出現解決了這個問題。

使用Ajax,網頁應用可以快速地將增量更新呈如今用戶界面上,而不須要重載(刷新)整個頁面。

ajax能夠實現,咱們發送請求,獲取相應的數據,而後經過js去動態渲染頁面,而不須要服務器拼接HTML,頁面的刷新也只是局部的刷新,再也不是整個頁面的刷新了。

三、原生Ajax的用法

手寫一個原生的ajax

var xhr = new XMLHttpRequest();
    xhr.open('post', 'http://', true);
    xhr.onreadystatechange = function() {
      if(xhr.readyState == 4) {
        if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
          console.log(xhr.responseText);
        }
       }
    }

    let postData = {"name1":"value1","name2":"value2"};
    postData = (function(value) {
      var dataString = "";
      for(var key in value){
           dataString += key+"="+value[key]+"&";
      };
      return dataString;
    })(postData);

    xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    // 異常處理
    xhr.onerror = function() {
       console.log('Network request failed')
    }
    // 跨域攜帶cookie
    xhr.withCredentials = true;
    xhr.send(postData);
複製代碼

  • readyState

用來標識當前XMLHttpRequest對象所處的狀態,XMLHttpRequest對象老是位於下列狀態中的一個:

  • status

表示http請求的狀態, 初始值爲0。若是服務器沒有顯式地指定狀態碼, 那麼status將被設置爲默認值, 即200。

  • responseType

表示響應的數據類型,並容許咱們手動設置,若是爲空,默認爲text類型,能夠有下面的取值:

  • onreadystatechange

當readyState屬性發生變化時,callback會被觸發。

  • onprogress
xhr.onprogress = function(event){
  console.log(event.loaded / event.total);
}
複製代碼

回調函數能夠獲取資源總大小total,已經加載的資源大小loaded,用這兩個值能夠計算加載進度。

  • onerror

當ajax資源加載失敗時會觸發callback。

四、jQuery對Ajax的封裝

在很長一段時間裏,人們使用jQuery提供的ajax封裝進行網絡請求,包括.ajax、.get、$.post等,這幾個方法放到如今,我依然以爲很實用。

$.ajax({
    dataType: 'json', // 設置返回值類型
    contentType: 'application/json', // 設置參數類型
    headers: {'Content-Type','application/json'},// 設置請求頭
    xhrFields: { withCredentials: true }, // 跨域攜帶cookie
    data: JSON.stringify({a: [{b:1, a:1}]}), // 傳遞參數
    error:function(xhr,status){  // 錯誤處理
       console.log(xhr,status);
    },
    success: function (data,status) {  // 獲取結果
       console.log(data,status);
    }
})
複製代碼

二十6、問題二十六: 介紹模塊化發展歷程

跨域方案
複製代碼

模塊化主要是用來抽離公共代碼,隔離做用域,避免變量衝突等。

【1】IIFE 使用自執行函數來編寫模塊化

(function(){
  return {
	data:[]
  }
})()
複製代碼

特色: 在一個單獨的函數做用域中執行代碼,避免變量衝突。

【2】AMD 使用requireJS 來編寫模塊化

define('./index.js',function(code){
	// code 就是index.js 返回的內容
})
複製代碼

特色: 依賴必須提早聲明好。

【3】CMD 使用seaJS 來編寫模塊化

define(function(require, exports, module) {  
  var indexCode = require('./index.js');
});
複製代碼

特色: 支持動態引入依賴文件。

【4】CommonJS nodejs 中自帶的模塊化

var fs = require('fs');
複製代碼

【5】ES Modules ES6 引入的模塊化,支持import 來引入另外一個 js 。

import a from 'a';
複製代碼

二十7、問題二十七: H5新增了哪些全局屬性

全局屬性
複製代碼
html5新增:
一、contenteditable		元素內容可編輯
二、contextmenu			元素上下文菜單,目前只有火狐瀏覽器支持
三、data-*				用於存儲頁面私有數據
四、draggable			是否可拖動
五、spellcheck			屬性規定是否對元素進行拼寫和語法檢查。
複製代碼

二十8、問題二十八: 你知道Shadow DOM(影子DOM)嗎?

你知道Shadow DOM(影子DOM)嗎?
複製代碼
Shadow DOM 是瀏覽器的一種行爲,可以自動添加子元素,好比aduio元素,當咱們設置了controls的時候,在網頁中會提供進度條、音量控制等。
複製代碼

二十9、問題二十九: 你知道href和src的區別嗎?

你知道href和src的區別嗎?
複製代碼
咱們能夠這麼理解:
href意思是打通當前文檔和定義的資源的鏈接,即引用
src意思是將目標資源嵌入到頁面中,即引入
複製代碼

三10、問題三十: CSS樣式的加載

CSS樣式的加載方式:
複製代碼

css應用到頁面中的三種引用方式:內聯、內嵌、外部樣式。

方式 特殊性 http請求 重用範圍 文檔大小 僞類與僞元素
內聯 最高 不可 增長 不可定義
內嵌 與外部相同 當前文檔 增長 能夠定義
外部 與內嵌相同 整個項目 保持 能夠定義

三11、問題三十一: JS腳本加載的幾種方式

JS腳本加載的幾種方式
複製代碼

三種方式:內嵌、外鏈、元素屬性

  • 內嵌

若是將內斂腳本放置在樣式表以後,那麼會致使延遲下載。由於js腳本中可能存在對樣式的修改,因此會等到樣式表加載完畢以後,再繼續執行腳本,而後纔會繼續解析後面的。因此這種方式並很差。因此通常都會吧script放在末尾附近。

  • 外鏈

三種狀況:

<script>			
當解析文檔過程當中遇到了script標籤,會中斷解析,下載js腳本並執行,
執行完畢以後在繼續文檔的解析
複製代碼
<script defer>		
當解析文檔過程當中遇到了script標籤,並不會中斷文檔的解析,而是js腳本和文檔解析一同進行,
等到文檔解析完畢以後,再去執行剛纔加載好的腳本。
複製代碼
<script async>		
當解析文檔過程當中遇到了script標籤,並不會中斷文檔的解析,
而是js腳本和文檔解析一同進行,當腳本加載完畢以後,就立馬執行,
這時候文檔的解析和腳本的執行是同時進行的。js腳本執行完畢以後,
文檔解析繼續進行。
複製代碼

使用<script defer>跟把咱們的script標籤放在body底部效果相似。

  • 元素屬性
一、事件屬性
<input type="button" onclick="print()" />
這種作法存在必定的侷限性,不能寫較爲復炸的函數申明和建立對象,
代碼量大的時候,可讀性就會大大下降。

二、特殊協議 「javascript:」僞協議只能做用於某幾個特定的屬性。
<a href="javascript:void(0);"> 能夠阻止a標籤的默認行爲(重定向)
void是運算符,會忽略計算結果返回undefined

「javascript:」僞協議的另外一個用途就是製做瀏覽器的書籤
複製代碼

三12、問題三十二: 你瞭解Meta標籤嗎?

你瞭解Meta標籤嗎?
複製代碼

meta標籤是空標籤,有四個屬性: name 、content、 charset、 http-equiv

  • charset 指定字符集
<meta charset="UTF_8" />
複製代碼
  • name + content 定義頁面的一些基本元數據信息,方便計算機的掃描閱讀
<meta name="application-name" content="web應用名稱" />
<meta name="author" content="web應用做者" />
<meta name="description" content="文檔描述" />
<meta name="generator" content="生成頁面的工具名" />
<meta name="keywords" content="關鍵字,每一個關鍵字能夠用逗號分隔" />
<meta name="robots" content="規定搜索引擎如何操做本文檔,如: noindex(禁止索引文檔),noarchive(禁止緩衝文檔內容)等" />
<meta name="viewport" content="定義視口" />
複製代碼

咱們在作移動端開發的時候,須要作viewport適配。

<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1; minimum-scale=1;user-scalable=no;">
複製代碼
  • http-equiv
一、指定默認樣式表
<meta http-equiv="default-style" content="red" />
上面 content 屬性的值必須匹配同一文檔中的一個 link 元素上的 title 屬性的值,或者必須匹配同一文檔中的一個 style 元素上的 title 屬性的值。

二、refresh
定義文檔自動刷新的時間間隔。
實例:
<meta http-equiv="refresh" content="300" />
<meta http-equiv="Refresh" content="2;URL=http://www.net.cn/" /> 
註釋:值 "refresh" 應該慎重使用,由於它會使得頁面不受用戶控制。在 W3C's Web 內容可訪問性指南 中使用 "refresh" 會到致使失敗。 複製代碼

總之,meta標籤能夠作如下事情:

一、聲明文檔的字符編碼
二、定義網頁的一些文檔描述信息,方便計算機的閱讀(搜索引擎)
三、用於適配移動端
四、調整文檔的首選樣式表、定時刷新等
複製代碼

三十3、問題三十三: a標籤的target屬性你瞭解嗎?

你瞭解Meta標籤嗎?
複製代碼
_self		當前窗口
_blank		新窗口
_parent		父窗口,若是沒有則是與_self效果相同
_top		頂層窗口,若是沒有則是與_self效果相同
複製代碼

經常使用的是_self和_target。

a標籤還能夠

<a href="tel:10086">撥打電話</a>
<a href="sms:10086?body=test">發送短信</a>
<a href="mailto:1392372716@qq.com?cc=jain@pp.com">發送郵件</a>
複製代碼

三十4、問題三十四: disabled和readonly的區別?

disabled和readonly的區別?
複製代碼
他們的寫法同樣:
disabled寫法:<input type="text" name="aaa" value="xxx" disabled="true"/>

readonly寫法:<input type="text" name="bbb" value="xxx" readonly="true"/>

相同點: 都能把咱們的文本表單設置爲只讀,不可編輯狀態。
複製代碼

不一樣點:

一、樣子效果不一樣: disabled能把咱們的表單變成不可編輯狀態的同時還能使得咱們的文本框變灰,
可是readonly不會。

二、readonly修飾下的表單元素,還能夠經過js去獲取文本內容,
可是disabled修飾下的文本不能經過js獲取文本內容。

三、readonly沒有disabled適用範圍逛,disabled還能夠適用於checkbox、
select等可是readonly沒效果。
複製代碼

三十5、問題三十五: 爲何iframe用的愈來愈少了?

爲何iframe用的愈來愈少了?
複製代碼

經過Iframe可以在一個文檔裏面嵌入另外一個文檔。而且這兩個文檔能夠作到互不影響,JS和CSS都不會影響。Iframe存在着一些不足,可是在一些領域他在以前仍是至關有用的。最近的HTML5已正在逐步取代Iframe。

iframe常被用於複用部分界面,比較早期的網站使用 iframe,主要是用於導航欄(navigator)。爲何?

由於一個網站不少頁面的導航欄部分是相同的,在避免切換頁面的時候重複下載,將導航欄和正文分開在 iframe 中,是一個方便的作法。同時帶來的不利是,默認狀況下,使用了 iframe 的網站的 URL 不會隨着頁面的變化而變化。這就意味着一旦刷新,網站可能又回到首頁。

那麼如今何時會用到 iframe 呢?

由於 iframe 的頁面和父頁面(parent)是分開的,因此它意味着,這是一個獨立的區域,不受 parent 的 CSS 或者全局的 JavaScript 的影響。典型的,好比所見即所得的網頁編輯器(WYSIWYG Online HTML Editor),由於它們須要 reset 本身的 CSS 到本身的標準,而不被 parent CSS 的 影響。

使用 iframe 是否是一個好的用法(good practice),不能一律而論,可是能夠確定是,如今的大部分網站避免採用這種方式的。

由於它的這兩個缺點,因此致使了它不能很好的使用下去:

(1)瀏覽器對同一域名下的併發請求作了限制,好比谷歌瀏覽器最大併發請求最大數爲6,iframe中的文檔與父文檔共享連接,也就是說,父文檔若是併發請求了5個資源,那麼子文檔中此時只能併發執行1個了,剩下的只能在下一次請求。iframe和主頁面共享鏈接池,而瀏覽器對相同域的鏈接有限制,因此會影響頁面的並行加載。 使用iframe以前須要考慮這兩個缺點。若是須要使用iframe,最好是經過javascript 動態給iframe添加src屬性值,這樣能夠繞開以上兩個問題。

(2)子文檔的加載會阻塞父文檔的加載,load事件的觸發時間差很少會增長一倍。

三十6、問題三十六: 爲何再也不使用flash了?

爲何再也不使用flash了?
複製代碼

flash不但存在必定的安全隱患,並且沒法很好的支持js和css,並且開發人員還需學習actionScript語法,數據處理並不方便,H5則是直接使用多媒體元素播放視頻或者音頻,不在依賴Flash。多媒體元素不只能夠在瀏覽器中實現很好的實現,並且支持移動端設別,易於定製樣式效果,可以使用Js和css操做咱們的多媒體元素,可以輕鬆實現響應式設計。

三十7、問題三十七: 圖像

圖像
複製代碼
  • 位圖圖像

由像素矩陣組成,無數個像素點組成,位圖圖像的特色是色彩豐富,能夠逼真的再現,所以經常使用於數碼照相,頁面效果等。canvas是基於位圖的圖像。

  • 矢量圖像

由點和線組成,色彩比較簡單,沒法逼真呈現圖像,可是當你放大的時候,不會丟失細節。SVG是基於矢量的圖形。

三十8、問題三十八: 咱們前端須要從哪些方面注意SEO?

咱們前端須要從哪些方面注意SEO?
複製代碼

SEO是(搜索引擎優化),搜索引擎會爬咱們的HTML頁面,而後從中提取有用的信息進行統計整理,爲了提升網頁的排名度,不少大公司都強烈要求咱們的HTML要符合必定的規則書寫。咱們大體須要注意如下幾點:

一、經過meta標籤來爲咱們的網頁添加一些元數據信息,好比關鍵字、描述、做者等,能夠方便咱們爬中的信息採集
二、儘可能讓每一個頁面的title都有所不一樣。
三、多使用語義化標籤,不要濫用div等。儘可能讓咱們的網頁有提綱。
四、爬蟲是不會去查看js文件的,因此不要把想給搜索引擎看的東西放入js中。
五、不要過多使用iframe,由於爬蟲不會去找iframe。
六、圖片儘可能寫上alt。
複製代碼
相關文章
相關標籤/搜索