1. 数据类型
JS分两种类型,原始类型和对象类型。 原始类型: 1.boolean 2.number 3.string 4.undefined 5.null 6.symbol 7.bigint 对象类型: 1.Object(Array,RegExp,Math,Map,Set) 2.Function
2. 类型判断
2.1 typeof
原始类型中除了null,都可以用typeof判断。 typeof判断函数为function,其他均为object。
2.2 instanceof
常用于判断对象类型
2.3 Object.prototype.toString
最佳选择,能判断的类型最完整
2.4 isXXX API
isArray(),isNaN()
3. 类型转换
Number(false) // -> 0
Number('1')// -> 1
Number('zb')// -> NaN
(1).toString()// '1'
转布尔值规则: 1.undefined、null、false、NaN、”、0、-0都转为false 2.其他所有值都转为true,包括所有对象 转数字规则: 1.true为1,false为0 2.null为0,undefined为NaN,symbol报错 3.字符串看内容,如果是数字或者进制值就正常转,否则就NaN 4.对象的规则隐式转换再讲
4. this
4.1 普通函数
谁调用this指向谁
var c = new foo()
c.a = 3
console.log(c.a) // this绑定到c上,不会被任何方式修改
使用apply,call,bind改变this,优先级仅次于new
4.2 箭头函数
箭头函数的this取决于定义时的环境
5. 闭包
定义:加入一个函数能访问外部的变量,那么这个函数它就是一个闭包。 闭包会将访问的变量存放在内部对象[[Scopes]]上,因此可以访问到本该销毁的变量 局部变量才是被存储在栈上,全局变量存在静态区域上,其他都存在堆上。(只针对Chrome)
6. new
new可以构建出一个实例,并绑定上this,执行步骤如下: 1.新生成一个对象 2.对象连接到构造函数原型上,并绑定this 3.执行构造函数代码 4.返回新对象 当构造函数中返回一个对象时,内部创建的新对象会被我们返回的对象所覆盖,所以构造函数一般来说不返回对象。
7. 作用域
作用域有三类: 1.全局作用域 2.函数作用域 3.块级作用域 [[Scopes]],定义时就被确定下来,后续不会改变
8. 原型
原型更重要引出继承,概念: 1.每个对象都有一个_proto_指向一个对象,就是原型 2.每个对象的原型都可以通过constructor访问构造函数,构造函数通过prototype访问原型 3.所有函数都可以通过_proto_找到Function对象 4.所有对象都可以通过_proto_找到Object对象 5.对象直接通过_proto_连接起来为原型链,顶层Object对象的原型为null
9. 继承
继承中的class,本质上还是一个函数。 ES5与6继承的区别: 1.ES6继承的子类需要调用super()才能拿到子类,ES5的话是通过apply这种绑定的方式 2.类声明不会提升,和let这些一致
function Super() {}
Super.prototype.getNumber = function() {
return 1
}
function Sub() {}
Sub.prototype = Object.create(Super.prototype, {
constructor: {
value: Sub,
enumerable: false,
writable: true,
configurable: true
}
})
let s = new Sub()
s.getNumber()
10. 深浅拷贝
10.1 浅拷贝
浅拷贝:第一层引用不同 通过assign和扩展运算符实现浅拷贝
let a = {age:1}
let b = Object.assign({},a)
b = {...a}
10.2 深拷贝
深拷贝:所有引用不同 使用JSON.parse(JSON.stringify(object))可以实现,不过存在不少缺陷。 也可以通过递归实现深拷贝:
// 利用 WeakMap 解决循环引用
let map = new WeakMap()
function deepClone(obj) {
if (obj instanceof Object) {
if (map.has(obj)) {
return map.get(obj)
}
let newObj
if (obj instanceof Array) {
newObj = []
} else if (obj instanceof Function) {
newObj = function() {
return obj.apply(this, arguments)
}
} else if (obj instanceof RegExp) {
// 拼接正则
newobj = new RegExp(obj.source, obj.flags)
} else if (obj instanceof Date) {
newobj = new Date(obj)
} else {
newObj = {}
}
// 克隆一份对象出来
let desc = Object.getOwnPropertyDescriptors(obj)
let clone = Object.create(Object.getPrototypeOf(obj), desc)
map.set(obj, clone)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key])
}
}
return newObj
}
return obj
}
11. Promise
高频考点,涉及API,问到all,race。 then,catch,finally都为微任务。其他代码都是宏任务(同步执行) 这些微任务在Promise状态为非pending时加入队列。 同级then下,前一个then执行完后,后面的才会加入任务队列。 最开始调用的then会首先依次加入任务队列。 同一个promise的每个链式调用的开端会首先依次进入队列。
11.1 async/await
async/await(ES8) 阻塞await后面的内容。
12. 事件循环
异步只是延迟执行同步代码。其他线程不影响(Web worker)。 Task(宏任务):同步代码、setTimeout回调、setInterval回调、IO、UI交互事件、postMessage、MessageChannel。 MicroTask(微任务):Promise的回调,Mutation observer回调,queueMicrotask回调。 执行顺序如下: 1.执行同步代码 2.执行完所有同步代码后且执行栈为空,判断是否有微任务需要执行 3.执行所有微任务且微任务队列为空 4.是否有必要渲染页面 5.执行一个宏任务
13. 模块化
CommonJS,ES6的ESM
14. 垃圾回收
分两个空间,新生代和老生代。
14.1 新生代
使用Scavenge GC算法 也分两个部分,From空间,To空间。 From占满时,启动算法检查存活的对象复制到To空间中,会和To空间互换。
14.2 老生代
使用标记清除和标记压缩算法 经历过Scavenge算法后还存活,To空间对象占比大小超过25%,会将新生代移到老生代。 启动标记清除算法条件: 1.某一个空间没有分块 2.空间中被对象超过一定限制 3.空间不能保证新生代中的对象移动到老生代中 标记活对象,销毁没被标记的对象
15. 其他
0.1+0.2 !== 0.3 浮点数用二进制表示的时候是无穷的,因为精度的问题,两个浮点数相加会造成截断丢失精度,因此再转换为十进制就出了问题。