都是用来处理延时和定时任务的函数
setTimeOut()只延迟执行一次,且是在指定的毫秒数之后再调用函数。
setInterval()是一段时间执行一次,可以执行多次,是在每隔指定的毫秒数循环调用函数,直到clearInterval把它清除掉。
原型和原型链(步骤十分详细)
让子类的原型对象(prototype)指向父类的实例,当子类实例找不到对应的属性和方法时,就会往它的原型对象,也就是父类实例上找,从而实现对父类属性和方法的继承。
详细请看JS-继承的六种方式
详细请看二、--2、改变函数内部this指向
模拟JS实现new操作符
function myNew(constrc, ...args) {
const obj = {}; // 1. 创建一个空对象
obj.__proto__ = constrc.prototype; // 2. 将obj的__proto__属性指向构造函数的原型对象
constrc.apply(obj,args); // 3. 将构造函数constrc执行的上下文this指向obj,并执行
return obj; // 4. 返回新创建的对象
}
基本类型 | 引用类型 | |
---|---|---|
种类 | Undefinded、Null、Number、String、Boolean、Symbol | Object、Array、RegExp、Date、Function |
描述 | 简单的数据段 | 为多个值构成的对象 |
操作 | 按值访问,可操作保存在变量中的实际的值。在栈内存中发生复制行为时系统会为新的变量提供新值 | 按引用访问,当复制保存对象的某个变量时,操作的是对象的引用(栈存的地址),但在为对象添加属性时,操作的是实际的对象(堆数据) |
存储 | 数据存放在栈中 | 数据存放在堆中,栈中存放的是堆中的引用地址 |
可以用typeof方法打印来判断是属于哪个类型的(数组Array.isArray(arr) )
不同类型的变量比较时要先转类型,叫做类型转换,类型转换也叫隐式转换。
算数运算转换规则:
① 字符串 + 数字,数字就会转成字符串。
② 数字 - 字符串,字符串转成数字。如果字符串不是纯数字就会转成NaN。字符串 - 数字也一样。两个字符串相减也先转成数字。
③ *,/,>,< 跟 - 的转换也是一样。
不同数据类型之间的比较转换规则:对象—>字符串—>数字,布尔值—>数字
例如:对象和布尔值进行比较时,对象先转换为字符串,然后再转换为数字,布尔值直接转换为数字
[] == true; //false []转换为字符串'',然后转换为数字0,true转换为数字1,所以为false
[1,2,3] == '1,2,3' // true [1,2,3]转化为'1,2,3',然后和'1,2,3', so结果为true;
[1] == 1; // true `对象先转换为字符串再转换为数字,二者再比较 [1] => '1' => 1 所以结果为true
'1' == 1 // true
'1' == true; // true
true == 1 // true
1、一个有趣的题目:
[] == false; //true 对象 => 字符串 => 数值0 false转换为数字0,
![] == false; //true
第二个前边多了个!,则直接转换为布尔值再取反,转换为布尔值时,空字符串(''),NaN,0,null,undefined这几个外返回的都是true, 所以! []这个[] => true 取反为false,所以![] == false为true。
2、特殊情况
undefined==null; //true
0 == null //false;
0 == undefined //false;
false == null //false;
false == undefined //false;
true == null //false;
true == undefined //false;
'' == null //false;
'' == undefined //false;
JS数字运算的精度缺失问题(数字的存储方式)
console.log(0.1+0.2)
//0.30000000000000004
原理:在计算机中数字无论是定点数还是浮点数都是以多位二进制的方式进行存储的。
在JS中数字采用的IEEE 754的双精度标准进行存储(存储一个数值所使用的二进制位数比较多,精度更准确)
0.1和0.2用二进制无法整除,JS中采用的IEEE 754的双精度标准,在存储空间有限的情况下,当出现这种无法整除的小数的时候就会取一个近似值,在js中如果这个近似值足够近似,那么js就会认为他就是那个值。
console.log(0.1000000000000001)
// 0.1000000000000001 (中间14个0,会打印除本身)
console.log(0.10000000000000001)
// 0.1 (中间15个0,js会认为两个值足够近似,所以输出0.1
此时对于JS来说,其不够近似于0.3,于是就出现了0.1 + 0.2 != 0.3 这个现象。 当然,也并非所有的近似值相加都得不到正确的结果。
解决办法:将浮点数转化成整数计算
0.1+0.2 => (0.1*10+0.2*10)/10
每次循环代表一个周期,在这个周期内,主线程执行栈正常工作,当主线程任务清空时,会从任务队列中提取到已完成的异步回调函数入栈,然后执行栈又开始工作,进入下一次事件循环。
注意:每次事件循环只会从任务队列中获取一个回调函数,无论回调队列中有多少个函数,都只会有一个推到主线程。其他的函数需要等到下一次事件循环(主线程任务又清空时)
拓展:
setTimeout
不一定是在规定时间内后立即执行。如上述例子,1000ms只代表多长时间后进入回调队列,但什么时候去执行它,要看主线程的任务什么时候结束。
console.log('script start')
setTimeout(() => {
console.log('异步操作1')
}, 1000)
setTimeout(() => {
console.log('异步操作2')
}, 1000)
console.log('script end')
/* 执行结果
* script start
* script end
* 异步操作1
* 异步操作2
*/
Macrotask 以及 Microtask 都属于异步任务,它们各自包括如下api:
setTimeout
、setInterval
、setImmediate
、I/O
、UI rendering
process.nextTick
、Promises
、Object.observe
、MutationObserver
其中 Macrotask 队列就是任务队列,在事件循环中,每次只会执行一个macrotask,而 Microtasks 则通常安排在当前正在执行的同步任务之后执行,并且需要与当前队列中所有 Microtask 都在同一周期内处理。并且每次主线程任务被清空时,先执行所有microtask,再去执行一个macrotask。