前言
题目来自ConardLi的blog
写的是自己的题解,水平有限,所以仅供参考
代码会整合在github,觉得有帮助就给个star吧~
正文
一、Javascript基础
变量和类型
1、JavaScript规定了几种语言类型
七种
基本类型:null,undefined,symbol,string,number,boolean
对象类型:object
最近出了一个bigInt类型
2、JavaScript对象的底层数据结构是什么
堆
引用类型的数据的地址是存放在栈里
3、Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol
- Symbol类型可以用作私有变量
例如:
const name = Symbol('name')
class People {
construct(n) {
this[name] = n
}
sayName() {
console.log(this[name])
}
}
我们使用闭包主要是保护这个 Symbol ,它无法直接在闭包外面访问。这样除了使用 Object.getOwnPropertySymbols()
之外我们无法访问 this[name]
属性,基本可以认为是私有的。
- 定义一组常量,保证这组常量的值都是不相等的
const COLOR_RED = Symbol();
const COLOR_GREEN = Symbol();
function getComplement(color) {
switch (color) {
case COLOR_RED:
return COLOR_GREEN;
case COLOR_GREEN:
return COLOR_RED;
default:
throw new Error('Undefined color');
}
}
常量使用 Symbol 值最大的好处,就是其他任何值都不可能有相同的值了,因此可以保证上面的switch语句会按设计的方式工作。
作为对象的key属性, 防止对象属性被重写
手动实现一个简单的symbol
symbol的本质其实是唯一性,如果我们能做到一个变量不能被预料,那么就能模拟出symbol,用随机数就可以达到这个效果。
const People = (function() {
const _name = '00' + Math.random()
function Person(name){
console.log(this)
this[_name] = name
}
Object.defineProperty(Person.prototype, 'name', {
get: function(){
return this[_name]
}
})
return Person
})()
4、JavaScript中的变量在内存中的具体存储形式
引用类型是用堆存储,基本类型是按栈存储
5、基本类型对应的内置对象,以及他们之间的装箱拆箱操作
JS内置对象 包含 Boolean
String
Number
Array
Function
Date
Math
Object
RegExp
Error
Global
装箱:把基本数据类型转化为对应的引用数据类型的操作
装箱实现的机制如下:
1、创建string类型的一个实例
2、在实例上调用指定的方法
3、销毁这个实例
const s1 = new String('some text')
const s2 = s1.substring(2)
s1 = null
拆箱:将引用类型对象转换为对应的值类型对象
它是通过引用类型的valueOf()
或者toString()
方法来实现的。如果是自定义的对象,你也可以自定义它的valueOf()
/tostring()
方法,实现对这个对象的拆箱。
const objNum = new Number(123);
const objStr =new String("123");
console.log( typeof objNum ); //object
console.log( typeof objStr ); //object
console.log( typeof objNum.valueOf() ); //number
console.log( typeof objStr.valueOf() ); //string
console.log( typeof objNum.toString() ); // string
console.log( typeof objStr.toString() ); // string
6、理解值类型和引用类型
值类型:
- 占用空间固定,保存在栈中(当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的
引用类型:
- 占用空间不固定,保存在堆中(当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。)
引用类型是通过指针来访问内存,不同变量引用同一地址的话会被互相影响
7、null 和 undefined的区别
null表示为空,代表此处不应该有值的存在,一个对象可以是null,代表是个空对象,而null本身也是对象。
有意思的是:
undefined表示『不存在』,JavaScript是一门动态类型语言,成员除了表示存在的空值外,还有可能根本就不存在(因为存不存在只在运行期才知道),这就是undefined的意义所在。
8、至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型
instanceof
instanceof只能通过true或者false来判断,不能直接看出来是什么类型typeof
没有办法正确判断null,数组,对象
null,数组,对象都返回的是objectObject.prototype.toString().call()
9、可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用
隐式转换的场景:
if语句
js里的相等==
双等号两边只要有以便是NaN,便返回false,且他自身不相等
布尔值会转换为数字,false转换为0,true转换为1
对象的转换
const a = [];
const b = [];
const c = {};
const d = {};
console.log("[] == []");
console.log(a == b);
console.log("[] == {}");
console.log(a == c);
console.log("{} == {}");
console.log(d == c);
console.log("[] == ![]");
console.log(a == !b);
对于前三个的原理是一样的,当两个值都是对象 (引用值) 时, 比较的是两个引用值在内存中是否是同一个对象. 因为此 [] 非彼 [], 虽然同为空数组, 确是两个互不相关的空数组, 所以为false。
而最后一个是因为右边空数组会转化为true,取反变为false,false变为0;左边空数组变为空字符串再变为0,0==0就为true
如何避免:
用全等符号===
来判断是否相等,es6中可以用Object.is()
来判断
巧用:
在 js 中,将任意类型数据装换成字符串:
const str = 变量 + " "; // str 将得到变量转化成字符串的结果
10、出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法
js的Number
类型遵循的是IEEE754标准,使用64位固定长度来表示
IEEE754标准由三个区域组成:
- sign bit (符号位) 一位
- exponent bias (指数偏移值) 十一位
- fraction (分数值) 五十二位
value = sign * exponent * fraction
如果某些小数的fraction有无限循环的现象,就像0.1
1001...(中间有 11 个 1001)...1010 (请注意最后四位,是 1010 而不是 1001,因为四舍五入有进位,这个进位就是造成 0.1 + 0.2 不等于 0.3 的原因)
JavaScript可以存储的最大数字:个人觉得应该是在2的53次方往上一些
JavaScript可以存储的最大安全数字: 2的53次方
JavaScript处理大数字的方法:大数相加
先将数字转换成字符串,反转一次,再转换成数组,同位相加进一,一直加完后再反转回来,输出字符串。
避免精度丢失的方法:
四则运算精度问题解决方案:
将数字当做字符串,手动的进行小数点移位,这个方案把浮点型转换为整型是完全可行的,接下来就考虑怎么实现的问题。 因为在JS里,超过一定大小的number就会被JS自动转化为科学计算法,所以在考虑把number当成字符串处理时。一定要考虑到这一点。toFixed
toFixed的方案相对要好解决,只要有了精度没问题的四则运算,四舍五入只要直接判断下浮点数需要精确位数是否大于5即可