一、JavaScript 规定了几种语言类型
两种类型:基本类型和引用类型
基本类型(7种):
String、Number、Boolean、Null、Undefined、Symbol、BigInt
访问:基本类型数据的值按值访问(直接在Stack中取值)
存储:基本类型的数据存放在栈内存(Stack)中
理解一下我不了解的 -> Null && Undefined
- Null 类型只有一个值:null(mdn解释),null 表示一个空值,变量赋值了,只不过赋值为空 转换为数字时:
console.log(Number(null)) // 0
- 一个没有被赋值的变量会有个默认值 undefined(mdn解释),表示变量没有赋值 转换为数字时:
console.log(Number(undefined)) // NaN
两者比较:
console.log(null == undefined) // true
console.log(null === undefined) // false
小意外
console.log(typeof(null)) // object
Null是基本数据类型,为什么打印出来会是object?
据说这是js由来已久的bug,js的数据在底层是以二进制存储,比如null所有存储值都是0,底层的判断机制,只要前三位为0,就会判定为object,所以才会有 typeof null === 'object'
引用类型(1种):Object
Object又包含了 ①Object类型 ②Function类型 ③Array类型 ④RegExp类型 ⑤Date类型 ⑥基本包装类型 ⑦单体内置对象
理解一下我不了解的 -> 基本包装类型 && 单体内置对象
- 基本包装类型:js提供了三个特殊的引用类型(Boolean\Number\String), 为了方便操作基本类型值,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而能够调用一些方法来操作这些数据。例如字符串可以直接.length,调用 charAt(),slice()等,数字可以直接调用toString(),toFixed()等
- 单体内置对象:是ECMAScritp提供的、不依赖于宿主环境的对象,不需要通过new来实例化,可以直接使用。例如Global的decodeURI()、decodeURIComponent(),例如直接Math.各种方法
二、JavaScript 对象的底层数据结构是什么
JavaScript 存储数据使用的是栈(Stack)和堆(Heap)
基本数据类型是值直接存储在栈中,访问直接到栈中取
引用数据类型值存放在堆中,栈中存放着指向值的地址,访问先到栈中取地址,再根据地址到堆中找到数据
const s1 = 'string'
const s2 = 777
const s3 = { key: 'value' }
const s4 = [1]
三、Symbol 类型在实际开发中的应用、可手动实现一个简单的Symbol
mdn解释:symbol是一种基本数据类型。Symbol()函数会返回symbol类型的值,具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持 new Symbol() 语法。
每个从Symbol返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符,这是该数据类型仅有的目的。
我理解一下,Symbol()返回一个独一无二的值,不能通过new调用,可以作为对象属性的标识
const s1 = Symbol()
console.log(s1) // Symbol()
console.log(typeof s1) // symbol
const s2 = Symbol('s2')
console.log(s2) // Symbol(s2)
const s3 = Symbol('s2')
console.log(s2) // Symbol(s2)
s2 === s3 // false
const s4 = Symbol()
const s5 = { [s4]: 's4' }
console.log(s5[s4]) // 's4'
// 可以s5[s4]调用,不能s5.s4调用
四、基本类型对应的内置对象,以及他们之间的装箱拆箱操作
上文中提到过的基本包装类型(Boolean\Number\String)就是基本类型对应的内置对象。
装箱:把基本数据类型转化为对应的引用数据类型,装箱分为隐式装箱和显式装箱。《javascript高级程序设计》中描述每当读取一个基本类型的时候,后台就会创建一个对应的基本包装类型对象,从而让我们能够调用一些方法来操作这些数据。
let s1 = 'Hello, JavaScript'
console.log(s1.slice(7, 17)) // JavaScript
let n2 = 5.67
console.log(n2.toFixed(1)) // 5.7
可以看出虽然是基本数据类型,但是可以调用方法,就是因为js在后台进行了隐式装箱动作,上面代码运行时实际为
let s1 = 'Hello, JavaScript'
console.log(new String(s1).slice(7, 17)) // JavaScript
let n2 = 5.67
console.log(new Number(n2).toFixed(1)) // 5.7
显式装箱
let s1 = 'Hello, JavaScript'
let s2 = new String(s1)
console.log(s1) // Hello,JavaScript
console.log(s2) // String {'Hello,JavaScript'}
拆箱与装箱对应,是装箱的反向操作,把引用类型数据转化为基本类型数据,通常使用valueOf()和toString()方法实现。
let s1 = new String('string')
console.log(s1, typeof(s1)) // String {'string'} 'object'
let s2 = s1.valueOf()
console.log(s2) // string
let s3 = s1.toString()
console.log(s3) // string
let n1 = new Number(123)
console.log(n1, typeof(n1)) // Number {123} 'object'
let n2 = n1.valueOf(n1)
console.log(n2) // 123
五、理解值类型和引用类型
上文中提到 基本数据类型是值直接存储在栈中,访问直接到栈中取
引用数据类型值存放在堆中,栈中存放着指向值的地址,访问先到栈中取地址,再根据地址到堆中找到数据
let s1 = 'str'
let s2 = s1
let n1 = 123
let o1 = {name: 'yi', age: 18}
let o2 = o1
let a1 = [1,2,3]
s2 = 'str2'
o2.name = 'yiyiyi'
console.log(s1) // str2
console.log(o1) // {name: 'yiyiyi', age: 20}
可以看出基本类型数据值直接存在栈中,修改s2不会影响s1的数据,而引用类型的数据值存在堆中,栈中存的是地址,取值时通过地址到堆中寻找,修改o2的值会影响o1
六、至少三种判断 JavaScript 数据类型的方式,以及优缺点,如何准确的判断数组类型
1、typeof
typeof 返回的都是字符串,可以返回string,boolean,number,function,object,undefined
console.log(typeof 1) // number
console.log(typeof 'str') // string
console.log(typeof true) // boolean
console.log(typeof null) // object
console.log(typeof undefined) // undefined
console.log(typeof function() {}) // function
console.log(typeof { name: 'yiyiyi'}) // object
console.log(typeof [1,2,3]) // object
可以看出,typeof 只能判断基础类型数据,引用类型数据包括 null 都会判断类型为 object
2、instanceof
instanceof 可以准确判断引用类型数据,判断不了基本类型数据。instanceof运算符用于测试构造函数的 prototype 属性是否出现在对象原型链中的任何位置,即instanceof右边变量的prototype在左边变量的原型链上就返回true
console.log(1 instanceof Number) // false
console.log(true instanceof Boolean) // false
console.log('str' instanceof String) // false
console.log([1,2,3] instanceof Array) // true
console.log({} instanceof Object) // true
console.log(function() {} instanceof Function) // true
3、constructor
基本数据类型和引用数据类型都可以通过constructor判断,但是如果数据原型被修改,这个方法就失效
console.log((1).constructor === Number) // true
console.log(('str').constructor === String) // true
console.log((true).constructor === Boolean) // true
console.log(([1,2,3]).constructor === Array) // true
console.log(({}).constructor === Object) // true
console.log((function() {}).constructor === Function) // true
4、Object.prototype.toString.call()
es5文档规范定义Object.prototype.toString():Object.prototype.toString()会返回[object,[[class]]]的字符串,其中[[class]]会返回es定义的对象类型,包含"Arguments", “Array”, “Boolean”, “Date”, “Error”, “Function”,“JSON”, “Math”, “Number”, “Object”,“RegExp”,和“String”;再加上es5新增加的返回[objectUndefined]和[object Null]
console.log(Object.prototype.toString.call(1)) // [object Number]
console.log(Object.prototype.toString.call('str')) // [object String]
console.log(Object.prototype.toString.call(true)) // [object Boolean]
console.log(Object.prototype.toString.call(null)) // [object Null]
console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call({})) // [object Object]
console.log(Object.prototype.toString.call([1,2.3])) // [object Array]
console.log(Object.prototype.toString.call(function() {})) // [object Function]
七、可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用
当运算符在计算时,运算符两边数据类型不同,CPU就无法计算,编译器会自动将运算符两边的数据做一个类I型转换,转为类型相同的数据在进行计算。比较运算场景下隐式类型转换比较常见,if判断也比较常见。 比如if判断的 == ,像 0、''、 null、undefined会隐式转为false,避免这种转换可以使用 === 进行强判断
八、出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript 处理大数字的方法,避免精度丢失的方法
-
出现小数精度丢失的原因
计算机的二进制实现和位数限制有些数无法有限表示。就像一些无理数不能有限表示,如 圆周率 3.1415926…,1.3333… 等。JS 遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。
-
js可以存储的最大数字
console.log(Number.MAX_VALUE) // 1.7976931348623157e+308
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
当超过±Math(2, 53) - 1就无法正确计算了
-
js处理大数字的方法
1、使用js新增的基本数据类型bigInt
console.log(11111111111111111 + 22222222222222222) // 33333333333333336
console.log(BigInt(11111111111111111) + BigInt(22222222222222222)) // 33333333333333336n
console.log(BigInt('11111111111111111') + BigInt('22222222222222222')) // 33333333333333333n
直接大数计算会出错,BigInt转换一下就OK了,注意BigInt转换时数字要用'',不然还是会出错
2、使用第三方库: json-bigint