变量和运算
typeof运算符可以识别哪些类型?
- 值类型;
- 函数(function);
- 引用类型(object);
- 注意:
typeof null === 'object'
- 注意:
Map和Object的区别
- Map可以以任意类型为key,Object只能以字符串为key;
- Map是有序结构,Object是无序结构;
手写一下JavaScript深拷贝
function deepClone(obj) {
if(typeof obj !== 'object' || obj == null) {
return obj;
}
let cloneObj;
if(obj instanceof Array) {
cloneObj = []
} else if(obj instanceof Object) {
cloneObj = {}
} else {
throw new Error("Undefined Type!");
}
Object.keys(obj).map(key => cloneObj[key] = deepClone(obj[key]));
return cloneObj;
}
手写一个函数isEqual,比较两个对象是否全相等
function isObject(obj) {
return typeof obj === "object" && obj !== null
}
function isEqual(obj1, obj2) {
if(obj1 === obj2) return true
if(!isObject(obj1) || !isObject(obj2)) return false
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if(obj1Keys.length !== obj2Keys.length) return false
for(const key in obj1) {
if(!isEqual(obj1[key], obj2[key])) return false
}
return true
}
常见的隐式类型转换都有哪些?
- 字符串拼接:
10 + '10' = '1010'
; ==
运算符:null == undefined
,可用== null
代替=== null && === undefined
;if
语句:if
中判断的依据是turely
与falsely
,即!!(x)
的真值。注意:!!('') === false
;
for in与for of有什么区别?
- for in仅适用于可枚举对象(key值);
- for of仅适用于可迭代对象(value值);
- 可迭代对象由一个内建的
Symbol.iterator
方法控制,循环启动时必须返回一个迭代器,此后通过迭代器中返回的next()
方法获取下一个值。
- 可迭代对象由一个内建的
- for await of仅适用于遍历
Promise
(功能上相当于Promise.all
);
原型和原型链
如何理解JavaScript原型?
- 每个
class
都有显式原型prototype
; - 每个实例都有隐式原型
__proto__
; - 实例的隐式原型指向对应类的显示原型;
实例获取属性或方法时,遵循的顺序是什么?
- 先在自身属性和方法中寻找;
- 当(1)寻找失败时,从隐式原型中寻找需要的属性和方法;
- 重复上述步骤,直到隐式原型的值为
null
;
作用域和闭包
什么是闭包?说说你对闭包的理解?
所有自由变量的查找,均从函数定义的地方开始,向上级作用域查找。
this如何取值?
this
的取值是在运行时确定的,不是在定义时确定的。
- 箭头函数没有
this
,箭头函数中的this
指向父级作用域; - 通过
call
可以改变自身的this
指向,通过bind
可以改变复制的this
指向;
哪里不能使用箭头函数?
- 箭头函数的缺陷:
- 没有
arguments
; - 无法通过
call, bind, apply
改变this
(箭头函数的this
为父级元素);
- 没有
- 不适用场景:
- 对象方法;
- 原型方法;
- 构造函数;
- 动态上下文中的回调函数;
异步
说说你对Promise的理解?
说说你对事件循环的认识?
手写一下Promise
Promise类实现
class MyPromise{
state = "pending"
value
reason
resolveCallBacks = []
rejectCallBacks = []
constructor(fn) {
const resolveHandler = (value) => {
if(this.state === "pending") {
this.state = "fulfilled"
this.value = value
this.resolveCallBacks.forEach(fn => fn(this.value))
}
}
const rejectHandler = (reason) => {
if(this.state === "pending") {
this.state = "rejected"
this.reason = reason
this.rejectCallBacks.forEach(fn => fn(this.reason))
}
}
try {
fn(resolveHandler, rejectHandler)
} catch (error) {
rejectHandler(error)
}
}
then(fn1, fn2) {
fn1 = typeof fn1 === "function" ? fn1 : (val) => val
fn2 = typeof fn2 === "function" ? fn2 : (err) => err
if(this.state === "pending") {
return new MyPromise((resolve, reject) => {
this.resolveCallBacks.push(() => {
try {
const newValue = fn1(this.value)
resolve(newValue)
} catch (e) {
reject(e)
}
})
this.rejectCallBacks.push(() => {
try {
const newReason = fn2(this.reason)
resolve(newReason)
} catch (e) {
reject(e)
}
})
})
}
if(this.state === "fulfilled") {
return new MyPromise((resolve, reject) => {
try {
const newValue = fn1(this.value)
resolve(newValue)
} catch (error) {
reject(error)
}
})
}
if(this.state === "rejected") {
return new MyPromise((resolve, reject) => {
try {
const newReason = fn2(this.reason)
resolve(newReason)
} catch (error) {
reject(newReason)
}
})
}
}
catch(fn) {
return this.then(null, fn)
}
}
Promise静态方法实现
MyPromise.resolve = function(value) {
return new MyPromise((resolve, reject) => resolve(value))
}
MyPromise.reject = function(reason) {
return new MyPromise((resolve, reject) => reject(reason))
}
MyPromise.all = function(promiseList = []) {
return new MyPromise((resolve, reject) => {
const length = promiseList.length
const result = []
let resolveCount = 0
promiseList.forEach((p, i) => {
p.then(data => {
result[i] = data
resolveCount += 1
if(resolveCount === length) {
resolve(result)
}
}).catch(err => reject(err))
})
})
}
MyPromise.any = function(promiseList = []) {
return new MyPromise((resolve, reject) => {
let resolved = false
promiseList.forEach(p => {
p.then(data => {
if(!resolved) {
resolved = true
resolve(data)
}
}).catch(err => reject(err))
})
})
}
DOM
说说你对DOM的理解?
手写一下防抖
用户输入结束或暂停时,再触发事件,使用装饰器模式。
function debounce(fn, delay = 500) {
let timer = null
return function() {
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
手写一下节流
将用户的高频输入等时间间隔触发,使用装饰器模式。
function throttle(fn, delay = 100) {
let timer = null
return function() {
if(timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
Ajax
什么是同源策略?
Ajax请求时,浏览器要求当前页面与服务端必须同源,即协议,域名,端口必须一致。
- 图片,CSS,JavaScript可以不同源;
- 所有的跨域,都需要获得服务端的允许和配合。
跨域的常见方式有什么?
- JSONP:服务端动态拼接数据,利用
<script>
绕过跨域限制; - CORS:服务端设置http-header;
GET和POST的区别有什么?
- GET:
- 查询操作,拼接在url上;
- 有缓存;
- 受url长度限制;
- POST:
- 提交操作,放在请求体内;
- 无缓存;
- 无长度限制;
存储
localStorage sessionStorage和cookie的区别?
- cookie:最多存4KB,请求时会被发送到服务端;
- localStorage:最多存5MB,不会随请求发送到服务端,永久存储;
- sessionStorage:最多存5MB,不会随请求发送到服务端,关闭页面即清空;