Appearance
js看输出题总结
数组的理解
javascript
var obj = {
'2':3,
'3':4,
'length':2,
'splice':Array.prototype.splice,
'push':Array.prototype.push
}
obj.push(1)
obj.push(2)
obj.push(3)
console.log(obj)
{
'2':1
'3':2,
'4':3,
'length':5,
'splice':Array.prototype.splice,
'push':Array.prototype.push
}
- obj 有长度,相当于类数组
- 调用数组的 push,会在数组的最后加一项,每次长度加一
- push(key)时,会隐式调用 toString 方法转为字符串
- key 值相同被覆盖
事件循环
宏任务:setInterval(),setTimeout(),setImmediate(),xhr回调
微任务 1:new Promise()
- Promise 是异步的,是指他的 then()和 catch()方法,Promise 本身还是同步的
- 有 resolve()后,才能执行 then(),有 reject(),不执行 then()
微任务 2:async await
- async function(){} 表示函数内存在异步操作
- await 强制下面代码等待,直到这行代码得出结果(await setTimeout 无效,适用于 ajax)
事件优先级:同步任务>异步任务(微任务(process.nextTick优先)>宏任务(取决于延时时间))
浏览器和node的事件循环区别
node会在每个阶段之间执行微任务,6个阶段:
- timers 阶段:这个阶段执行 timer(setTimeout、setInterval)的回调
- I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
- idle, prepare 阶段:仅 node 内部使用
- poll 阶段:获取新的 I/O 事件, 适当的条件下 node 将阻塞在这里
- check 阶段:执行 setImmediate() 的回调
- close callbacks 阶段:执行 socket 的 close 事件回调
process.nextTick是独立于 Event Loop 之外的,它有一个自己的队列,当每个阶段完成后,如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他 microtask 执行
javascriptsetTimeout(()=>{ console.log('timer1') Promise.resolve().then(function() { console.log('promise1') }) }, 0) setTimeout(()=>{ console.log('timer2') Promise.resolve().then(function() { console.log('promise2') }) }, 0) //浏览器:timer1=>promise1=>timer2=>promise2 //node:timer1=>timer2=>promise1=>promise2
看输出
javascript
//浏览器
(function () {
setTimeout(() => {
console.log(0);
});
new Promise((resolve) => {
console.log(1);
setTimeout(() => {
resolve();
Promise.resolve().then(() => {
console.log(2);
setTimeout(() => console.log(3));
Promise.resolve().then(() => console.log(4));
});
});
Promise.resolve().then(() => console.log(5));
}).then(() => {
console.log(6);
Promise.resolve().then(() => console.log(7));
setTimeout(() => console.log(8));
});
console.log(9);
})();
//1、9、5、0、6、2、7、4、8、3
//node.js
console.log("1");
setTimeout(function () {
console.log("2");
process.nextTick(function () {
console.log("3");
});
new Promise(function (resolve) {
console.log("4");
resolve();
}).then(function () {
console.log("5");
});
});
process.nextTick(function () {
console.log("6");
});
new Promise(function (resolve) {
console.log("7");
resolve();
}).then(function () {
console.log("8");
});
setTimeout(function () {
console.log("9");
process.nextTick(function () {
console.log("10");
});
new Promise(function (resolve) {
console.log("11");
resolve();
}).then(function () {
console.log("12");
});
});
//1,7,6,8,2,4,3,5,9,11,10,12
let test = async function () {
await new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
resolve();
}, 3000);
}).then(() => {
console.log(2);
});
console.log(3);
await new Promise((resolve, reject) => {
console.log(4);
setTimeout(() => {
resolve();
}, 1000);
}).then(() => {
console.log(5);
});
console.log(6);
};
test();
//1
//2
//3
//4
//5
//6
运算符优先级
('b' + 'a' + + 'a' + 'a').toLowerCase()
=('b' + 'a' + (+ 'a') + 'a').toLowerCase()//正号优先级大于加号
=('b' + 'a' + Number('a') + 'a').toLowerCase()//隐式转换
=('ba' + NaN + 'a').toLowerCase()
=('ba' + 'NaN' + 'a').toLowerCase()
=('baNaNa').toLowerCase()
='banana'
精度丢失
javascript
0.1 + 0.2 === 0.3//false
(0.1 * 10 + 0.2 * 10) / 10) == 0.3; // true
function numbersequal(a,b){ return Math.abs(a-b)<Number.EPSILON; }
Number.EPSILON=(function(){ //解决兼容性问题
return Number.EPSILON?Number.EPSILON:Math.pow(2,-52);
})();
var a=0.1+0.2, b=0.3;
console.log(numbersequal(a,b)); //true
trycatchfinallyreturn执行顺序
try 里面放 return,finally 会执行完再 return
javascript
// return 执行了但是没有立即返回,而是先执行了finally
function kaimo() {
try {
return 0;
} catch (err) {
console.log(err);
} finally {
console.log("a");
}
}
console.log(kaimo()); // a 0
// finally 中的 return 覆盖了 try 中的 return。
function kaimo() {
try {
return 0;
} catch (err) {
console.log(err);
} finally {
return 1;
}
}
console.log(kaimo()); // 1
变量生命周期
javascript
a; // undefined 变量提升
b; // ReferenceError 临时死区 const
c; // ReferenceError 临时死区 let
var a = "value";
const b = 3.14;
let c = true;
a; //'value'
b; //3.14
c; //true
javascript
var a = 10;
function foo() {
console.log(a); // undefined
var a = 20; // 使上面的a变成局部作用域
}
foo();
javascript
var a = 10;
function foo() {
console.log(a); // ReferenceError
let a = 20; // 使上面的a变成局部作用域
}
foo();
javascript
var a = 10;
function foo() {
console.log(a); // 全局作用域10
}
foo();
javascript
var a = 10;
function foo() {
console.log(a); // undefined
var a = 20; // 使上面的a变成局部作用域
console.log(a); //20
}
console.log(a); //10
foo();
this的指向
javascript
var x = 10; // global scope
var foo = {
x: 90,
getX: function () {
return this.x;
},
};
foo.getX(); // 90,函数里的this指向foo
let xGetter = foo.getX;
xGetter(); // 10,函数里的this指向window
let getFooX = foo.getX.bind(foo); //使用call和apply同理
getFooX(); // 90
javascript
var num = 1;
let obj = {
num: 2,
add: function() {
this.num = 3;
// 这里的立即指向函数,因为我们没有手动去指定它的this指向,所以都会指向window
(function() {
// 所有这个 this.num 就等于 window.num
console.log(this.num);
this.num = 4;
})();
console.log(this.num);
},
sub: function() {
console.log(this.num)
}
}
// 下面逐行说明打印的内容
/**
* 在通过obj.add 调用add 函数时,函数的this指向的是obj,这时候第一个this.num=3
* 相当于 obj.num = 3 但是里面的立即指向函数this依然是window,
* 所以 立即执行函数里面console.log(this.num)输出1,同时 window.num = 4
*立即执行函数之后,再输出`this.num`,这时候`this`是`obj`,所以输出3
*/
obj.add() // 输出 1 3
// 通过上面`obj.add`的执行,obj.name 已经变成了3
console.log(obj.num) // 输出3
// 这个num是 window.num
console.log(num) // 输出4
// 如果将obj.sub 赋值给一个新的变量,那么这个函数的作用域将会变成新变量的作用域
const sub = obj.sub
// 作用域变成了window window.num 是 4
sub() // 输出4
isNaN和Number.isNaN函数的区别
- isNaN:传入任何非数字值均为NaN
- Number.isNaN:先判断是否数字,再判断NaN
javascript
isNaN('a')//true
Number.isNaN('a')//false
隐式转换
javascript
console.log([] + [])->console.log("" + "")->""
console.log({} + [])->console.log("[object Object]" + "")->"[object Object]"
console.log([] == ![])->console.log(0 == !true)->console.log(0 == false)->true
console.log(true + false)->console.log(1 + 0)->1
[]==false,[]==![],[]==0,''==0,""=="" true
{}==false,{}==!{},{}==0,NaN==0 false