变量和类型
javascript 规定了几种语言类型?
Undefined、Null、Boolean、String、Number、Symbol、Object、Function
简单类型都不是对象
undefined
表示未定义,任何变量赋值前都是 undefined 类型,值为 undefined (而不是 null)
undefined 是一个变量,void(0) 返回 undefinednull
只有一个值,表示空值,是关键字,typeOf(null) 返回 object
String
字符串 UTF16编码,最大长度 253-1
Number
typeof(NaN) 和 typeof(Infinity) 都返回 number
NaN != NaN
Infinity / Infinity = NaNSymbol
表示独一无二的值,是一切非字符串的对象key的集合。
Symbol(参数):可以接受一个字符串参数,表示对 Symbol 实例的描述。
1 | Symbol(1) === Symbol(1) // false |
Object
为什么给对象添加的方法能用在基本类型上?
1 | Symbol.prototype.hello = () => { console.log('hello')} |
运算符提供了装箱操作,会根据基础类型构造一个临时对象,使得基础类型可以调用对象的方法
- Function
装箱和拆箱
在 JavaScript 中,没有任何方法可以更改私有的 Class 属性,因此 Object.prototype.toString 是可以准确识别对象对应的基本类型的方法,它比 instanceof 更加准确。
装箱过程使用 object 函数
拆箱转换使用 valueOf、toString,如果valueOf 和 toString都不存在,或者没有返回基本类型,则会产生TypeError。
JavaScript对象的底层数据结构是什么?
所有 js 对象都是基于 HeapObject 的类生成的,对象的存储结构:
- 头部信息
- 属性块,k-v
- 元素块
函数 function 类型本身也具有对象化的能力
函数 function 与对象 object 超然的结合能力
数组应该算是线性数据结构,线性数据结构一般有一定的规律,适合进行统一的批量迭代操作等,有点像波。
而对象是离散数据结构,适合描述分散的和个性化的东西,有点像粒子。
JavaScript里的对象到底是波还是粒子?
波粒二象性
javascript 对象是 字典
结构
Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol?
- 使用Symbol来作为对象属性名(key)
必须用 对象[key]
,不能使用 对象.key
Symbol类型的 key 是不能通过 Object.keys() 或者 for…in 来枚举
JSON.stringify(obj) Symbol 类型也会被排除
获取 Symbol定义的对象属性:
Object.getOwnPropertySymbol(obj)
Reflect.ownKeys(obj)
使用Symbol替代常量
使用Symbol定义类的私有属性、方法
可以定义枚举
1 | let levels = { |
Symbol.for(‘foo’):定义使用同一个Symbol
原型和原型链
实现继承的几种方式以及他们的优缺点?
- 原型链继承
1 | // 将子类的原型链指向父类的对象实例 |
优点:继承构造函数的属性,父类构造函数的属性,父类原型的属性
缺点:无法向父类构造函数传参,且原型对象的引用属性是所有实例共享的,即属性没有私有化,原型上属性的改变会作用到所有的实例上。
构造函数继承
实现:子类构造函数使用 call、apply 劫持父类构造函数方法,并传参
原理:call、apply 更改子类函数的作用域,执行父类构造函数,因此子类可以继承父类公有属性
1 | function D(name, id){ |
优点:解决原型链继承缺点,可以实现多继承
缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
组合继承
1 | function E(name, id){ |
缺点:执行两次父类的构造函数,消耗内存,子类的构造函数会代替原型上的父类构造函数
原型式继承
1 | var parent = { |
缺点:共享应用类型
寄生式继承
原理:二次封装原型式继承,并扩展
1 | function createObj(obj){ |
寄生组合式继承
1 | function inheritPrototype(subClass, superClass) { |