- 1.JavaScript规定了几种语言类型
- 2.JS对象的底层数据结构是什么
- 3.Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol
- 4.JS中的变量在内存中的具体存储形式
- 5.基本类型对应的内置对象,以及他们之间的装箱拆箱操作
- 6.理解值类型和引用类型
- 7.null和undefined的区别
- 8.至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型
- 9.可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用
- 10.出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法
1.JavaScript规定了几种语言类型
- Number
- String
- Boolean
- true/false
- Null
- null 表示的是:“定义了但是为空”。所以,在实际编程时,我们一般不会把变量赋值为 undefined,这样可以保证所有值为 undefined 的变量,都是从未赋值的自然状态。
- Null 类型也只有一个值,就是 null,它的语义表示空值,与 undefined 不同,null 是 JavaScript 关键字,所以在任何代码中,你都可以放心用 null 关键字来获取 null 值。
- Undefined
- 函数默认返回值
function ret() { // 默认是返回 undefined }
- 获取对象不存在的 key
var obj = {} obj.name
- 获取数组不存在的下标
var aa = [] aa[0] undefined
- 声明变量,不初始化值
var foo;
- Object
- BigInt
- Symbol
- 其中什么是symbol?
Symbol函数会生成一个唯一的值
可以理解为Symbol类型跟字符串是接近的
但每次生成唯一的值,也就是每次都不相等,
至于它等于多少,并不重要 这对于一些字典变量,比较有用。
Symbol 是 ES6 中引入的新类型,它是一切非字符串的对象 key 的集合,在 ES6 规范中,整个对象系统被用 Symbol 重塑。
Symbol 可以具有字符串类型的描述,但是即使描述相同,Symbol 也不相等。
创建 Symbol 的方式是使用全局的 Symbol 函数。例如:
var mySymbol = Symbol(“my symbol”);
任意创建两个Symbol都不会相同
obj['name'] = 'tianyufei';
obj.name = 'xxx'; //修改了
console.log(obj['name']); //xxx
var xxx = "na" + "me";
console.log(obj[xxx]);//本想输出name但输出xxx
【注】问题是,如果用字符串作为对象属性,会导致,只要值是对象的这个属性,就可以访问。
// 避免以上问题,将属性设置为Symbol类型
let name = Symbol("描述1:我是name属性");
var xxx = Symbol();
console.log(name === xxx); //false任意两个Symbol都不会相同
obj[name] = 'tianyufei';
console.log(obj[name]); //tianyufei
console.log(obj); //{Symbol(描述1:我是name属性): "tianyufei"}
2.JS对象的底层数据结构是什么
详细讲解:
https://www.jianshu.com/p/7d3ab9a22b11?mode=dark
JavaScript使用的是 堆(Heap) 和 栈( Stack)
JavaScript基本类型数据都是直接按值存储在栈中的(Undefined、Null、不是new出来的布尔、数字和字符串),每种类型的数据占用的内存空间的大小是确定的,并由系统自动分配和自动释放。这样带来的好处就是,内存可以及时得到回收,相对于堆来说 ,更加容易管理内存空间。
JavaScript引用类型数据被存储于堆中 (如对象、数组、函数等,它们是通过拷贝和new出来的)。其实,说存储于堆中,也不太准确,因为,引用类型的数据的地址指针是存储于栈中的,当我们想要访问引用类型的值的时候,需要先从栈中获得对象的地址指针,然后,在通过地址指针找到堆中的所需要的数据。
3.Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol
开发中的应用:https://blog.csdn.net/qq_41070239/article/details/90053964
简单的Symbol :https://segmentfault.com/a/1190000015262174
4.JS中的变量在内存中的具体存储形式
js变量类型:分为基本类型和引用
- 基本类型是保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,通过按值访问,(有undefined,null,以及不是new出来的布尔、字符串、数字)
- 引用类型是保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JavaScript不允许直接访问堆内存中的位置,因此操作对象时,实际操作对象的引用。
(有对象、数组/函数/拷贝或new出的变量)
5.基本类型对应的内置对象,以及他们之间的装箱拆箱操作
typeof 和 instanceof 区别
- typeof: 经常用来检测一个变量是不是最基本的数据类型
- instanceof: 用来判断某个构造函数的 prototype 属性所指向的对象是否存在于另外一个要检测对象的原型链上。简单说就是判断一个引用类型的变量具体是不是某种类型的对象
拆箱和装箱操作
引用类型有个特殊的基本包装类型,它包括String、Number和Boolean。
作为字符串的a可以调用方法(进行了装箱操作)
- 装箱:把基本数据类型转化为对应的引用数据类型的操作
装箱分为:隐式装箱和显式装箱
1. 隐式装箱: (每当读取一个基本类型的时候,后台就会创建一个对应的基本包装类型对象,从而让我们能够调用一些方法来操作这些数据。(隐式装箱))
let a = 'zhang';
let b = a.indexOf('h') // 1
// 后台运行的顺序
let a = new String('zhang')
let b = a.indexOf('h')
a = null
a是基本类型,不是对象,不应该具有方法,js内部进行了一些列处理(装箱), 使得它能够调用方法。在这个基本类型上调用方法,其实是在这个基本类型对象上调用方法。这个基本类型的对象是临时的,它只存在于方法调用那一行代码执行的瞬间,执行方法后立刻被销毁。
实现机制:
创建String类型的一个实例;
在实例上调用指定的方法;
销毁这个实例;
2. 显式装箱:通过内置对象可以对Boolean、Object、String等可以对基本类型显示装箱
let a = 'zhang'
- 拆箱: 拆箱和装箱相反,就是把引用类型转化为基本类型的数据,通常通过引用类型的valueof()和toString()方法实现
let name = new String ('zhang')
let age = new Number (22)
console.log(typeof name) // object
console.log(typeof age) // object
引用类型是object
下面进行拆箱操作 ---- 把引用类型转成基本类型
console.log(typeof name.toString()) // string 基本类型
console.log(typeof age.toString()) // string
console.log(typeof age.valueof()) // number
6.理解值类型和引用类型
同47.null和undefined的区别
- 定义不同:null是空值,表示一个变量不再指向任何对象地址;undefined是没有定义,(表示应该有一个值,但没有定义)
类型不同:
null的类型是object,undefined的类型是undefined。null代表没有对象,是空值,undefined是缺少值。
- 解释下typeof null 为 ‘object’的bug
JavaScript中的数据在底层是以二进制存储,比如null所有存储值都是0,但是底层的判断机制,只要前三位为0,就会判定为object,所以才会有typeof null === ‘object’这个bug。
- 用法不同:
- null
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。
Object.getPrototypeOf(Object.prototype)
// null
- undefined
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
8.至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型
原文地址:https://www.cnblogs.com/onepixel/p/5126046.html
- typeof
(右侧是一元表达式,返回这个表达式的数据类型,返回结果包括number boolean string object undefined function)
- 优缺点:可以对基础数据类型做出 准确的判断,在对于引用类型的返回的基本都是object(除了function外)。判断是引用类型,typeof使用不方便
- instanceof (判断A是否为B的实例)
表达式为:A instanceof B,如果 A是B的实例,则返回true; 否则返回false
在这里特别注意的是 instanceof检测的是原型
特殊问题:
[] instanceof Array; // true
[] instanceof Object; // true
【】能被判断出Array也能判断出object,因为一切皆是对象
{} instanceof Object;// true
new Date() instanceof Date;// true
function Person(){};
new Person() instanceof Person;
new Date() instanceof Object;// true
new Person instanceof Object;// true
[ ]、Array、Object 三者之间的关系
从 instanceof 能够判断出 [ ].proto 指向 Array.prototype,而 Array.prototype.proto 又指向了Object.prototype,最终 Object.prototype.proto 指向了null,标志着原型链的结束。因此,[]、Array、Object 就在内部形成了一条原型链:
instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。
针对数组的这个问题,ES5 提供了 Array.isArray() 方法 。该方法用以确认某个对象本身是否为 Array 类型,而不区分该对象在哪个环境中创建。
Array.isArray() 本质上检测的是对象的 [[Class]] 值,[[Class]] 是对象的一个内部属性,里面包含了对象的类型信息,其格式为 [object Xxx] ,Xxx 就是对应的具体类型 。对于数组而言,[[Class]] 的值就是 [object Array] 。
- constructor 查看对象对应的构造函数
当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用 (构造函数名.prototype.constructor = 构造函数名)
1. null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。
2. 函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object
- toString 是Object的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。
对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。
判断数组类型方法:
最好用Array.isAarray(value),也可以用instanceof 和 object.prototype.toString.call
9.可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用
原文链接: https://www.nblogs.com/archives/496/
10.出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法
- 出现小数精度丢失的原因
原因其实就是js number类型运算都需要先将十进制转二进制
但小数点后的位数转二进制会出现无限循环的问题,只能舍0入1,所以会出现小数点丢失问题
查看详细原因(链接)
- JavaScript可以存储的最大数字、最大安全数字
最大安全数是 2^53-1
最大整数范围: -2^53-1 ~ 2^53-1
浮点类型:1位符号位, 11个指数位,52个尾数位(小数位)
可以通过 Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER 来获取js的最大安全数和最小安 全数;得到的值就是Math.pow(2, 53)-1,所以在js中只要计算结果小于Math.pow(2, 53)就不会丢失精度。
- JavaScript处理大数字的方法、避免精度丢失的方法
1.小数精度问题
将小数转化为整数进行运算
function formatFloat (num1, num2) {
var baseNum, baseNum1, baseNum2;
try {
baseNum1 = num1.toString().split(".")[1].length;
} catch (e) {
baseNum1 = 0;
}
try {
baseNum2 = num2.toString().split(".")[1].length;
} catch (e) {
baseNum2 = 0;
}
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
return (num1 * baseNum + num2 * baseNum) / baseNum;
};
console.log(formatFloat(0.1,0.2))
限制精度,只保留小数部分位数,减小精度出现的误差问题
Number.toFixed()
console.log((0.1 + 0.2).toFixed(12) == 0.3)
> true
console.log((0.1 + 0.2).toFixed(12))
> 0.300000000000
console.log((2.4/0.8).toFixed(12))
> 3.000000000000
2.解决大整数精度问题
算法:
function bigNumberAdd(a,b) {
var res = '', c = 0;//进位值,初始c值为0
a = a.split('');//将数据拆分为数组
b = b.split('');//同上
while (a.length || b.length || c) {//遍历数据
c += ~~a.pop() + ~~b.pop();//进位值c
res = c % 10 + res;//依次相加
c = c > 9;//若c大于9,c为true,下次循环中true转换为1,即有进位
}
return res.replace(/^0+/, '');//返回值
}
以上方法即可正确的实现大数据相加
bigNumberAdd('12478945645654','489789411231231523');//调用
注意:参数需传递字符串类型