1. reduce方法
语法: arr.reduce(callback,[initialValue])
callback:执行数组中每个值的函数,包含四个参数
1.previousValue(上一次调用回调返回的值,或者是提供的初始值initialValue)
2.currentValue(数组中当前被处理的元素)
3.index(当前元素在数组中的索引)
4.array(调用 reduce 的数组)
initialValue:(作为第一次调用 callback 的第一个参数。)
例1: var arr = [1,2,3,4];
var sum = arr.reduce(function(prev,cur,index,array){
console.log(prev,cur,index);
return prev+cur;
});
console.log(arr,sum);
打印结果:
1,2,1
3,3,2
6,4,3
[1,2,3,4],10
// 第一次index值是从1开始的,第一次的prev是数组中第一个值,数组长度为4,但是reduce循环3次;
例2: var arr = [1,2,3,4];
var sum = arr.reduce(function(prev,cur,index,array){
console.log(prev,cur,index);
return prev+cur;
},7);//此处增加初始值
console.log(arr,sum);
打印结果;
7 1 0
8 2 1
10 3 2
13 4 3
[1,2,3,4],17
// 此处index是从0开始的,第一次的prev为是我们设置的初始值7,循环四次;
结论:如果没有设置initialValue初始值,reduce则会从index=1开始执行callback,跳过第一个索引,如果提供initialValue,从索引0开始。如果这个数组为空,要是我们设置了初始值就不会报错.
2. 数组降维
forEach 递归降维
const oldArr = [
1,
[
2, [3],
[4, 5, 6],
[7, 8, 9],
10,
11,
],
12,
13,
14,
[15, 16, 17],
];
const newArr = [];
const ergodic = (arr) => {
arr.forEach((item) => {
if (Array.isArray(item)) {
ergodic(item);
} else {
newArr.push(item);
}
})
}
ergodic(oldArr, newArr);
console.log(newArr);
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 ]
方式二:
const oldArr = [
1,
[
2, [3],
[4, 5, 6],
[7, 8, 9],
10,
11,
],
12,
13,
14,
[15, 16, 17],
];
function flatten(arr) {
return arr.reduce(function(prev,item){
return prev.concat(Array.isarry(item)?flatten(item):item)
},[])
}
console.log(flatten(oldArr))
3.Event Loop
这种主线程从 “任务队列” 中读取执行事件,不断循环重复的过程,就被称为 事件循环(Event Loop)
js运行机制
1.所有同步任务都在主线程上执行,形成一个 “执行栈”(execution context stack)。
2.主线程之外,存在一个 “任务队列”(task queue),在走主流程的时候,如果碰到异步任务,那么就在 “任务队列” 中放置这个异步任务。
3.一旦 “执行栈” 中所有同步任务执行完毕,系统就会读取 “任务队列”,看看里面存在哪些事件。那些对应的异步任务,结束等待状态,进入执行栈,开始执行。
4.主线程不断重复上面三个步骤
JavaScript的异步任务
宏任务(Macrotask):script(整体代码)、setTimeout、setInterval、
XMLHttpRequest.prototype.onload、I/O、UI 渲染。
微任务(Microtask):Promise,MutationObserver。
// 位置 1
setTimeout(function () {
console.log('timeout1');
}, 1000);
// 位置 2
console.log('start');
// 位置 3
Promise.resolve().then(function () {
// 位置 5
console.log('promise1');
// 位置 6
Promise.resolve().then(function () {
console.log('promise2');
});
// 位置 7
setTimeout(function () {
// 位置 8
Promise.resolve().then(function () {
console.log('promise3');
});
// 位置 9
console.log('timeout2')
}, 0);
});
// 位置 4
console.log('done');
1.首先分清宏任务和微任务,先碰到script(整体代码),【位置 1】属于宏任务放到任务队列中,待会执行。
2.【位置2】是script(整体代码)无阻碍,直接执行。
3.【位置 3】属于微任务,暂时标记,走完script(整体代码)之后,优先执行微任务,再执行宏任务。
4.【位置4】是script(整体代码)的无阻碍代码,直接执行。
这样,第一波步骤执行了【位置2】start和【位置4】的done
接着上面我们走完了第一遍代码,然后现在这一步先走 script(整体代码)下的微任务【位置3】
1.碰到【位置 5】无阻碍代码,直接执行。
2.【位置6】是微任务,暂时标记。等执行完【位置3】整体代码后优先执行它。
3.【位置7】是宏任务,加入任务队列。看他和【位置1】谁先执行。
4.执行完【位置3】发现里面有【位置6】微任务直接执行输出。
到这一步已经走完了微任务。接着宏任务,【位置 1】和【位置 7】都被丢到任务队列了,是不是【位置 1】先走呢?
答案;不是! 各个环境都有自己的流程。Chrome控制台打印的话,那就是先【位置 7】,然后是【位置1】。
结果就是:
start
done
promise1
promise2
timeout2
promise3
timeout1
增加练习:
console.log("script start");
setTimeout(function() {
console.log("setTimeout---0");
}, 0);
setTimeout(function() {
console.log("setTimeout---200");
setTimeout(function() {
console.log("inner-setTimeout---0");
});
Promise.resolve().then(function() {
console.log("promise5");
});
}, 200);
Promise.resolve()
.then(function() {
console.log("promise1");
})
.then(function() {
console.log("promise2");
});
Promise.resolve().then(function() {
console.log("promise3
");
});
console.log("script end");
答案:
script start
script end
promise1
promise3
promise2
setTimeout---0
setTimeout---200
promise5
inner-setTimeout---0
最后再看一个示例
setTimeout(function() {
console.log(4);
}, 0);
const promise = new Promise(function executor(resolve) {
console.log(1);
for (var i = 0; i < 10000; i++) {
i == 9999 && resolve();
}
console.log(2);
}).then(function() {
console.log(5);
});
console.log(3);
输出结果:
1
2
3
5
4
解析答案不是 3 1 2 5 4的原因:
promise的特性;
- promise是有兼容性问题的,node环境下默认支持,还可以下载相应插件来解决兼容性问题
- promise是有三种状态的,等待态pending / 成功态resolved / 失败态rejected
- promise的状态是可以转换的,可以从pending -> resolved 或 pending -> rejected,但是resolved不能转换为rejected/pending,rejected不能转换为resolved/pending,简而言之即状态只会更改一次
promise 构造函数的第一个参数是executor
let promise = new Promise(function(resolve,reject){
console.log('我是会立即执行的');
})
promise.then(()=>{
//成功的回调
},()=>{
//失败的回调
})
- executor默认在new的时候自动执行。
- 每个promise实例都有then方法
- then方法中有两个参数,即成功/失败的函数。
4.script 引入方式
1.html静态<script>引入;
2.js动态引入<script>;
3.<script defer> 延迟加载,元素解析完成后加载;
4.<script async> 异步加载,但执行时会阻塞元素渲染;
5.输出以下结果
var a = [0,1,2,3]
console.log(a.push(4)) // 5
//原因:push方法可向数组的末尾添加一个或多个元素
//,并返回新的长度
console.log(a) // [0,1,2,3,4]
6.输出结果
function asd(count){
return {
cal:function(){
return ++count;
}
}
}
var obj = asd(5);
console.log(obj.cal()) // 6
console.log(obj.cal()) // 7
console.log(obj.cal()) // 8
7.统计字符串中出现最多的字母和次数
var str = 'abcdasabdsa';
var obj = {};
for(var i = 0;i<str.length;i++){
var current = str.charAt(i);//获取当前位置的元素
if(obj[current]){
obj[current]++;
}else{
obj[current] = 1;
}
}
console.log(obj)//{a: 4, b: 2, c: 1, d: 2, s: 2}
var maxStr = null;
var max = 0;
for(var j in obj){
if(obj[j] > max){
max = obj[j];
maxStr = j;
}
}
console.log('出现字符串最多的次数:'+ max)
console.log('出现最多次数的字母:'+ maxStr)
8.格式化金钱,每千分位加逗号
1.
formatPrice('100978');
function formatPrice(str){
var s = '';
var count = 0;
for(var i = str.length -1;i>=0;i--){
s = str[i]+s;
count++;
if(count % 3 == 0 && i != 0){
s = ','+s;
}
}
console.log(s)
return s;
}
2.
console.log(format('908887'))
function format (str){
return str.replace(/(\d)(?=(?:\d{3})+$)/g,'$1,');
}
9.统计数组中出现的次数对象
var arr = [23,7,9,23,7,6,7,8,9];
var result = arr.reduce(function(prev,curr){
prev[curr] ? prev[curr]++ :(prev[curr]=1)
return prev
},{})
console.log(result) //{6: 1, 7: 3, 8: 1, 9: 2, 23: 2}