# JS 基础和核心问题
# 判断数据类型
- typeof 用于判断基本数据类型,引用数据类型和 null 都判为 object
- instanceof 判断实例对象的 prototype 如 a instanceof A 判断 a 是否由 A new 产生
- constructor 检测不出 undefined 和 null; 使用方法 str.constructor == String ; true
- toString.call (待测值) toString 是 Object 原型对象的一个方法,根据原型链往上找 如函数 fn --> fn._proto_(Fn.prototype) --> Fn.prototype._proto_ (Object.prototype)
- jQuery 内置的方法
# 浅拷贝 vs 深拷贝
浅拷贝只拷贝一层,实际上指向同一个地址;
1 | let newArr = [...oldArr] |
深拷贝拷贝全部,将数组内的对象也拷贝,开辟了新地址;
// 四种深拷贝
1. 递归
2.Object.create()
3.jQuery 的 $.extend
4. 导入 lodash 使用 cloneDeep
# 递归实现深拷贝
1 | //递归实现深拷贝 |
# 闭包
在函数内调用函数外部的变量,创建私有变量
-
手写一个闭包,能记录函数调用的次数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14function Fn(){
let i = 0;
return ()=>{
i++;
console.log(`调用了${i}次`)
}
}
const f = new Fn()
f(); //> "调用了1次"
f(); //> "调用了2次"
f(); //> "调用了3次"
f = null //利用标记清除 回收内存
原理: 函数在作用找不到变量,顺着父级作用域往上找。 函数作用域只与创建时的位置有关,和调用时无关,如:
1 | let a = "全局变量a" |
# 完全理解 call (),apply (),bind ()
三者都用于改变函数传参和 this 指向,注意 箭头函数 无法改变 this
fn.call('新的this对象','参数1','参数2','参数3'..)
fn.apply(''新的this对象',['参数1','参数2','参数3'])
fn.bind('新的this对象','参数1','参数2','参数3'..)() //bind返回的是一个新的函数,传参成为固定参数
1 | function oldFn(a,b){ |
# 暂时性死区
一句话:一进入当前作用域,变量就已经存在了,但不可获取,只有等到声明后才能获取和使用,作用是防止在变量声明前就使用,减少运行时的错误
var 存在变量提升,所以声明前访问,返回 undefined 而不报错
1 | function fn1(){ |
# JavaScript 执行机制 —— 事件循环
# 异步?
JavaScript 是一门单线程语言,没有异步,一切异步都是以同步的方法模拟的!
# 事件循环 Event Loop
event loop 它最主要是分三部分:主线程、宏队列(macrotask)、微队列(microtask)
1 | console.log('1'); |
** 宏任务(Macrotasks):**js 同步执行的代码块,setTimeout、setInterval、XMLHttprequest、setImmediate、I/O、UI rendering 等。
** 微任务(Microtasks):**promise、process.nextTick(node 环境)、Object.observe, MutationObserver 等。
浏览器在执行 js 代码过程中会维护一个执行栈,每个方法都会进栈执行之后然后出栈(FIFO)。与此同时,浏览器又维护了一个消息队列,所有的异步方法,在执行结束后都会将回调方法塞入消息队列中,当所有执行栈中的任务全部执行完毕后,浏览器开始往消息队列寻找任务,先进入消息队列的任务先执行。
执行完一次宏任务后,检查任务队列微任务,执行完后进入下一次宏任务执行
宏任务和微任务的总结: 宏任务 Macrotasks 就是参与了事件循环的异步任务。 微任务 Microtasks 就是没有参与事件循环的 “异步” 任务。
# Node 环境的 JS 执行机制
node 环境下 js 事件循环机制与 浏览器环境下有出入,不过随着 node 版本的更新,正在不断规范。
nod 执行栈 -> 宏任务 -> 宏任务 -> 微任务 -> 微任务
浏览器环境:执行栈 -> 宏任务 -> 微任务 -> 宏任务 -> 微任务