跳到主要内容

JavaScript那些有趣的事

&& 与 ||

let flag = false;
let res = flag || new Date()
console.log(res)//Wed Mar 16 2022 22:19:32 GMT+0800 (中国标准时间)

let flag2 = true;
let res2 = flag2 && "idea小时";
console.log(res2)//idea小时

如果||前面为false,则返回后者。

如果&&前面为true,则返回后者。

var与let

console.log(a)//undefined
var a=10;
console.log(a)
let a=10;//Uncaught ReferenceError: Cannot access 'a' before initialization

var类型变量有个特性:声明提升。即不管var变量在哪里定义,都会在当前代码块最开始的地方声明它。所以在它被初始化之前,可以打印它,但是只能得到undefined。

但是let没有这个特性,所以它是一个错误。

for (let i = 0; i < 5; i++) {

}
console.log(i)//Uncaught ReferenceError: i is not defined
for (var i = 0; i < 5; i++) {

}
console.log(i)//5

let变量的作用域很明确,就是定义他的地方。在for循环之外就访问不到了。

而var变量又有一个特性,var定义的全局变量自动会绑定到全局对象window上。

window

任务队列

for (var i = 0; i < 5; i++) {
setTimeout(()=>{
console.log(i)
},0)
}//5 5 5 5 5

即使设置了0ms(立即)执行console.log(i),但最终还是打印了5。

因为JavaScript是单线程的,setTimeout这种行为,会将里面的函数放在一个任务队列里,JavaScript完成主线的代码之后,才会去执行任务队列里的任务。因此,当执行任务队列里的任务时,i循环增加到了5。

并且,setTimeout后面设置的毫秒数,是指:在多少ms之后,把里面的函数放进任务队列里,并不是立即执行,执行时间是不确定的。

又因为var是全局变量,所以打印5看起来很正常。

那么,把var改成let。

for (let i = 0; i < 5; i++) {
setTimeout(()=>{
console.log(i)
},0)
}//0 1 2 3 4

这时,又会打印0 1 2 3 4,这又是为啥?

这个涉及到闭包。

在 setTimeout( )函数中,我们写了个箭头函数,箭头函数中访问了在箭头函数外的作用域中的变量i,因此这个箭头函数就可以成为闭包。

而闭包的特性是,会一直拥有他引用的变量所在的词法作用域。

在这里,用let声明的i,每次循环都会创建一个闭包,闭包函数引用的i每一次循环都是不同的i,所以打印0 1 2 3 4。

而var声明的i,每次循环都会创建一个闭包,但是每个闭包函数引用的i是全局变量i,所以打印出来的结果是相同的.

区别就在于,var声明的i是在for循环外面,let是在for循环开始的时候。

有人可能会说,是不是你设置的时间太短了,你设置成1000ms,用let是不是也会打印5次5。

事实上,还是会打印0 1 2 3 4。和时间没有关系的,上面说过,这个时间只是将函数放进任务队列里的时间。

总结一句话:闭包函数会一直拥有其引用变量所在的词法作用域链。

IIFE(立即调用函数表达式)

平时我们定义和执行函数分为两部分。

function test(){
console.log(2)
}
test();

使用IIFE(函数外面套个括号,然后再在后面加个括号):会在定义的时候就执行函数

(function test(){
console.log(2)
})()

不要换行!

function addTwo(x) {
return x + 2;
}

function addThree(x) {
return x + 3;
}

function addFive(x) {
return x + 5;
}

function addTen(x) {
return
Promise.
resolve(x).
then(addTwo).
then(addThree).
then(addFive);
}
addTen(10).then(console.log);

IDE提示:Unreachable code

有问题吗?有问题。

js是对回车敏感的语言,并不是靠分号来区分语句的。所以这里本意是想让代码更美观,把return后面的内容换到了下一行,但是return独占一行就会直接返回了。

应该这么写:

function addTen(x) {
return Promise.
resolve(x).
then(addTwo).
then(addThree).
then(addFive);
}

Array.prototype.reduce() 挺好玩

let res = [1, 2, 3, 4].reduce((pre, cur) => pre+cur, 10);
console.log(res)//20
//即10+1,11+2,13+3,16+4

此函数对数组里的元素进行循环。

reduce中接收两个参数,第一个参数是个函数(reducer函数),第二个参数是initialValue(可选的)。

如果不提供initialValue,那么循环将从第二个元素开始。

let res = [1, 2, 3, 4].reduce((pre, cur) => pre+cur);
console.log(res)//10
//即1+2+3+4

如果提供了初始值,就相当于在数组前面添加了初始值。

reducer函数,接收四个参数:

pre:上一个reducer函数的返回值;(对于上面的例子,就是return pre+cur)

cur: 当前遍历到的元素。

curIndex:当前遍历到的元素的下标

arr:被遍历的对象

具体的解释在官方文档里有:Array.prototype.reduce()

通用的合成期约函数

对于下面的代码,我只能说:js神奇!

reduce函数是一个循环处理的过程,循环一个数组,接收一个reducer回调函数,如果回调函数只有两个参数,那么第一个参数表示previousValue,第二个参数表示currentValue,回调函数中的返回值就是下一次操作的previousValue。

这里的previousValue就是promise,即promise.then(fn)执行后的返回值。

回调函数后面的promise.resolve(x)是initialValue(初始值),被当做循环的第一项,如果没有指定初始值,则会取数组的第一项当做initialValue,也就是说循环从第二个元素开始。

fn就表示数组中当前循环到的值。

function addTwo(x) {
return x + 2;
}

function addThree(x) {
return x + 3;
}

function addFive(x) {
return x + 5;
}

// function addTen(x) {
// return Promise.resolve(x).then(addTwo).then(addThree).then(addFive);
// }

// addTen(10).then(console.log);

// function addTen2(x) {
// return [addTwo, addThree, addFive]
// .reduce((promise, fn) => promise.then(fn), Promise.resolve(x))
// }

function compose(...fns) {
return (x) => fns.reduce((promise, fn) => promise.then(fn), Promise.resolve(x))
}

let addTen = compose(addTwo, addThree, addFive)
addTen(11).then(console.log)

== 和 === 是不一样的

==比较的是值。类似于Java中的equals。

===不仅比较值,还得比较类型,类型不一致也不行。

通常IDE会推荐使用===,使用==会爆出警告。

Number(x)这种和直接声明一样。

let a=3;
let b=new Number(3);
let c=3;
let d=Number(3);

console.log(a==b);//true
console.log(a===b);//false
console.log(a==c);//true
console.log(a===c);//true
console.log(a==d)//true
console.log(a===d)//true
console.log(b==d)//true
console.log(b===d)//false

Function*

const teams = [
{name: 'team1', members: ['123', '333']},
{name: 'team2', members: ['23', '34']},
]

function* getMembers(members) {
for (let i = 0; i < members.length; i++) {
yield members[i];
}
}

function* getTeam(teams) {
for (let i = 0; i < teams.length; i++) {
yield* getMembers(teams[i].members);
}
}

let obj = getTeam(teams);
console.log(obj.next());
console.log(obj.next())
console.log(obj.next())
console.log(obj.next())