目录
一、JavaScript基础
变量和类型
1、JavaScript规定了几种语言类型
2、JavaScript对象的底层数据结构是什么
3、Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol
4、JavaScript中的变量在内存中的具体存储形式
5、基本类型对应的内置对象,以及他们之间的装箱拆箱操作
6、理解值类型和引用类型
7、null和undefined的区别
8、至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型
9、可能发生隐式类型转换的场景以及转换原则
10、出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法
JavaScript
规定了几种语言类型两种类型:基本类型和引用类型
JavaScript
对象的底层数据结构是什么 JavaScript中的数据在底层是以二进制存储的。
比如
null
所有存储值都是0,但是底层的判断机制,只要前三位为0,就会判定为object
,所以才会有typeof null === 'object'
这个bug;同理可以解释数字类型中的另外一个bug,即:0.1+0.2 != 0.3;
因为浮点数在运算时,会先将数字转成二进制数,然后进行二进制运算,在转换过程中,会出现取余不尽的情况,即有无限小数,然后会进行类似四舍五入的方式进行保留,导致运算精度不准确,当然,解决这个问题,可以让他们同时乘以一个极大的数,然后除以极大的数来抵消掉,项目中,也可以用big.js来帮助处理;
Symbol
类型在实际开发中的应用、可手动实现一个简单的Symbol
特性:Symbol类型的key是不能通过Object.keys()或者for...in来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。也正因为这样一个特性,当使用JSON.stringify()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外。
let obj = {
[Symbol('name')]: 'zzz',
age: 18,
title: 'Engineer'
}
Object.keys(obj) // ['age', 'title']
for (let p in obj) {
console.log(p) // 分别会输出:'age' 和 'title'
}
Object.getOwnPropertyNames(obj) // ['age', 'title']
JSON.stringify(obj) // {"age":18,"title":"Engineer"}
/*
const TYPE_AUDIO = 'AUDIO'
const TYPE_VIDEO = 'VIDEO'
const TYPE_IMAGE = 'IMAGE'
*/
const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()//保证变量的唯一性,但是这种场景意义不是很大,少了一些特定变量名称的粘贴复制吧,const声明的就是唯一值了
function handleFileResource(resource) {
switch(resource.type) {
case TYPE_AUDIO:
playAudio(resource)
break
case TYPE_VIDEO:
playVideo(resource)
break
case TYPE_IMAGE:
previewImage(resource)
break
default:
throw new Error('Unknown type of resource')
}
}
在JavaScript中,是没有如Java等面向对象语言的访问控制关键字private的,类上所有定义的属性或方法都是可公开访问的。因此这对我们进行API的设计时造成了一些困扰。
而有了Symbol以及模块化机制,类的私有属性和方法才变成可能。
通常情况下,我们在一个浏览器窗口中(window),使用Symbol()函数来定义和Symbol实例就足够了。但是,如果你的应用涉及到多个window(最典型的就是页面中使用了
let gs1 = Symbol.for('global_symbol_1') //注册一个全局Symbol
let gs2 = Symbol.for('global_symbol_1') //获取全局Symbol
gs1 === gs2 // true
JavaScript
中的变量在内存中的具体存储形式String()
、Number()
、Boolean()
、Symbol()
RegExp()
、Date()
、Array()
、Object()
、Function()
、Error()
装箱:将基本数据类型转为引用数据类型。装箱分为隐式和显示
隐式装箱: 每当读取一个基本类型的值时,后台会创建一个该基本类型所对应的对象。在这个基本类型上调用方法,其实是在这个基本类型对象上调用方法。这个基本类型的对象是临时的,它只存在于方法调用那一行代码执行的瞬间,执行方法后立刻被销毁。
let num=123;
num.toFixed(2); // '123.00'
//上方代码在后台的真正步骤为
var c = new Number(123);
c.toFixed(2);
c = null;
(1)创建一个 Number 类型的实例。
(2)在实例上调用方法。
(3)销毁实例。
显式装箱: 通过内置对象 Boolean、Object、String 等可以对基本类型进行显示装箱。
var obj = new String('123');
拆箱:拆箱与装箱相反,将引用数据类型转为基本数据类型。拆箱过程内部调用了抽象操作 ToPrimitive 。该操作接受两个参数,第一个参数是要转变的对象,第二个参数 PreferredType 是对象被期待转成的类型。第二个参数不是必须的,默认该参数为 number,即对象被期待转为数字类型。
1、占用空间固定,保存在栈中
2、保存与复制的是值本身
3、使用typeof检测数据的类型
4、基本类型数据是值类型
1、占用空间不固定,保存在堆中
2、保存与复制的是指向对象的一个指针
3、使用instanceof检测数据类型
4、使用new()方法构造出的对象是引用型
null
和undefined
的区别在 JavaScript 中, null 用于对象, undefined 用于变量,属性和方法。对象只有被定义才有可能为 null,否则为 undefined。null 和 undefined 的值相等,但类型不等。所以设置一个值为null是合理的,但是设置一个值为undefined就不合理。
null == undefined // true
null === undefined // false
!!null // false
!!undefined // false
Number(null) //0
Number(undefined) //NaN
typeof null // object
typeof undefined // undefined
JavaScript
数据类型的方式,以及他们的优缺点,如何准确的判断数组类型注意:typeof null也是返回object
instanceof是用来判断A是否为B的实例时,表达式为:A instanceof B,如果 A是B的实例,则返回true; 否则返回false。在这里特别注意的是 instanceof检测的是原型。
toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型格式为[object,xxx],xxx是具体的数据类型,基本上所有对象的类型都可以通过这个方法获取到。但是IE6下,undefined和null均为Object。
constructor是查看对象对应的构造函数,constructor在对应对象的原型下面,是自动生成的,当我们写一个构造函数的时候,程序自动添加:构造函数名.prototype.constructor = 构造函数名。
string
number
boolean
JavaScript
可以存储的最大数字、最大安全数字,JavaScript
处理大数字的方法、避免精度丢失的方法 计算机中的计算使用的是二进制,二进制只有0和1,遵照十进制四舍五入,二进制是0舍1入,这是浮点数计算精度丢失的根本原因。
大整数的精度丢失跟浮点数本质一样,尾数位最大是52位,因此JS中能精准表示的最大整数是Math.pow(2, 53),大于该值可能会丢失精度。
//加法
Number.prototype.add = function(arg) {
var r1, r2, m;
try {
r1 = this.toString().split(".")[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
return (this * m + arg * m) / m;
};
//减法
Number.prototype.sub = function(arg) {
return this.add(-arg);
};
//乘法
Number.prototype.mul = function(arg) {
var m = 0,
s1 = this.toString(),
s2 = arg.toString();
try {
m += s1.split(".")[1].length;
} catch (e) {}
try {
m += s2.split(".")[1].length;
} catch (e) {}
return (
(Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) /
Math.pow(10, m)
);
};
//除法
Number.prototype.div = function(arg) {
var t1 = 0,
t2 = 0,
r1,
r2;
try {
t1 = this.toString().split(".")[1].length;
} catch (e) {}
try {
t2 = arg.toString().split(".")[1].length;
} catch (e) {}
with (Math) {
r1 = Number(this.toString().replace(".", ""));
r2 = Number(arg.toString().replace(".", ""));
return (r1 / r2) * pow(10, t2 - t1);
}
};