1. js数据类型,有几种是新增的?
undefined null boolean number string object
symbol bigInt -- 新增
symbol 独一无二 且 不可变更 => 用来解决全局变量的冲突和内部变量覆盖
bigInt 用于解决大数据类型,精度问题的优化,可以安全的存储和操作任意精度整数
2. 基础数据类型通常会如何进行分类?使用起来有什么区别?使用过程中你是如何区分他们的?
可以分为:原始数据类型 + 引用数据类型
原始数据类型: undefined null boolean number string
引用数据类型: 对象、数组、函数
效果不同:
原始数据类型直接赋值后,不存在引用关系
属性引用关系
存储位置不同:
栈: 原始数据类型 =>先进后出栈维护结构 =>栈区由编译器自动分配释放 => 临时变量方式
堆: 引用数据类型 =>堆内存由开发者进行分配=>直到应用结束
原始数据放置在栈中,空间小、大小固定、操作频繁
引用类型数据量大、大小不固定,赋值给的是地址
3. 数据类型转换
1).isNaN 和Number.isNaN 的区别
isNaN 包含了一个隐式转化。
isNaN =>接收参数会先尝试参数转成数值型,不能被转数值的参数 返回true,非数字值传入返回true
Number.isNaN => 接收参数会先判断参数是否为数字再判断是否为NaN => 不会进行数据类型转换
2). 其他的类型转换场景
转换成字符串:
Null 和 Undefined =>'null''undefined "
Boolean =>'true''false
Number =>'数字’大数据 会转换成带有指数形式
Symbol =>'内容"
普通对象 =>'[0bject 0bject]'
转成数字:
undefined => NaN
Null => 0
Boolean =>truel1 false0
String =>包含非数字的值NaN 空 0
Symbol => 报错
对象 =>相应的基本值类型 => 相应的转换
转成Boolean:
undefined | null | false | +0-0 | NaN | "" => false
4. 原始数据类型如何具有属性操作?比如字符串.length 等操作
js的包装类型,原始数据类型在调用方法和属性时,js会在后台隐式地将基本类型转换成对象。
let a = 'hello'
a.length // 5
js 在收集阶段 Object(a) String { 'hello' }
// 去包装
let a = 'hello'
let b = Object(a)
let c = b.valueOf() // 'hello'
5. 数组方法
转换方法: toString() toLocalString() join()
尾操作: pop() push()
首操作: shift() unshift()
排序: reverse() sort()
连接: concat()
截取: slice()
插入: splice()
索引: indexOf()
迭代方法: every() some() filter() map() forEach()
归并: reduce()
6. 变量提升 & 作用域
现象:无论在任何位置声明的函数、变量,都被提升到模块、函数的顶部
js实现原理:
解析 / 执行,检查语法、预编译,代码中即将执行的变量核函数声明调整到全局顶部,并且赋值为 undefined,再解析上下文,arguments,函数参数,再统一执行预编译。
就产生了全局上下文(变量定义,函数声明),和函数上下文(变量定义,函数声明,this,arguments)
再去执行阶段,按照代码顺序从上而下逐行运行。
变量提升存在的意义?
提高性能,先提升,编译解析,后面多次使用到该变量或方法,就只需要去头部找到就可以,无需重复解析,这样就会更加灵活,允许补充定义,规避掉一些不必要的报错。
注意:let 和 const 不支持变量提升,会报错
7. 闭包
什么是闭包,闭包的作用?
在一个函数中访问另一个函数作用域中变量的方法。函数外部可以访问到函数内部的变量。跨作用域可以创建私有变量,但是已经运行结束的逻辑,依然会残留在闭包里,使得变量得不到回收,占据内存,容易造成内存泄漏,需要在使用完成时释放掉,将其赋值为null。
题目:闭包改造for循环
for(var i = 0; i< 9; i++) { // let就不会有问题,因为每次循环相当于一个块级作用域,没有变量提升
setTimeout(() => {
console.log(i)
}, i * 1000)
}
// 利用闭包改造
for(var i = 0; i< 9; i++) {
((j) => {
setTimeout(() => {
console.log(j)
}, i * 1000)
})(j)
}
8. JS ES内置对象有哪些?
内置对象
值属性类: Infinity,NaN,undefined,null
函数属性: eval(),parseInt()
对象: 0bject,Function,Boolean,Symbol,Error
数字: Number,Math,Date
字符串: String,RegExp
集合: Map,set,weakMap
抽象控制: promise
映射: proxy
9. instance 区分数据类型的原理?
获取需要验证的_proto,依次在传入类型的构造函数的原型链上查找是否匹配得上
eg: 2 instanceof Number // true
拿“2” 的原型,在 "Number" 的原型链上进行遍历匹配,匹配成功就返回true,否则返回false
同理可得:
利用构造函数可以判断数据类型:
(2).constructor === Number
([]).constructor === Array
问题:用 constructor 的隐患?
constructor代表的是构造函数指向的类型,是可以被修改的。
判断数据类型还可以用 Object.prototype.toString.call()
eg: let a = Object.prototype.toString
a.call(2) a.call([])
10. 原型 & 原型链
构造函数:是用来新建一个对象的,构造函数内部 有一个属性叫 prototype
prototype 是一个对象,包含共享属性和方法。
使用构造函数创建对象后,对象内部会存在一个指针(__proto__), 这个指针指向构造函数prototype属性的对应值。
链式获取属性规则:对象在获取属性时会优先在自身属性查找,若找不到则会去原型链上继续找,(每个对象都有自己的【__proto__】顺序:对象本身 =>原型对象 =>直到null 终止查找)如果想要跳过自身属性使用原型链的属性,就可以使用 Object.prototype.toString.call() (见上条)。
11. 异步编程
回调函数 => cb 回调地域
promise => 链式调用,语义不明确
generator => 考虑如何控制执行 co库
async await => 不改变书写习惯,实现看似同步的处理异步代码
promise 手写promise
promise是一个对象,来触发操作
有三种状态: pending resolved rejected
两个过程:pending => resolved / pending => rejected
promise缺点:无法取消,pending无细分状态
12. 内存和浏览器执行
浏览器输入地址后发生了什么?link:https://blog.csdn.net/weixin_54714100/article/details/136320335?spm=1001.2014.3001.5502
垃圾回收概念:
js具有自动的垃圾回收机制,会找到不再使用的变量,释放其占用的内存空间
两种变量的回收方式:
局部变量:块级作用域内有效,函数执行完毕并后续不再使用该函数时,释放局部变量,闭包关联的变量无法释放,需手动释放。
全局变量:全局有效,使用完才会回收。
现代浏览器如何处理垃圾回收?
标记清除
存储在内存中的所有变量都会有一个 环境状态 的标记(进入环境,执行环境,离开环境),定期遍历标记参数,销毁带有 离开环境 的变量,释放内存
引用计数
跟踪每个值被引用的次数,当一个执行态被使用完毕,就计数减一,直到引用次数变为0,就表示该变量不会再被使用,释放其内存。
减少垃圾的方案
数组优化:清空数组时,赋值一个 空数组 [] length = 0
object 优化:对象尽量复用,减少深拷贝
函数优化:循环中的函数表达式,尽量统一放在外部,这样不必每次解析。