聲明:慕課網《前端JavaScript面試技巧》的筆記,僅用於查閱複習,不得用於商業用途。javascript
基礎知識css
JS APIhtml
開發環境前端
運行環境java
關於面試node
關於基礎jquery
先從幾道面試題提及git
typeof
能獲得的哪些類型?===
,什麼時候使用 ==
?window.onload
和 DOMContentLoaded
的區別?<a>
標籤,點擊的時候彈出對應的序號require.js
的基本功能思考github
上一節思考問題的結論面試
題目考察的知識點
typeof
能獲得的哪些類型? 考點:JS 變量類型===
,什麼時候使用 ==
? 考點:強制類型轉換window.onload
和 DOMContentLoaded
的區別? 考點:瀏覽器渲染過程<a>
標籤,點擊的時候彈出來對應的序號 考點:做用域require.js
的基本功能 考點:JS 模塊化題目
typeof
能獲得的哪些類型?===
,什麼時候使用 ==
?知識點
變量類型
typeof
運算符值類型
let a = 100
let b = a
a = 200
console.log(b) // 100
複製代碼
引用類型
let a = { age: 20 }
let b = a
b.age = 21
console.log(a.age) // 21
複製代碼
typeof
運算符
typeof undefined // "undefined"
typeof 'abc' // "string"
typeof 123 // "number"
typeof true // "boolean"
typeof {} // "object"
typeof [] // "object"
typeof null // "object"
typeof console.log // "function"
複製代碼
變量計算 - 強制類型轉換
字符串拼接
let a = 100 + 10 // 110
let b = 100 + '10' // "10010"
複製代碼
==
運算符
100 == '100' // true
0 == '' // true
null = undefined // true
複製代碼
if
語句
let a = true
if (a) {}
let b = 100
if (b) {}
let c = ''
if (c) {}
複製代碼
邏輯運算符
console.log(10 && 0) // 0
console.log('' || 'abc') // "abc"
console.log(!window.abc) // true
// 判斷一個變量會被當作 true 仍是 false
let a = 100
console.log(!!a) // true
複製代碼
解答
JS 中使用 typeof
能獲得的哪些類型?
答:undefined
、string
、number
、boolean
、object
、function
、symbol
let sym = Symbol('commet')
console.log(typeof sym) // "symbol"
複製代碼
什麼時候使用 ===
,什麼時候使用 ==
?
答:判斷對象的某個屬性是否存在或爲 null
時可使用 ==
,由於 jQuery 源碼這麼使用
if (obj.a == null) {
// 這裏至關於 obj.a === null || obj.a === undefined 的簡寫形式
// 這是 jQuery 源碼中推薦的寫法
}
複製代碼
JS 中有哪些內置函數?
答:數據封裝類對象,包括 String
、Number
、Boolean
、Object
、Array
、Function
、Date
、RegExp
、Error
JS 變量按照存儲方式區分爲哪些類型,並描述其特色?
答:值類型和引用類型,一個存儲值,一個存儲引用地址,引用類型能夠無限拓展屬性
如何理解 JSON ?
答:JSON 只不過是一個 JS 對象而已,也是一種數據格式
JSON.stringify({ a: 10, b: 20 })
JSON.parse('{"a":10,"b":20}')
複製代碼
if
語句中條件爲 false
的狀況有哪些?
答:''
、0
、NaN
、false
、null
、undefined
JS 中有哪些內置對象?
答:JSON
、Math
let s = Symbol()
typeof s // "symbol"
let s1 = Symbol()
console.log(s === s1) // false
let s2 = Symbol('s2s2')
console.log(s2) // Symbol(s2s2)
let s3 = s2
console.log(s3 === s2) // true
let sym1 = Symbol('111')
let sym2 = Symbol('222')
let obj = { [sym1]: 'hello world' }
obj[sym2] = 123
console.log(obj) // {Symbol(111): "hello world", Symbol(222): 123}
console.log(obj[sym1]) // "hello world"
console.log(obj[sym2]) // 123
複製代碼
題目
new
一個對象的過程知識點
instanceof
構造函數
function Foo(name, age) {
this.name = name
this.age = age
this.class = 'class__1'
// return this // 默認有這一行
}
let f = new Foo('negrochn', 18)
// let f2 = new Foo('lexiaodai', 17) // 建立多個對象
複製代碼
構造函數 - 擴展
let a = {} // 實際上是 let a = new Object() 的語法糖
let b = [] // 實際上是 let b = new Array() 的語法糖
function Foo() {} // 實際上是 let Foo = new Function()
// 使用 instanceof 判斷一個函數是不是一個變量的構造函數
console.log(b instanceof Array) // true
複製代碼
5 條原型規則
null
之外)__proto__
(隱式原型)屬性,屬性值是一個普通的對象prototype
(顯式原型)屬性,屬性值也是一個普通的對象__proto__
屬性值指向它的構造函數的 prototype
屬性值__proto__
(即它的構造函數的 prototype
)中尋找// 原則 1
let obj = {}
obj.a = 100
let arr = []
arr.a = 100
function fn() {}
fn.a = 100
// 原則 2
console.log(obj.__proto__)
console.log(arr.__proto__)
console.log(fn.__proto__)
// 原則 3
console.log(fn.prototype)
// 原則 4
console.log(obj.__proto__ === Object.prototype) // true
複製代碼
// 構造函數
function Foo(name) {
this.name = name
}
Foo.prototype.alertName = function() {
alert(this.name)
}
// 建立實例
let f = new Foo('negrochn')
f.printName = function() {
console.log(this.name)
}
// 測試
f.printName()
f.alertName() // 原則 5
複製代碼
for (let key in f) {
// 高級瀏覽器已經在 for in 中屏蔽了來自原型的屬性
// 可是這裏建議你們仍是加上這個判斷,保證程序的健壯性
if (f.hasOwnProperty(key)) {
console.log(key)
}
}
複製代碼
// 構造函數
function Foo(name) {
this.name = name
}
Foo.prototype.alertName = function() {
alert(this.name)
}
// 建立實例
let f = new Foo('negrochn')
f.printName = function() {
console.log(this.name)
}
// 測試
f.printName()
f.alertName()
f.toString() // 要去 f.__proto__.__proto__ 中查找
複製代碼
// instanceof 用於判斷引用類型屬於哪一個構造函數的方法
console.log(f instanceof Foo) // true, f 的 __proto__ 一層一層往上,可否對應到 Foo.prototype
console.log(f instanceof Object) // true
複製代碼
如何準確判斷一個變量是數組類型
答:使用 instanceof Array
let arr = []
console.log(arr instanceof Array) // true
console.log(typeof arr) // "object" ,typeof 是沒法判斷是不是數組的
複製代碼
寫一個原型鏈繼承的例子
// 動物
function Animal() {
this.eat = function() {
console.log('animal eat')
}
}
// 狗
function Dog() {
this.bark = function() {
console.log('dog bark')
}
}
Dog.prototype = new Animal()
// 哈士奇
let hashiqi = new Dog()
// 接下來代碼演示時,會推薦更加貼近實戰的原型繼承示例
複製代碼
描述 new
一個對象的過程
答:
this
指向這個新對象this
賦值this
function Foo(name, age) {
this.name = name
this.age = age
this.class = 'class__1'
// return this // 默認有這一行
}
let f = new Foo('negrochn', 18)
複製代碼
zepto 或其餘框架源碼中如何使用原型鏈
答:
寫一個封裝 DOM 查詢的例子
function Elem(id) {
this.elem = document.getElementById(id)
}
Elem.prototype.html = function(val) {
let elem = this.elem
if (val) {
elem.innerHTML = val
return this // 爲了鏈式操做
} else {
return elem.innerHTML
}
}
Elem.prototype.on = function(type, fn) {
let elem = this.elem
elem.addEventListener(type, fn)
return this // 爲了鏈式操做
}
let div1 = new Elem('div1')
div1.html('<p>Hello World</p>').on('click', function() {
alert('clicked')
})
複製代碼
無
題目
this
幾種不一樣的使用場景<a>
標籤,點擊的時候彈出對應的序號知識點
this
執行上下文
<script>
或者一個函數this
、arguments
拿出來console.log(a) // undefined
var a = 100
fn('negrochn') // "negrochn" 20
function fn(name) {
age = 20
console.log(name, age)
var age
}
複製代碼
無
this
,要在執行時才能確認值,定義時沒法確認
call
、apply
、bind
var a = {
name: 'A',
fn: function() {
console.log(this.name)
}
}
a.fn() // this === a
a.fn.call({ name: 'B' }) // this 做爲 { name: 'B' }
var fn1 = a.fn
fn1() // this === window
複製代碼
// 做爲構造函數執行
function Foo(name) {
this.name = name
}
let f = new Foo('negrochn')
f.name // "negrochn"
複製代碼
// 做爲對象屬性執行
let obj = {
name: 'A',
printName: function() {
console.log(this.name)
}
}
obj.printName() // "A"
複製代碼
// 做爲普通函數執行
function fn() {
console.log(this)
}
fn() // window
複製代碼
// call 、apply 、bind
function fn1(name, age) {
console.log(name)
console.log(this)
}
fn1.call({ x: 1 }, 'negrochn', 20) // "negrochn" { x: 1 }
fn1.apply({ x: 200 }, ['negrochn', 20]) // "negrochn" { x: 200 }
let fn2 = function(name, age) {
console.log(name)
console.log(this)
}.bind({ x: 300 })
fn2('negrochn', 20) // "negrochn" { x: 300 }
複製代碼
做用域
// 無塊級做用域
if (true) {
var name = 'negrochn'
}
console.log(name) // "negrochn"
// 只有函數和全局做用域
var a = 100
function fn() {
var a = 200
console.log('fn', a)
}
console.log('global', a) // "global" 100
fn() // "fn" 200
複製代碼
做用域鏈
var a = 100
function fn() {
var b = 200
// 當前做用域沒有定義的變量,即「自由變量」
console.log(a)
console.log(b)
}
fn()
複製代碼
var a = 100
function f1() {
var b = 200
function f2() {
var c = 300
console.log(a) // a 是自由變量
console.log(b) // b 是自由變量
console.log(c)
}
f2()
}
f1()
複製代碼
無
JS 沒有塊級做用域,ES6 有塊級做用域
閉包
function f1() {
var a = 100
// 返回一個函數(函數做爲返回值)
return function() {
console.log(a)
}
}
// f1 獲得一個函數
var f = f1()
var a = 200
f() // 100
複製代碼
閉包的使用場景
// 閉包 1 ,函數做爲返回值
function f1() {
var a = 100
return function() {
console.log(a) // a 是自由變量,向父級做用域去尋找,函數定義時的父做用域
}
}
var f = f1()
var a = 200
f() // 100
複製代碼
// 閉包 2 ,函數做爲參數傳遞
function f1() {
var a = 100
return function() {
console.log(a)
}
}
var f = f1()
function f2(fn) {
var a = 200
fn()
}
f2(f) // 100
複製代碼
說一下對變量提高的理解
說明 this
幾種不一樣的使用場景
call
、apply
、bind
建立 10 個 <a>
標籤,點擊的時候彈出對應的序號
// 這是一個錯誤的寫法
var i, a
for(i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function(e) {
e.preventDefault()
alert(i) // 自由變量,要去父做用域獲取值
})
document.body.appendChild(a)
}
複製代碼
// 這是正確的寫法
var i
for(i = 0; i < 10; i++) {
(function(i) {
var a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function(e) {
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
})(i)
}
複製代碼
若是理解做用域
實際開發中閉包的應用
// 閉包實際應用中主要用於封裝變量,收斂權限
function isFirstLoad() {
var _list = []
return function(id) {
if (_list.indexOf(id) >= 0) {
return false
} else {
_list.push(id)
return true
}
}
}
// 使用
var firstLoad = isFirstLoad()
console.log(firstLoad(10)) // true
console.log(firstLoad(10)) // false
console.log(firstLoad(20)) // true
// 你在 isFirstLoad 函數外,根本不可能修改掉 _list 的值
複製代碼
無
題目
知識點
什麼是異步
console.log(100)
setTimeout(function() {
console.log(200)
}, 1000)
console.log(300)
複製代碼
對比同步
console.log(100)
alert(200) // 1 秒以後手動點擊確認
console.log(300)
複製代碼
什麼時候須要異步
alert
同樣阻塞程序運行前端使用異步的場景
setTimeout
、setInterval
<img>
加載// Ajax 請求
console.log('start')
$.get('./data1.json', function(data1) {
console.log(data1)
})
console.log('end')
複製代碼
// 動態 <img> 加載
console.log('start')
var img = document.createElement('img')
img.onload = function() {
console.log('loaded')
}
img.src = '/xxx.png'
console.log('end')
複製代碼
// 事件綁定
console.log('start')
document.getElementById('btn1').addEventListener('click', function() {
alert('clicked')
})
console.log('end')
複製代碼
無
console.log(100)
setTimeout(function() {
console.log(200)
})
console.log(300)
複製代碼
setTimeout
後,傳入 setTimeout
的函數會被暫存起來,不會當即執行(單線程的特色,不能同時幹兩件事)單線程
同步和異步的區別是什麼?分別舉一個同步和異步的例子
alert
是同步,setTimeout
是異步一個關於 setTimeout 的筆試題
console.log(1)
setTimeout(function() {
console.log(2)
}, 0)
console.log(3)
setTimeout(function() {
console.log(4)
}, 1000)
console.log(5)
// 1
// 3
// 5
// 2
// 4 ,一秒後
複製代碼
前端使用異步的場景有哪些?
setTimeout
、setInterval
<img>
加載重點總結
題目
2020-02-24
格式的日期forEach
函數知識點
Date
Date.now() // 獲取當前時間毫秒數
var dt = new Date()
dt.getTime() // 獲取毫秒數
dt.getFullYear() // 年
dt.getMonth() // 月(0-11)
dt.getDate() // 日(1-31)
dt.getHours() // 時(0-23)
dt.getMinutes() // 分(0-59)
dt.getSeconds() // 秒(0-59)
複製代碼
Math
Math.random() // 獲取隨機數
複製代碼
數組 API
forEach
,遍歷全部元素every
,判斷全部元素是否都符合條件some
,判斷是否有至少一個元素符合條件sort
,排序map
,對元素從新組裝,生成新數組filter
,過濾符合條件的元素// forEach
var arr = [1, 2, 3]
arr.forEach(function(item, index) {
// 遍歷數組的全部元素
console.log(index, item)
})
// 0 1
// 1 2
// 2 3
複製代碼
// every
var arr = [1, 2, 3]
var result = arr.every(function(item, index) {
// 用來判斷全部的數組元素,都知足條件
if (item < 4) {
return true
}
})
console.log(result) // true
複製代碼
// some
var arr = [1, 2, 3]
var result = arr.some(function(item, index) {
// 用來判斷只要有一個數組元素知足條件
if (item < 2) {
return true
}
})
console.log(result) // true
複製代碼
// sort
var arr = [1, 4, 2, 3, 5]
var result = arr.sort(function(a, b) {
// 從小到大排序
return a - b // 從大到小排序 return b - a
})
console.log(result) // [1, 2, 3, 4, 5]
複製代碼
// map
var arr = [1, 2, 3]
var result = arr.map(function(item, index) {
// 將元素從新組裝並返回
return '<b>' + item + '</b>'
})
console.log(result) // ["<b>1</b>", "<b>2</b>", "<b>3</b>"]
複製代碼
// filter
var arr = [1, 2, 3]
var result = arr.filter(function(item, index) {
// 經過某一個條件過濾數組
if (item >= 2) {
return true
}
})
console.log(result) // [2, 3]
複製代碼
對象 API
var obj = {
x: 100,
y: 200,
z: 300
}
var key
for (key in obj) {
// 注意這裏的 hasOwnProperty ,在將原型鏈的時候講過了
if (obj.hasOwnProperty(key)) {
console.log(key, obj[key])
}
}
// x 100
// y 200
// z 300
複製代碼
無
獲取 2020-02-24
格式的日期
function formatDate(dt) {
if (!dt) {
dt = new Date()
}
var year = dt.getFullYear()
var month = dt.getMonth() + 1
var date = dt.getDate()
return year + '-' + month.toString().padStart(2, '0') + '-' + date.toString().padStart(2, '0')
}
formatDate(new Date()) // "2021-02-24"
複製代碼
獲取隨機數,要求長度一致的字符串格式
var random = Math.random()
random = random + '0000000000'
random = random.slice(0, 10)
console.log(random)
複製代碼
寫一個能遍歷對象和數組的通用 forEach
函數
function forEach(obj, fn) {
var key
if (obj instanceof Array) {
obj.forEach(function(item, index) {
fn(index, item)
})
} else {
for (key in obj) {
fn(key, obj[key])
}
}
}
var arr = [1, 2, 3]
forEach(arr, function(key, value) {
console.log(key, value)
})
// 0 1
// 1 2
// 2 3
var obj = {
x: 100,
y: 200
}
forEach(obj, function(key, value) {
console.log(key, value)
})
// x 100
// y 200
複製代碼
重點總結
回顧 JS 基礎知識
Hello World
都不能實現JS Web API
W3C 標準中關於 JS 的規定有
頁面彈框是 window.alert(123)
,瀏覽器須要作
window
全局變量,對象類型alert
屬性,屬性值是一個函數獲取元素 document.getElementById(id)
,瀏覽器須要
document
全局變量,對象類型getElementById
的屬性,屬性值是一個函數可是 W3C 標準沒有規定任何 JS 基礎相關的東西
全面考慮,JS 內置的全局函數和對象有哪些?
navigator.userAgent
總結
常說的 JS(瀏覽器執行的 JS)包含兩部分
DOM ,全稱 Document Object Model
題目
知識點
DOM 本質
<?xml version="1.0" encoding="utf-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend</body>
<other>
<a></a>
<b></b>
</other>
</note>
複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<p>this is p</p>
</div>
</body>
</html>
複製代碼
DOM 本質:瀏覽器拿到 HTML 代碼後,DOM 把 HTML 代碼結構化成瀏覽器可識別以及 JS 可識別的東西。
HTML 代碼就是一個字符串,可是瀏覽器已經把字符串結構化成樹形結構了。
DOM 能夠理解爲:瀏覽器把拿到的 HTML 代碼,結構化成一個瀏覽器能識別而且 JS 可操做的一個模型而已。
DOM 節點操做
獲取 DOM 節點
var div1 = document.getElementById('div1') // 元素
var divList = document.getElementsByTagName('div') // 集合
console.log(divList.length)
console.log(divList[0])
var containerList = document.getElementsByClassName('container') // 集合
var pList = document.querySelectorAll('p') // 集合
複製代碼
Property ,JS 對象屬性
var pList = document.querySelectorAll('p')
var p = pList[0]
console.log(p.style.width) // 獲取樣式
p.style.width = '100px' // 修改樣式
console.log(p.className) // 獲取 class
p.className = 'p1' // 修改 class
// 獲取 nodeName 和 nodeType
console.log(p.nodeName) // "P"
console.log(p.nodeType) // 1
複製代碼
Attribute ,HTML 標籤屬性
var pList = document.querySelectorAll('p')
var p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-name', 'imooc')
p.getAttribute('style')
p.setAttribute('style', 'font-size: 30px;')
複製代碼
無
DOM 結構操做
新增節點
var div1 = document.getElementById('div1')
// 添加新節點
var p = document.createElement('p')
p.innerHTML = 'new p'
div1.appendChild(p) // 添加新建立的元素
// 移動已有節點
var p4 = document.getElementById('p4')
div1.appendChild(p4)
複製代碼
獲取父元素和子元素
var div1 = document.getElementById('div1')
var parent = div1.parentNode
var children = div1.childNodes
複製代碼
刪除節點
div1.removeChild(children[0])
複製代碼
無
DOM 是哪一種基本的數據結構?
答:樹
DOM 操做的經常使用 API 有哪些?
答:
DOM 節點的 Attribute 和 Property 有何區別?
答:
重點總結
題目
知識點
navigator
srceen
location
history
navigator & screen
// navigator
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome) // 81
// screen
console.log(screen.width) // 1920
console.log(screen.height) // 1080
複製代碼
location & history
// location ,以 http://localhost:8080/login?username=negrochn&password=123456#mid=1 爲例
console.log(location.href) // http://localhost:8080/login?username=negrochn&password=123456#mid=1
console.log(location.protocol) // http:
console.log(location.host) // localhost:8080
console.log(location.hostname) // localhost
console.log(location.port) // 8080
console.log(location.pathname) // /login
console.log(location.search) // ?username=negrochn&password=123456
console.log(location.hash) // #mid=1
// history
history.back()
history.forward()
複製代碼
無
題目
知識點
通用事件綁定
var btn = document.getElementById('btn1')
btn.addEventListener('click', function(e) {
console.log('clicked')
})
function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn)
}
var a = document.getElementById('link1')
bindEvent(a, 'click', function(e) {
e.preventDefault() // 阻止默認行爲
alert('clicked')
})
複製代碼
關於 IE 低版本的兼容性
attachEvent
綁定事件,和 W3C 標準不同事件冒泡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件冒泡</title>
</head>
<body>
<div id="div1">
<p id="p1">激活</p>
<p id="p2">取消</p>
<p id="p3">取消</p>
<p id="p4">取消</p>
</div>
<div id="div2">
<p id="p5">取消</p>
<p id="p6">取消</p>
</div>
<script> function bindEvent(elem, type, fn) { elem.addEventListener(type, fn) } var p1 = document.getElementById('p1') var body = document.body bindEvent(p1, 'click', function(e) { e.stopPropagation() alert('激活') }) bindEvent(body, 'click', function(e) { alert('取消') }) </script>
</body>
</html>
複製代碼
代理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件冒泡</title>
</head>
<body>
<div id="div1">
<a href="#">a1</a>
<a href="#">a2</a>
<a href="#">a3</a>
<a href="#">a4</a>
<!-- 會隨時新增更多 a 標籤 -->
</div>
<script> var div1 = document.getElementById('div1') div1.addEventListener('click', function(e) { var target = e.target if (target.nodeName === 'A') { alert(target.innerHTML) } }) </script>
</body>
</html>
複製代碼
完善通用綁定事件的函數
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
elem.addEventListener(type, function(e) {
var target
if (selector) {
target = e.target
if (target.matches(selector)) {
fn.call(target, e)
}
} else {
fn(e)
}
})
}
// 使用代理
var div1 = document.getElementById('div1')
bindEvent(div1, 'click', 'a', function(e) {
console.log(this.innerHTML)
})
// 不使用代理
var a = document.getElementById('a1')
bindEvent(div1, 'click', function(e) {
console.log(a.innerHTML)
})
複製代碼
代理的好處
無
編寫一個通用的事件監聽函數
答:
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
elem.addEventListener(type, function(e) {
let target
if (selector) {
target = e.target
if (target.matches(selector)) {
fn.call(target, e)
}
} else {
fn(e)
}
})
}
複製代碼
描述事件冒泡流程
答:
對一個無限下拉加載圖片的頁面,如何給每一個圖片綁定事件
答:
重點總結
題目
知識點
XMLHttpRequest
var xhr = new XMLHttpRequest()
xhr.open('GET', '/api', false)
xhr.onreadystatechange = function() {
// 這裏的函數異步執行
if (xhr.readyState == 4) {
if (xhr.status == 200) {
alert(xhr.responseText)
}
}
}
xhr.send(null)
複製代碼
IE 兼容性問題
readyState
status
狀態碼 | 英文描述 | 中文描述 |
---|---|---|
100 | Continue | 繼續。客戶端應繼續其請求。 |
200 | OK | 請求成功。 |
204 | No Content | 無內容。服務器成功處理,但未返回內容。 |
206 | Partial Content | 部份內容。服務器成功處理了部分 GET 請求。 |
301 | Moved Permanently | 永久移動。請求的資源已被永久的移動到新 URI ,返回信息會包括新的 URI ,瀏覽器會自動定向到新 URI 。 |
302 | Found | 臨時移動。客戶端應繼續使用原有 URI 。 |
304 | Not Modified | 未修改。所請求的資源未修改,服務器返回此狀態碼時,不會返回任何資源。 |
307 | Temporary Redirect | 臨時重定向。使用 GET 請求重定向。 |
400 | Bad Request | 客戶端請求的語法錯誤,服務器沒法理解。 |
401 | Unauthorized | 請求要求用戶的身份認證。 |
403 | Forbidden | 服務器理解請求客戶端的請求,可是拒絕執行此請求。 |
404 | Not Found | 服務器沒法根據客戶端的請求找到資源(網頁)。 |
500 | Internal Server Error | 服務器內部錯誤,沒法完成請求。 |
502 | Bad Gateway | 做爲網關或代理工做的服務器嘗試執行請求時,從遠程服務器接收到了一個無效的響應。 |
503 | Service Unavailable | 因爲超載或系統維護,服務器暫時的沒法處理客戶端的請求。 |
什麼是跨域
<img src=xxx>
,用於打點統計,統計網站多是其餘域<link href=xxx>
,可使用 CDN<script src=xxx>
,可使用 CDN ,用於 JSONP跨域注意事項
JSONP 實現原理
<script src="http://coding.imooc.com/api.js">
callback({ x: 100, y: 200 })
(可動態生成)<script> window.callback = function(data) { // 這是咱們跨域獲得的信息 console.log(data) } </script>
<script src="http://coding.imooc.com/api.js"></script>
<!-- 以上將返回 callback({ x: 100, y: 200 }) -->
複製代碼
服務器設置 http header
// 注意:不一樣後端語言的寫法可能不同
// 第二個參數填寫容許跨域的域名稱,不建議直接寫 *
response.setHeader('Access-Control-Allow-Origin', 'http://a.com, http://b.com')
response.setHeader('Access-Control-Allow-Headers', 'X-Requested-With')
response.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')
// 接收跨域的 Cookie
response.setHeader('Access-Control-Allow-Credentials', 'true')
複製代碼
手寫一個 Ajax ,不依賴第三方庫
var xhr = new XMLHttpRequest()
xhr.open('GET', '/api', false)
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
alert(xhr.responseText)
}
}
}
xhr.send(null)
複製代碼
跨域的幾種實現方式
重點總結
題目
知識點
Cookie
document.cookie
獲取和修改Cookie 用於存儲的缺點
document.cookie
localStorage 和 sessionStorage
getItem(key)
、setItem(key, value)
請描述一下 Cookie 、sessionStorage 和 localStorage 的區別?
關於開發環境
開發環境包含
IDE
Git
經常使用 Git 命令
git add .
git checkout xxx
,還原文件git commit -m 'xxx'
,提交到本地倉庫git push origin master
,提交到遠程倉庫git pull origin master
,下載遠程倉庫的文件git branch
,建立分支git checkout -b xxx
/git checkout xxx
,新建一個分支/切換到另外一個分支git merge xxx
,把以前的分支拷貝到這裏無
多人開發
步驟
git checkout -b dev
,建立一個 dev 分支vi a.js
修改內容(vi 屬於 Linux 命令,git status
查看是否被修改,git diff
查看修改的內容)git add .
git commit -m 'update dev'
git push origin dev
,提交到遠程倉庫的 dev 分支git checkout master
,切換到 master 分支git pull origin master
,下載遠程倉庫的 master 分支的文件git merge dev
,把 dev 分支的修改內容拷貝到 master 分支git push origin master
,提交到遠程倉庫的 master 分支附加命令
cat 文件名
,查看文件內容git diff
,查看修改的內容git branch
,查看分支和當前在哪條分支上git status
,查看文件是否被修改的狀態vi 文件
,新建文件並打開文件esc + :wq
,退出並保存文件不使用模塊化
// util.js
function getFormatDate(date, type) {
// type === 1 返回 2021-03-06
// type === 2 返回 2021年3月6日
// ...
}
複製代碼
// a-util.js
function aGetFormatDate(date) {
// 要求返回 2021年3月6日格式
return getFormatDate(date, 2)
}
複製代碼
// a.js
var dt = new Date()
console.log(aGetFormatDate(dt))
複製代碼
<script src="util.js"></script>
<script src="a-util.js"></script>
<script src="a.js"></script>
<!-- 1. 這些代碼中的函數必須是全局變量,才能暴露給使用方。全局變量污染。 -->
<!-- 2. a.js 知道要引用 a-util.js ,可是他知道還須要依賴於 util.js 嗎 -->
複製代碼
AMD
define
函數require
函數使用 require.js
// util.js
define(function() {
return {
getFormatDate: function(date, type) {
if (type === 1) {
return '2021-03-06'
} else if (type === 2) {
return '2021年3月6日'
}
}
}
})
複製代碼
// a-util.js
define(['./util.js'], function(util) {
return {
aGetFormatDate: function(date) {
return util.getFormatDate(date, 2)
}
}
})
複製代碼
// a.js
define(['./a-util.js'], function(aUtil) {
return {
printDate: function(date) {
console.log(aUtil.aGetFormatDate(date))
}
}
})
複製代碼
// main.js
require(['./a.js'], function(a) {
var date = new Date()
a.printDate(date)
})
複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AMD</title>
</head>
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.min.js" data-main="./main.js"></script>
</body>
</html>
複製代碼
無
CommonJS
使用 CommonJS
// util.js
module.exports = {
getFormatDate: function(date, type) {
if (type === 1) {
return '2021-03-06'
} else if (type === 2) {
return '2021年3月6日'
}
}
}
複製代碼
// a-util.js
var util = require('util.js')
module.exports = {
aGetFormatDate: function(date) {
return util.getFormatDate(date, 2)
}
}
複製代碼
AMD 和 CommonJS 的使用場景
重點總結
運行環境
知識點
題目
window.onload
和 DOMContentLoaded
的區別知識點
加載資源的形式
加載一個資源的過程
瀏覽器渲染頁面的過程
<script>
時,會執行並阻塞渲染爲什麼要把 CSS 放在 head 中?
答:保證先加載 CSS ,接着渲染,否則要渲染兩次,用戶體驗差。
爲什麼要把 JS 放在 body 最下面?
答:不會阻塞渲染過程,提升性能。
window.onload
和 DOMContentLoaded
window.addEventListener('load', function() {
// 頁面的所有資源加載完纔會執行,包括圖片、視頻等
})
document.addEventListener('DOMContentLoaded', function() {
// DOM 渲染完便可執行,此時圖片、視頻還可能沒有加載完
})
複製代碼
從輸入 URL 到獲得 HTML 的詳細過程
window.onload
和 DOMContentLoaded
的區別
性能優化
原則
從哪裏入手
加載資源優化
渲染優化
資源合併
<script src=a.js></script>
<script src=b.js></script>
<script src=c.js></script>
複製代碼
<script src="abc.js"></script>
複製代碼
緩存
CDN
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
複製代碼
使用 SSR 後端渲染
懶加載
<img id="img1" src="preview.png" data-realsrc="abc.png" />
<script> var img1 = document.getElementById('img1') img1.src = img1.getAttribute('data-realsrc') </script>
複製代碼
緩存 DOM 查詢
// 未緩存 DOM 查詢
var i
for (i = 0; i < document.getElementByTagName('p').length; i++) {
}
// 緩存了 DOM 查詢
var pList = document.getElementByTagName('p')
var i
for (i = 0; i < pList.length; i++) {
}
複製代碼
合併 DOM 插入
var listNode = document.getElementById('list')
// 要插入 10 個 li 標籤
var frag = document.createDocumentFragment()
var x, li
for (x = 0; x < 10; x++) {
li = document.createElement('li')
li.innerHTML = 'List item ' + x
frag.appendChild(li)
}
listNode.appendChild(frag)
複製代碼
事件節流
var textarea = document.getElementById('text')
var timeoutid
textarea.addEventListener('keyup', function() {
if (timeoutid) {
clearTimeout(timeoutid)
}
timeoutid = setTimeout(function() {
// 觸發 change 事件
}, 100)
})
複製代碼
儘早操做
window.addEventListener('load', function() {
// 頁面的所有資源加載完纔會執行,包括圖片、視頻等
})
document.addEventListener('DOMContentLoaded', function() {
// DOM 渲染完便可執行,此時圖片、視頻還可能沒有加載完
})
複製代碼
知識點
XSS
<script>
<
爲 <
,>
爲 >
XSRF
<img src="xxx.com/pay?id=100" />
簡歷
面試過程當中
var
和 let
、const
的區別
答:
var
是 ES5 語法,let
、const
是ES6 語法,var
有變量提高var
和 let
是變量,可修改,const
是常量,不可修改let
、const
有塊級做用域,var
沒有typeof
返回哪些類型
答:
typeof null === 'object'
)列舉強制類型轉換和隱式類型轉換
答:
parseInt
、parseFloat
、toString
等手寫深度比較,模擬 lodash 的 isEqual
答:
// 判斷是不是對象或數組
function isObject(obj) {
return typeof obj === 'object' && obj !== null
}
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
// 值類型(注意,參與 equal 的通常不會是函數)
return obj1 === obj2
}
if (obj1 === obj2) {
return true
}
// 兩個都是對象或數組,並且不相等
// 1. 先取出 obj1 和 obj2 的 keys ,比較個數
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) {
return false
}
// 2. 以 obj1 爲基準,和 obj2 一次遞歸比較
for (let key in obj1) {
if (!isEqual(obj1[key], obj2[key])) {
return false
}
}
// 3. 全相等
return true
}
const obj1 = {
a: 100,
b: {
x: 100,
y: 200
}
}
const obj2 = {
a: 100,
b: {
x: 100,
y: 200
}
}
console.log(obj1 === obj2) // false
console.log(isEqual(obj1, obj2)) // true
複製代碼
split
和 join
的區別
答:
'1-2-3'.split('-') // [1, 2, 3]
[1, 2, 3].join('-') /// '1-2-3'
複製代碼
數組的 pop
、push
、unshift
、shift
分別作什麼
答:
const arr = [10, 20, 30, 40]
const result = arr.pop()
console.log(result, arr) // 40, [10, 20, 30]
複製代碼
const arr = [10, 20, 30, 40]
const result = arr.push(50) // 返回 length
console.log(result, arr) // 5, [10, 20, 30, 40, 50]
複製代碼
const arr = [10, 20, 30, 40]
const result = arr.unshift(5) // 返回 length
console.log(result, arr) // 5, [5, 10, 20, 30, 40]
複製代碼
const arr = [10, 20, 30, 40]
const result = arr.shift()
console.log(result, arr) // 10, [20, 30, 40]
複製代碼
純函數:1. 不改變源數組;2. 返回一個數組
// 純函數
const arr = [10, 20, 30, 40]
const cArr = arr.concat([50, 60, 70]) // [10, 20, 30, 40, 50, 60, 70]
const mArr = arr.map(item => item * 10) // [100, 200, 300, 400]
const fArr = arr.filter(item => item > 20) // [30, 40]
const sArr = arr.slice() // [10, 20, 30, 40]
// 非純函數
// push 、pop 、shift 、unshift
// forEach
// some every reduce
複製代碼
數組 slice
和 splice
的區別
答:
slice
是切片,splice
是剪接const arr = [10, 20, 30, 40, 50]
// slice 是純函數
const sliceArr = arr.slice(1, 4)
// splice 不是純函數
const spliceArr = arr.splice(1, 2, 'a', 'b', 'c')
console.log(arr, spliceArr) // [10, "a", "b", "c", 40, 50],[20, 30]
複製代碼
[10, 20, 30].map(parseInt)
返回結果是什麼?
答:
map
的參數和返回值parseInt
的參數和返回值[10, 20, 30].map(parseInt)
// 至關於
[10, 20, 30].map((item, index) => {
return parseInt(item, index)
})
// 分解爲
parseInt(10, 0) // 10
parseInt(20, 1) // NaN
parseInt(30, 2) // NaN
複製代碼
Ajax 請求 get 和 post 的區別?
答:
函數 call 和 apply 的區別?
答:
fn.call(this, p1, p2, p3)
fn.apply(this, arguments)
複製代碼
事件代理(委託)是什麼?
答:
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
elem.addEventListener(type, function(e) {
var target
if (selector) {
target = e.target
if (target.matches(selector)) {
fn.call(target, e)
}
} else {
fn(e)
}
})
}
// 使用代理
var div1 = document.getElementById('div1')
bindEvent(div1, 'click', 'a', function(e) {
console.log(this.innerHTML)
})
// 不使用代理
var a = document.getElementById('a1')
bindEvent(div1, 'click', function(e) {
console.log(a.innerHTML)
})
複製代碼
閉包是什麼,有什麼特性?有什麼負面影響?
答:
如何阻止事件冒泡和默認行爲?
答:
event.stopPropagation()
event.preventDefault()
查找、添加、刪除、移動 DOM 節點的方法?
答:
getElementById
、getElementsByTagName
、getElementsByClassName
、querySelectorAll
appendChild
(添加和移動)removeChild
parentNode
、childNodes
如何減小 DOM 操做?
答:
解釋 JSONP 的原理,爲什麼它不是真正的 Ajax ?
答:
document
的 load 和 ready 的區別
答:
window.addEventListener('load', function() {
// 頁面的所有資源加載完纔會執行,包括圖片、視頻等
})
document.addEventListener('DOMContentLoaded', function() {
// DOM 渲染完便可執行,此時圖片、視頻還可能沒有加載完
})
複製代碼
==
和 ===
的不一樣
答:
==
會嘗試類型轉換===
嚴格相等==
?函數聲明和函數表達式的區別
答:
function fn() {}
const fn = function() {}
new Object()
和 Object.create()
的區別
答:
{}
等同於 new Object()
,原型是 Object.prototype
Object.create(null)
沒有原型Object.crate({...})
可指定原型const obj1 = {
a: 10,
b: 20,
sum() {
return this.a + this.b
}
}
const obj2 = new Object({
a: 10,
b: 20,
sum() {
return this.a + this.b
}
})
const obj3 = new Object(obj1)
console.log(obj1 === obj2) // false
console.log(obj1 === obj3) // true
const obj4 = Object.create(null) // {} ,但沒有原型
const obj5 = new Object() // {}
const obj6 = Object.create(obj1) // 建立一個空對象,把空對象的原型指向 obj1
console.log(obj1 === obj6) // false
console.log(obj1 === obj6.__proto__) // true
複製代碼
關於 this
的場景題
const User = {
count: 1,
getCount: function() {
return this.count
}
}
console.log(User.getCount()) // 1
const func = User.getCount
console.log(func()) // undefined
複製代碼
關於做用域和自由變量的場景題(1)
let i
for (i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i)
}, 0)
}
// 4
// 4
// 4
複製代碼
判斷字符串以字母開頭,後面字母數字下劃線,長度 6-30
答:
const reg = /^[a-zA-Z]\w{5,29}$/
複製代碼
關於做用域和自由變量的場景題(2)
let a = 100
function test() {
alert(a) // 100
a = 10
alert(a) // 10
}
test()
alert(a) // 10
複製代碼
手寫字符串 trim
方法,保證瀏覽器兼容性
答:
String.prototype.trim = function() {
return this.replace(/^\s+/, '').replace(/\s+$/, '')
}
// (原型、this 、正則表達式)
複製代碼
如何獲取多個數字中的最大值
答:
function max() {
const nums = Array.prototype.slice.call(arguments) // 變爲數組
let max = -Infinity
nums.forEach(n => {
if (n > max) {
max = n
}
})
return max
}
// 或者使用 Math.max()
複製代碼
如何用 JS 實現繼承?
答:
如何捕獲 JS 程序中的異常?
答:
// 第一種方式
try {
// TODO
} catch(error) {
console.error(error) // 手動捕獲
} finally {
// TODO
}
// 第二種方式
// 自動捕獲
window.onerror = function(message, source, lineNum, colNum, error) {
// 第一,對跨域的 JS ,如 CDN 的不會有詳細的報錯信息
// 第二,對於壓縮的 JS ,還要配合 SourceMap 反查到未壓縮代碼的行、列
}
複製代碼
什麼是 JSON ?
答:
window.JSON
是一個全局對象,JSON.stringify
和 JSON.parse
獲取當前頁面 URL 參數
答:
location.search
URLSearchParams
// 傳統方式
function query(name) {
const search = location.search.substr(1)
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i')
const res = search.match(reg)
if (res === null) {
return null
}
return res[2]
}
複製代碼
// URLSearchParams
function query(name) {
const search = location.search
const p = new URLSearchParams(search)
return p.get(name)
}
複製代碼
將 URL 參數解析爲 JS 對象
答:
// 傳統方式,分析 search
function query2Obj() {
const res = {}
const search = location.search.substr(1) // 去掉前面的 ?
search.split('&').forEach(paramStr => {
const arr = paramStr.split('=')
const [key, val] = arr
res[key] = val
})
return res
}
複製代碼
// 使用 URLSearchParams
function query2Obj() {
const res = {}
const pList = new URLSearchParams(location.search)
pList.forEach((val, key) => {
res[key] = val
})
return res
}
複製代碼
手寫數組 faltern ,考慮多層級
答:
function flat(arr) {
// 驗證 arr 中,還有沒有深層數組
const isDeep = arr.some(item => item instanceof Array)
if (!isDeep) {
return arr
}
return flat(Array.prototype.concat.apply([], arr))
}
const res = flat([1, 2, [3, 4], [5, [6, 7]]])
console.log(res) // [1, 2, 3, 4, 5, 6, 7]
複製代碼
數組去重
答:
// 傳統方式
function unique(arr) {
const res = []
arr.forEach(item => {
if (res.indexOf(item) < 0) {
res.push(item)
}
})
return res
}
console.log(unique([1, 2, 3, 1, 2, 3, 4])) // 1 2 3 4
複製代碼
// 使用 Set(無序、不重複)
function unique(arr) {
const set = new Set(arr)
return [...set]
}
console.log(unique([1, 2, 3, 1, 2, 3, 4])) // 1 2 3 4
複製代碼
手寫深拷貝
答:
function deepClone(obj = {}) {
// 若是不是數組或對象,直接返回
if (typeof obj !== 'object' || obj == null) {
return obj
}
// 初始化返回結果
let result
if (obj instanceof Array) {
result = []
} else {
result = {}
}
// 遍歷數組或對象的屬性
for (let key in obj) {
// 保證 key 不是原型的屬性
if (obj.hasOwnProperty(key)) {
// 遞歸調用
result[key] = deepClone(obj[key])
}
}
// 返回結果
return result
}
// 注意 Object.assign 不是深拷貝
複製代碼
介紹一下 RAF(requestAnimationFrame
)
答:
setTimeout
要手動更新頻率,而 RAF 瀏覽器會自動控制setTimeout
依然執行<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>setTimeout</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<style> #div1, #div2 { width: 100px; height: 50px; margin-bottom: 20px; background-color: red; } </style>
</head>
<body>
<div id="div1">setTimeout</div>
<div id="div2">requestAnimateFrame</div>
<script> const $div1 = $('#div1') let curWidth = 100 const maxWidth = 640 function animate() { curWidth += 3 $div1.css('width', curWidth) if (curWidth < maxWidth) { setTimeout(animate, 16.7) } } animate() const $div2 = $('#div2') let curWidth2 = 100 function animate2() { curWidth2 += 3 $div2.css('width', curWidth2) if (curWidth2 < maxWidth) { window.requestAnimationFrame(animate2) } } animate2() </script>
</body>
</html>
複製代碼
前端性能如何優化?通常從哪幾個方面考慮?
答: