有哪些数据类型 区别
八种 string,number,null,undefined,symbol,object,bigint,boolen 区别 存储位置不同 原始数据在堆中 引用类型在栈中
数据类型检测的方式
- typeof 只能识别原始类型
- constructor
let a = []
a.constructor
- instanceof 缺点:多个窗口之间 比如当前是页面A通过iframe嵌入了页面B 此时 页面A的实例 !== B页面产生的构造函数实例 A的数组 instanceof B的Array 有可能返回false 判断对象类型
obj instanceof Type
- Object.prototype.toString 缺点:toString是个原型链上的方法 有可能会被重写(知道就行)
Object.prototype.toString = function(){
return 213
}
Object.prototype.toString.call(123); // "[object Number]"
判断数组的方式
- instanceof
- constructor
- isArray Array.isArray(arr)
- Object.prototype.toString.call()
- isPrototypeOf() 判断对象是否在另一个对象原型链上
var a = {}
Object.prototype.isPrototypeOf(a)
//true
- Object.getPrototypeOf 返回原型对象进行比较
Object.getPrototypeOf(arr) === Array.prototype
null和undefined的区别
null
表示特指对象未设置
是字面量属性 不是全局对象的一个属性
数字计算中null会转为0
undefined
表示缺失值 此处应该有个值但是还未定义
是全局对象的一个属性
数字计算中计算结果会变成NaN
instanceof操作符实现原理及实现
通过查找原型对比
function myInstanceof(left, right) {
let p = Object.getPrototypeOf(left)
let l = right.prototype
while(true){
if(!p)return false
if(p===l)return true
p = Object.getPrototypeOf(p)
}
}
==操作符的强制类型转换规则
- 有一个是布尔 先转为数字
- 字符串==数字 布尔 null 字符串转为数字
- 对象==字符串 数字 符号 对象转为原始值
对象转换原始值 先用valueof 如果valueof还是返回对象 在调用tostring方法 如果没有这两个方法 或者返回不是基本类型的值就抛出TypeError - undefined == null 返回true
- Nan==Nan false
- 0=='非空字符串' 0转为字符串
- 其余情况转为数字
Object.is() 、比较操作符 ===、== 的区别
Object.is()与===之间的主要区别在于它们如何处理NaN和-0。
- 对于
NaN,Object.is(NaN, NaN)返回true,而NaN === NaN返回false。 - 对于
-0和+0,Object.is(-0, 0)返回false,而-0 === 0返回true。
Object.is()与==之间的主要区别在于:
-
==运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换(这种行为将"" == false判断为true) -
Object.is不会强制转换两边的值。
new操作符
- 创建一个新对象
- 把这个对象绑定到类的原型链上
- 调用类构造函数改变this指向到新对象上
- 判断类执行完后有没有返回值 没有的话再返回这个新对象
数组有哪些方法
- Array.isArray()
- concat()
- toString(),join()
- pop(),push() pop返回被删除的值,push返回添加后的长度
- shift(),unshift() shift返回被删除的值,unshift返回添加后的长度
- reverse(),sort() 改变原数组
- slice() 返回被切割的值 这个值是从原数组上浅拷贝来的 修改对象里的值会影响到原数组
8. splice()会改变原数组
9. indexOf(),includes(),find(),findIndex()
10. forEach(),map(),filter(),every(),some()
11. reduce() reduce第一个参数为回调函数 第二个值为初始值 如果没写第二个值 数组第一个值会被作为初始值 整个回调将从数组第二个值开始
12. flat()
什么是类数组 怎么转为数组
- 函数参数对象
- 用getElementsByTagNmae/Classname/name获得的HTML节点
- 用querySelector获得的NodeList
转为数组
- Array.prototype.slice.call(arguments) 用call bind apply方法
- Array.from
- [...arguments] 扩展运算符
- function(...args){} 函数接受参数时使用
为什么变量提升 产生了什么问题
- 提升性能 让函数可以在执行时预先分配栈空间
- 提高代码容错 让一些不规范的代码也能正常执行
变量提升导致变量作为全局变量 会干扰一些正常的操作
var tmp = new Date();
function fn(){
console.log(tmp);
if(false){
var tmp = 'hello world';
}
}
fn();// undefined
/** * 在这个函数中,原本是要打印出外层的tmp变量,
但是因为变量提升的问题
, * 内层定义的tmp被提到函数内部的最顶部,
相当于覆盖了外层的tmp,所以打印结果为undefined。 */
箭头函数和普通函数的区别
- 定义形式不同
- 箭头函数全是匿名函数
- this指向不同 普通函数指向调用者 没有就是window 箭头函数始终指向定义时的上下文
- 箭头函数没有prototype属性
- 箭头函数不能用于构造函数 不能被new new时 需要把新对象的__proto__绑定到prototype上 箭头函数没有 箭头函数无法改变this指向 无法执行构造函数的内容
- 箭头函数没有arguments对象
作用域和作用域链
从存储上来说 作用域本质上是一个对象 作用域中的变量可以理解是该对象的成员 作用域就是代码的执行环境 全局作用域就是全局执行环境 局部作用域就是函数的执行环境 都是栈内存
作用域分为全局作用域和局部作用域 局部作用域只包含函数作用域 ES6的const let提供了块级作用域
函数作用域就是函数定义产生出来的作用域 块级作用域 通过const let 声明
- 在函数内部
- 在代码块 花括号捏 注意点
- 不会进行变量提升
- 禁止同一声明
- for循环()内部 ()之内会建立一个隐藏的作用域 该作用域不属于for后面的{}中 并且只有for后边{}产生的块级作用域才能访问这个隐藏的作用域
作用域链
多个作用域对象连续引用形成的链式结构
使用方面 使用一个变量时 会尝试在当前作用域下去寻找改变量 没找到再往上层作用域寻找 以此类推直到找到或者到全局作用域还没找到 那么就报错
存储方面 作用域链以数组的形式存储 第一个索引对应的是函数本身的执行上下文 就是当前执行代码所在环境的变量对象 下一个索引对应的空间存储是该对象的外部执行环境 以此类推 直到全局执行环境
var a = 10
function fun() {
console.log(a)
}
function show(f) {
var a = 20
(function() {
f() //10,而不是20; 函数的作用域是在函数定义的时候就被决定了,与函数在哪里被调用无关
})()
}
show(fun)
变量查找机制
- 变量只能在特定的区域内才能被访问 外部环境不能访问内部的任何变量和函数 可以向上搜索但不可以向下搜索
- 可以减轻命名压力
浏览器垃圾回收
- 标记法
- 引用计数法
标记法
- 从根节点出发 window 文档dom树
- 遍历节点确认那些对象是可达 那些对象不可达
- 回收垃圾 整理内存
对象分为临时对象和长期对象
临时对象
- 函数内部的声明 块级作用域的变量 当函数执行完事 会销毁里面的对象
长期对象
- webapi
- window
- dom
对于不同的对象 分为主垃圾回收器和副垃圾回收器
主垃圾回收器
-
对象占用空间大
-
对象存活时间久
-
先标记->遍历
-
垃圾清理 释放内存空间
-
会产生大量不连续的内存碎片 需要进行内存整理
副垃圾回收器 通常只支持1~8m的内容 新生代的垃圾回收 分为两个区域 一半对象区 一半空闲区
- 先标记
- 把对象区使用中的对象复制到空闲区 做一次有序排列
- 复制完后 把空闲区和对象区进行对调
回收时机
- cpu空闲时回收
- 分代回收 多回收新生代 少回收老生代
- 增量回收 把垃圾回收工作分成小块 每次处理一部分 多次处理