前端八股文总结——数据结构

196 阅读6分钟

数据类型

基础类型:Number,String,Boolean,undefined,null
存放位置:栈
引用数据:Object
存放位置:堆

判断方式

1.typeof
缺点:无法判断null,因为js最初的机制导致将 null 的标签值是0x00,而对象的类型标签是 0,所以typeof判断null为Object。

typeof 111 //"number"
typeof NaN //"number"
typeof undefined //"undefined"
typeof null //"object"

2.instanceof
缺点:只能判断实例关系,因为 null 和 undefined 无构造函数,因此无法判断

let str = new String()
str instanceof String  //true
'1111' instanceof String //false

instaceof原理:查找原型链,实例的_prop_等于构造函数的prototype。
instance.prop = instance.constructor.prototype

function myInstanceof(left,right) {
  if (left === undefined || left === null) throw Error("参数不能为null或undefined")
  let L = left._prop_
  let R = right.prototype
  while(true) {
    if (L===null) return false
    if (L===R) return true
    L = L._prop_
  }
}

3.constructor
缺点:无法判断 null 和 undefined

'111'.constructor // ƒ String() { [native code] }
NaN.constructor // ƒ Number() { [native code] }

4.Object.prototype.toString.call() 最完美的方法

Object.prototype.toString.call("1111") //'[object String]'
Object.prototype.toString.call(NaN) //'[object Number]'
Object.prototype.toString.call(undefined) //'[object Undefined]'
Object.prototype.toString.call(null) //'[object Null]'

Object.prototype.toString 和数组的 toString() 为什么不一样
每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 "[object type]"。

隐式转换

操作符的隐式转换

一元运算符
+

  1. 隐式转换为number类型;
  2. 与字符串运算时起到连接的作用。
+"1"  // 1
"a"+"b"   // "ab"
NaN+undefined   // NaN
NaN+null  // NaN
NaN+"ab"   // NaNab

-

  1. 将字符串隐式转为数字,非数字转为NaN,运算结果为NaN;
  2. 将数字变成负数。

undefined 转为 NaN,null 转为 0

-"a"  // NaN
-undefined  // NaN
-null  // -0
-NaN  // NaN
"a"-"b"  // NaN
"a"-null  // NaN
3-null  // 3

比较运算符
==和===

  1. 对于string,number等基础类型,双等和三等是有区别的。不同类型间比较,双等之比较“转化成同一类型后的值”看“值”是否相等,===如果类型不同,其结果就是不等;同类型比较,直接进行“值”比较,两者结果一样。
  2. 对于Array,Object等高级类型,双等和三等是没有区别的进行“指针地址”比较。
0 == undefined  // false
0 == null // false
0 == NaN //false
+0 === -0 // true
NaN == NaN // false
undefined == null // true  因为undefined派生自null
undefined === null // false
undefined == NaN // false
null == NaN // false

>和<

  1. 非数值转为数值再进行比较
  2. 如果两边都是非数字,比较字符编码时进行按位比较,比较Unicode编码
3 < "5"  // true
3 < "accc"  // false
"abbbbb">"baaaaaa"  // false
"3" < NaN  // false
undefined > null // false
undefined < null // false
undefined < NaN // false
undefined > NaN // false 
null < NaN // false
null > NaN // false

数组

常用方法
  1. shift():删除数组第一个元素,返回删除的值,改变数组
  2. unshift():向数组的开头添加一个或多个元素,返回新的长度,改变数组
  3. push():向数组末尾添加元素,返回新的长度,改变数组
  4. pop():删除数组末尾元素,返回删除的值,改变数组
  5. concat():连接两个数组,返回合并后的数组,不合并相同内容,不改变数组

检测是否是数组的方法
1. Array.isArray()
2. Object.prototype.toString.call()
3. instanceof
4. constructor

let a = [1,2,3]
Array.isArray(a) // true
Object.prototype.toString.call(a) // [Object Array]
a instanceof Array // true
a.constructor // ƒ Array() { [native code] }
数组转字符串方法
  1. join()
  2. toString()

字符串转数组
1. 扩展运算符
2. split()
3. Array.from()

截取数组
  1. slice(start,end) 数组中被选中的元素 不改变原数组
  2. splice(index, num, item1, ....., itemX) 返回删除的项目,改变数组
