声明:慕课网《前端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>
复制代码
前端性能如何优化?通常从哪几个方面考虑?
答: