JS
# 原型和原型链
- 每个对象都会在内部初始化一个属性 prototype,当访问对象的属性时,如果这个对象内部不存在这个属性,就会去 prototype 里找这个属性,这个 prototype 又会有自己的 prototype 属性,这样一直找下去就是原型链概念
- 优先取构造函数的上属性
- 每个函数都有 prototype 属性,除了 Function.prototype.bind(),该属性指向原型。
- 每个对象都有proto属性,指向了创建该对象的构造函数的原型。其实这个属性指向了 prototype,但是 prototype 是内部属性,我们并不能访问到,所以使用proto来访问。
- 对象可以通过 proto 来寻找不属于该对象的属性,proto 将对象连接起来组成了原型链。
- 原型链的终集是 null
# 防抖和节流
- 防抖,触发事件后在一段时间内只执行一次,若在这段时间内再次触发则会重新计算执行时间
- 节流,连续发生的事件在一段时间内只执行一次,节流会减少执行频率
# es6 转 es5 的实现思路
# call、apply、bind 的区别
- 都可以改变指针的对象,将函数放到特定作用域中执行
- call 函数名.call(作用域对象, 多个参数)
- apply 函数名.apply(作用域对象, 数组参数)
- bind 绑定作用域 函数名.bind(作用域对象, 多个参数)
# void 0 与 undefined
# let 与 var 区别
- var 会全局污染,用 var 声明的变量会挂在 window 全局变量下,用 let 声明的变量则不会;两者都可以跨标签使用。
- let 有块级作用域
- let 在执行上下文时会有暂时性死区,不允许在声明前使用变量;var 可以在声明前使用变量,值为 undefined
- var 允许重复声明,let 不允许重复声明
# this 指向
- 通常情况下指向函数的直接调用者,而非间接调用者,如果该对象没有对应属性,则不会再往上一层查找
- 在事件中,this 指向触发这个事件的对象,特殊情况 IE 中 attachEvent 的 this 总是指向 window
- 如果有 new 关键字,this 指向 new 出来的那个对象
- 箭头函数没有自己的 this 如果我们在函数中引用 this,this 值取决于外部“正常的”函数。这是箭头函数的一个特性,当我们并不想要一个独立的 this,反而想从外部上下文中获取时,它很有用
- 一个函数在声明时,可能就使用了 this,但是这个 this 只有在函数被调用时才会有值。
# 闭包
- 在函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用域链
- 让这些变量始终保存在内存中,不会随着函数的结束而自动销毁,用完需要对闭包做 null 的处理手动销毁
- 作用域链,如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,查找过程形成的链条就叫做作用域链
- 优点:
- 避免全局变量的污染
- 封装对象的私有属性和方法
- 缺点:
- 会常驻内存,增大内存使用量
- 使用不当会造成内存泄露
# 混合
# 继承
- 构造继承,函数内使用 call、apply 调用
- 原型继承,设置 prototype 属性
# 模板引擎
- 模板引擎负责组装数据,将变量模板编译成浏览器能识别的代码
# Flux
# 同构
# 浅拷贝
只拷贝第一层
- 循环
- es6 解构
- Object.assign()
# 深拷贝
- json 转换,正则、函数、会变成{},日期会变成字符串
- 不能序列化函数
- 会忽略 undefined
- 不能解决循环引用的对象
- 递归,判断正则、日期、函数等特殊类型
# 面向对象
- 将对象方法封装成相互独立的模块
- 以功能来划分
# 面向过程
- 分析出解决问题所需要的步骤,然后一步一步解决
# 多线程实现
- 单线程原因:避免 dom 渲染的冲突
- webworker 支持多线程,但是不能操作 dom
# 遍历(异步遍历,提前返回问题)
- forEach(item, index, array) 类似 for 循环,没有返回值。IE 不支持,无法使用 break,continue 跳出循环,使用 return 跳出循环
- map(item, index) 需要返回值
- reduce((prev, item, index) => {}, initValue) 对数组中每个元素执行回调,并将累计结果返回给下一个回调
- for...in 可以遍历对象
- for...of
# 模块化开发
- 立即执行函数不暴露私有属性
# 懒加载原理
- 图片加载是由 src 的值引起的,当对 src 进行赋值时浏览器会请求图片资源,可以用 html5 属性 data-xxx 来保存图片路径,当需要显示图片时,再将 data-xxx 的值赋给 src,实现按需加载
# 异步加载 js
- defer(只支持 IE),async
- 创建 script,插入到 dom 中,再执行 callback
# 装饰器
- 对类或属性方法加上前置操作
- 实现复用
# 异步
- 执行顺序,一个宏任务里必须执行完所有微任务才会执行下一个宏任务,同级先微任务后宏任务
- 宏任务:定时器、事件绑定、ajax
- 微任务:async、await、promise
# fetch
- 基于 promise,支持 async/await
- 需要进行封装
- 只对网络请求报错,400,500 均为正常
- 默认不会带 cookie,需要配置项。fetch(url, {credentials: 'include'})
- 不支持超时控制。使用 setTimeout 及 promise.reject 的实现的超时控制并不能阻止请求过程继续在后台运行
- 没有办法原生监测请求的进度
# axios
- 支持浏览器和 nodejs 发请求
- 支持 promise
- 客户端支持防 CSRF
- 提供了一些并发请求的接口
- 拦截请求和响应
- 取消请求
- 自动转换 json 数据
- 监测请求的进度
# 多标签页之间的通信
- localStorage,页面 a 使用 localStorage.setItem 存储值,页面 b 监听 storage 事件
- cookie,页面 a 存储值,页面 b 轮询 cookie 值
- websocket,全双工通信,服务器可以主动发数据,页面 a 发送数据到服务器,服务器发送数据到页面 b
- sharedWorker,多页面多线程
# 判断类型
Object.prototype.toString.call(value).slice(8, -1)
- typeof
- 对于原始类型来说,除了 null 都可以显示正确的类型
- 对于对象来说,除了函数都会显示 object
- instanceof
- 可以正确的判断对象的类型,因为内部机制是通过对象 prototype 原型链判断
# 比较运算符 ==
- 对象/数组==字符串,会转换成字符串
- null==undefined,与其他值不相等
- NAN!=NAN
- 对象/数组==数字,会先转换成字符串,然后再转换成数字
- 剩下的都是转换成数字
# 事件代理
- 把原本需要绑定的事件委托给父元素,让父元素监听处理多个子元素
- 事件代理的原理是 dom 事件冒泡,使用事件代理的好处是可以提高性能,节省内存空间,减少事件注册
- 可以实现新增子对象是无需再次对其绑定
# 事件
- 事件冒泡,子元素先触发,父元素后触发
- 时间捕获,父元素先触发,子元素后触发
- dom 事件流,同时支持冒泡和捕获,document.addEventListener(),第三个参数 true 表示捕获,false 表示冒泡
- 阻止冒泡,w3c 使用 stopPropagation(),IE 使用 cancelBubble = true
- 阻止捕获,阻止事件的默认行为,w3c 中使用 preventDefault(),IE 中设置 window.event.returnValue = false
# new 操作符
1,创建一个新对象,并分配给 this 变量,同时继承该函数的原型;2,函数体执行。通常它会修改 this,为其添加新的属性和方法;3, 返回 this
function User(name) { // this = {};(隐式创建) // 添加属性到 this this.name = name this.isAdmin = false // return this;(隐式返回) }
在一个函数内部,我们可以使用 new.target 属性来检查它是否被使用 new 进行调用了。对于常规调用,它为 undefined,对于使用 new 的调用,则等于该函数
function User() { alert(new.target) } // 不带 "new": User() // undefined // 带 "new": new User() // function User { ... }
# ajax
- 创建 xhr 连接
- 设置参数,并打开连接
- 发送请求
- 接收请求
- 优点
- 异步请求提升了用户体验
- 实现局部数据更新
- 缺点
- 安全问题,ajax 暴露了客户端与服务器交互细节
- 对搜索引擎支持弱,不利于 seo
# 内存泄露
- 闭包使用不当,不需要的对象仍存在于内存中
- 死循环
# 垃圾回收
- 在
JavaScript
引擎中有一个被称作垃圾回收器
的东西在后台执行,它监控着所有对象的状态,并删除掉那些已经不可达的 - 如果一个值可以通过引用链从根访问任何其他值,则认为该值是可达的。比方说,如果全局变量中有一个对象,并且该对象有一个属性引用了另一个对象,则 该 对象被认为是可达的。而且它引用的内容也是可达的
- 几个对象相互引用,但外部没有对其任意对象的引用,这些对象也可能是不可达的,并被从内存中删除。
- 固有的可达值的基本集合,这些值被称作
根
(roots
)- 当前执行的函数,它的局部变量和参数
- 当前嵌套调用链上的其他函数、它们的局部变量和参数
- 全局变量
- 定期执行以下“垃圾回收”步骤
- 垃圾收集器找到所有的根,并“标记”(记住)它们
- 然后它遍历并“标记”来自它们的所有引用
- 然后它遍历标记的对象并标记 它们的 引用。所有被遍历到的对象都会被记住,以免将来再次遍历到同一个对象
- 如此操作,直到所有可达的(从根部)引用都被访问到
- 没有被标记的对象都会被删除
# AMD,Commonjs,ES6
- AMD 是异步加载模块,允许指定回调函数
- AMD 推荐通过返回一个对象作为模块对象
- Commonjs 是服务器端的模块规范,加载模块是同步的,只有模块加载完成才能执行后面的操作
- Commonjs 推荐通过 module.exports 或 exports 的属性赋值来达到暴露模块对象的目的
- Commonjs 支持动态导入,例如 require(${path}/xx.js),导出时是值拷贝,
- ES6 中一个模块就是一个文件,改文件内左右的变量和方法外部无法直接获取只能通过 export 输出
- ES6 异步加载,导入导出都指向同一个内存地址
# window.onload 和$(document).ready
- window.onload 必须等页面内所有元素(包括图片)加载完毕后才能执行
- $(document).ready 是 dom 结构绘制完毕后就会执行,不必等到加载完毕
# 判断标准浏览器和 IE 浏览器
- 标准,document.addEventListener,支持冒泡和捕获,事件类型不需要添加 on,添加多个事件正序执行
- IE,document.attachEvent,支持冒泡,事件类型需要添加 on,添加多个事件倒序执行
# event loop
# 事件流
# canvas 图片跨域
# Map and Set(映射和集合)
Map 是一个带键的数据项的集合,就像一个 Object 一样。 但是它们最大的差别是 Map 允许任何类型的键(key)。它的方法和属性如下:
- new Map() —— 创建 map。
- map.set(key, value) —— 根据键存储值。
- map.get(key) —— 根据键来返回值,如果 map 中不存在对应的 key,则返回 undefined。
- map.has(key) —— 如果 key 存在则返回 true,否则返回 false。
- map.delete(key) —— 删除指定键的值。
- map.clear() —— 清空 map。
- map.size —— 返回当前元素个数。
对象仅支持 string/symbol 作为键。Map 还可以使用对象作为键
// 在 Object 中,我们则无法使用对象作为键。在 Object 中使用字符串作为键是可以的,但我们无法使用另一个 Object 作为 Object 中的键 let john = { name: 'John' } let ben = { name: 'Ben' } let visitsCountObj = {} // 尝试使用对象 visitsCountObj[ben] = 234 // 尝试将对象 ben 用作键 visitsCountObj[john] = 123 // 尝试将对象 john 用作键,但我们会发现使用对象 ben 作为键存下的值会被替换掉 // 变成这样了! alert(visitsCountObj['[object Object]']) // 123 // 因为 visitsCountObj 是一个对象,它会将所有 Object 键例如上面的 john 和 ben 转换为字符串 "[object Object]"
Map 使用 SameValueZero 算法来比较键是否相等。它和严格等于 === 差不多,但区别是 NaN 被看成是等于 NaN。所以 NaN 也可以被用作键。
// 1,如果我们想从一个已有的普通对象(plain object)来创建一个 Map,那么我们可以使用内建方法 Object.entries(obj),该方法返回对象的键/值对数组,该数组格式完全按照 Map 所需的格式。 let obj = { name: 'John', age: 30 } let map = new Map(Object.entries(obj)) // Object.entries() 返回一个包含该对象所有 [key, value] 键值对的数组。 alert(map.get('name')) // John // 2,因为 Object.fromEntries 期望得到一个可迭代对象作为参数,而不一定是数组。并且 map 的标准迭代会返回跟 map.entries() 一样的键/值对。因此,我们可以获得一个普通对象(plain object),其键/值对与 map 相同。 let map = new Map() map.set('banana', 1) map.set('orange', 2) map.set('meat', 4) let obj = Object.fromEntries(map.entries()) // 创建一个普通对象(plain object)(*) // 完成了! // obj = { banana: 1, orange: 2, meat: 4 } alert(obj.orange) // 2
Set 是一个特殊的类型集合 —— “值的集合”(没有键),它的每一个值只能出现一次。
- new Set(iterable) —— 创建一个 set,如果提供了一个 iterable 对象(通常是数组),将会从数组里面复制值到 set 中。
- set.add(value) —— 添加一个值,返回 set 本身
- set.delete(value) —— 删除值,如果 value 在这个方法调用的时候存在则返回 true ,否则返回 false。
- set.has(value) —— 如果 value 在 set 中,返回 true,否则返回 false。
- set.clear() —— 清空 set。
- set.size —— 返回元素个数。