let a = [1,2,3]
a.slice(1,2) // [2]
a // [1,2,3]
let a = [1,2,3]
a.splice(0,1) // [1]
a // [2,3]
拉平数组
  1. flat():拉平数组,回跳过空位,不改变数组;
  2. flatMap():拉平数组并执行map()方法,不改变数组;
// 实现flat
function myFlat (arr,num = 1) {
  return num > 0 ? arr.reduce((pre,cur) => {
    pre.concant(Array.isArray(cur) ? myFlat(cur,num-1) : cur)
  },[]) : arr.slice()
}
遍历数组
  1. map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。可以遍历symbol属性。
  2. filter():ES5 跳过空位,ES6 空位处理为undefined 返回一个符合func条件的元素数组
  3. some((currentValue,index,arr)=>{},thisArg):ES5 跳过空位,ES6 空位处理为undefined 返回一个Boolean,判断是否有元素符合func条件
  4. every():ES5 跳过空位,ES6 空位处理为undefined 返回一个Boolean,判断每个元素是否符合func条件
  5. reduce():ES5 跳过空位,ES6 空位处理为undefined

共同点
1. 只能遍历数组;
2. 依次遍历数组中的每一项;
3. 都支持三个参数【item,index,arr】。
不同点
1. forEach 没有返回值,不做处理返回undefined。
2. map 有返回值,不做处理返回对应长度的 undefined 数组,return 不会改变原数组。

对象

属性

value:值
configuable:是否可配置,false后不能改为true
enumable:是否可枚举
writable:是否可修改

禁止对象扩展的方法

object.preventExtensions() 不可扩展
object.seal() 密封对象
object.freeze() 冻结对象

检验方法
Object.preventExtension()xObject.isExtensioble()
Object.seal()xxObject.isSealed()查看对象的[[configurable]]属性(false)
Object.freeze()xxx查看对象的[[writable]]属性(false)

Set

特性

SET【值:值】(集合): 类似于数组,无重复值,可以数组去重。键值与键名相同。

new Set() 可以接受一个数组或其他具有 iterable 接口的数据结构

方法
  1. size()
  2. add(value)
  3. delete(value)
  4. has(value):返回true或false
  5. clear()
  6. forEach():无返回值
  7. keys():返回键,返回遍历器
  8. values():返回值,返回遍历器
应用
  1. 数组去重
  2. 求交集和并集

Map

键值类型

任何值

方法
  1. size()
  2. set(key,value)
  3. get(key)
  4. has(key)
  5. clear()
  6. keys():返回键名
  7. values():返回数值
  8. forEach():遍历所有成员
应用

数据储存

WeakMap

键值类型

Object 类型

特点

可被垃圾回收机制回收【引用后】,key不可被枚举,不可遍历【因为垃圾回收后weakMap的成员数量会变动,而垃圾回收是不可预测的】

方法
  1. set(key,value)
  2. delete(key)
  3. has(key)
  4. get(key)
应用
  1. 封装private Data
  2. 管理DOM listerner

传统使用 jQuery 的时候,我们会通过 $.data() 方法在 DOM 对象上储存相关信息(就比如在删除按钮元素上储存帖子的 ID 信息),jQuery 内部会使用一个对象管理 DOM 和对应的数据,当你将 DOM 元素删除,DOM 对象置为空的时候,相关联的数据并不会被删除,你必须手动执行 $.removeData() 方法才能删除掉相关联的数据,WeakMap 就可以简化这一操作。

  1. Cache

当我们需要关联对象和数据,比如在不修改原有对象的情况下储存某些属性或者根据对象储存一些计算的值等,而又不想管理这些数据的死活时非常适合考虑使用 WeakMap。

  1. 深浅拷贝解决循环引用

Symbol

Symbol 特性
  1. 通过Symbol() 函数生成,不可以通过 new 调用
  2. typeof(Symbol("aaa"))// "symbol"
  3. instanceof 结果为 false,因为symbol并不是通过new创建的
  4. Symbol 函数接受一个字符串或对象作为参数,参数为对象时去调用toString()方法生成Symbol
  5. Symbol 函数参数相同返回值也不同
  6. Symbol 不能与其他类型进行计算,会报错。
  7. Symbol 只能被 Object.getOwnPropertySymbols() 获取到
  8. 可以被map遍历到,for..in 和 for,Object.keys() 无法遍历
Symbol 方法

Symbol.for(key):根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol,如果找到了,则返回它,否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中。

Symbol 应用
  1. 定义私有变量或方法
  2. 运用到单例模式中【全局只有一个实例】