ES5和ES6常用数据处理函数
前言
现代前端并不是画画静态页面那么简单,很多以前后端的数据处理都放到了前端,由于数据处理越来越多和频繁。对js的数据处理函数有一定了解会在遇到问题的时候找到最优解,下面一起来看看常用的数据处理。(由于数组,对象,字符串是处理较多的类型,下面围绕这三种类型展开)
内容
大纲
console.log([])
VM166:1
[]
length: 0
__proto__: Array(0)
concat: ƒ concat()
constructor: ƒ Array()
copyWithin: ƒ copyWithin()
entries: ƒ entries()
every: ƒ every()
fill: ƒ fill()
filter: ƒ filter()
find: ƒ find()
findIndex: ƒ findIndex()
flat: ƒ flat()
flatMap: ƒ flatMap()
forEach: ƒ forEach()
includes: ƒ includes()
indexOf: ƒ indexOf()
join: ƒ join()
keys: ƒ keys()
lastIndexOf: ƒ lastIndexOf()
length: 0
map: ƒ map()
pop: ƒ pop()
push: ƒ push()
reduce: ƒ reduce()
reduceRight: ƒ reduceRight()
reverse: ƒ reverse()
shift: ƒ shift()
slice: ƒ slice()
some: ƒ some()
sort: ƒ sort()
splice: ƒ splice()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
unshift: ƒ unshift()
values: ƒ values()
Symbol(Symbol.iterator): ƒ values()
Symbol(Symbol.unscopables): {copyWithin: true, entries: true, fill: true, find: true, findIndex: true, …}
__proto__: Object
一 常用的处理数组和对象的方法
1.箭头函数(有了箭头函数this的指向有了变化,所以单独提出来说一下)
// var和let
var a = 99; // 全局变量a
f(); // f是函数,虽然定义在调用的后面,但是函数声明会提升到作用域的顶部。
console.log(a); // a=>99, 此时是全局变量的a
function f() {
console.log(a); // 当前的a变量是下面变量a声明提升后,默认值undefined
var a = 10;
console.log(a); // a => 10
}
// 1. let定义块级作用域变量
{
var i = 9;
}
console.log(i); // 9
{
let i = 9; // i变量只在 花括号内有效!!!
}
console.log(i); // Uncaught ReferenceError: i is not defined
// 2.没有变量提升与暂时性死区
console.log(aicoder); // 错误:Uncaught ReferenceError ...
let aicoder = 'aicoder.com';
// 3.let变量不能重复声明
let a = 0;
let a = 'sss';
// Uncaught SyntaxError: Identifier 'a' has already been declared
// var
for (var i = 0; i < 5; i++) {
setTimeout(function (){
console.log(i);
},1000);
}
// 旧解决方案
for (var i = 0; i < 5; i++) {
(function(i){ //立刻执行函数
setTimeout(function (){
console.log(i);
},1000);
})(i);
}
// 新方案
for (let i = 0; i < 5; i++) {
setTimeout(function (){
console.log(i);
},1000);
}
// this指向
function Person() {
this.age = 0;
setTimeout(function() {
// 回调里面的 `this` 变量就指向了期望的那个对象了
console.log(this.age);
}, 1000);
}
var p = new Person()
------------------------------------
function Person() {
this.age = 0;
setTimeout(() => {
// 回调里面的 `this` 变量就指向了期望的那个对象了
console.log(this.age);
}, 1000);
}
var p = new Person()
2.forEach和map和for...in for...of
forEach和map都是遍历函数,大家用forEach会遇到很多的问题,比如不能break, 不能return,这些都做了优化
//map不会改变原数组, forEach会改变原数组
// 1. 需要一个新数组
var array = [1, 2, 3];
array.forEach((item, index) => {
array[index] = item * 2
});
var ttt = array.map((item, index) => {
return item * 2
});
// 2. 需要修改后台返回的对象,加一个sex字段
var array = [{name: 'zz'}, {name: 'yy'}, {name: 'cc'}];
array.forEach((item, index) => {
forEach[index].sex = '男'
});
var array = [{name: 'zz'}, {name: 'yy'}, {name: 'cc'}];
var ttt = array.map((item, index) => {
item.sex = '男'
return item
});
// 如果不想改变原数组,返回一个新对象
var array = [{name: 'zz'}, {name: 'yy'}, {name: 'cc'}];
var ttt = array.map((item, index) => {
return {
...item,
key: '男'
}
});
// 既然map返回的是一个新数组,那我们用链式调用看看
var array = [2, 3, 5];
var arr2 = array.map(value => value * value).filter(value => value > 10);
// console.log(arr2)
// [25]
// 总结
forEach()可以做到的东西,map()也同样可以。反过来也是如此(只是比较繁琐)。
map()会分配内存空间存储新数组并返回,forEach()不会返回数据。
forEach()允许callback更改原始数组的元素。map()返回新的数组(也可以改变原始数组)。
// for...in for...of 区别
// for of无法循环遍历对象
var userMsg = {
0: 'nick',
1: 'freddy',
2: 'mike',
3: 'james'
};
for(var key in userMsg){
console.log(key, userMsg[key]);
}
console.log('-----------分割线-----------');
for(var item of userMsg){
console.log(item);
}
// 遍历输出结果不同
var arr = ['nick','freddy','mike','james'];
for(var key in arr){
console.log(key);
}
console.log('-----------分割线-----------');
for(var item of arr){
console.log(item);
}
//for in 会遍历自定义属性,for of不会
var arr = ['nick','freddy','mike','james'];
arr.name = "数组";
for(var key in arr){
console.log(key+': '+arr[key]);
}
console.log('-----------分割线-----------');
for(var item of arr){
console.log(item);
}
二 ES5 和 ES6 处理数据的区别
1.处理数组
// includes(es6) 判断数组里面是否包含某个值
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
// for...in..(es5) 遍历数组
var arr= [0,1,2,3]
for(var i in arr) {
console.log(i)
}
// for...of..(es6) 遍历数组
var arr= [0,1,2,3]
for(var i of arr) {
console.log(i)
}
//find(es6) 返回数组的第一个匹配的元素的值
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
// findindex(es6) 返回数组的第一个匹配的元素的位置值
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
console.log(found);
// ...扩展运算符(es6) 扩展运算符(spread)是三个点(...),将一个数组转为用逗号分隔的参数序列。
console.log(...[1, 2, 3])
// 1 2 3
// flat(es6) 地板函数可以把多维数组变成一维的
[1, 2, [3, 4]].flat(index) // 想展开几级数组就设置index为多少
[1, [2, [3]]].flat(Infinity) // 如果想展开所有的数组传值就设置为Infinity
// reverse(es5) 反转数组
[1,2,3].reverse()
2.处理对象
// for...in.. 遍历对象
// 正常
var obj= {'a':1,'b':2,'c':3,'d':4}
for(var i in obj) {
console.log(i)
}
// 变态(会把原型上的属性也遍历出来,所有有时候用for循环更好)
Array.prototype.myfun = () => {
alert('myfun')
}
var arr= [0,1,2,3]
for(var i in arr) {
console.log(arr[i])
}
// Object.keys(obj)获取对象的所有key返回一个数组集合
var obj = {'name': 'xiaoming', 'age': 18};
console.log(Object.keys(obj))
// Object.values(obj)获取对象的所有value返回一个数组集合
var obj = {'name': 'xiaoming', 'age': 18};
console.log(Object.values(obj))
3.处理字符串
// substr 字符串截取
let month = "07" // 想月份显示的时候不带0
if (month.substr(0, 1) === '0') {
month = month.substr(1, 2)
}
// indexOf和lastIndexOf(es5) 字符串第一次和最后一次出现的位置
let str="Hello world, welcome to the universe.";
console.log(str.indexOf("welcome"))
let str="I am from runoob,welcome to runoob site.";
console.log(str.lastIndexOf("runoob"));
// split 分割字符串
"2019-08-06".split('-') // 返回分割数组 ["2019", "08", "06"]
三 模块的导入和导出 import和export
-
先看看常用的jquery是怎样来封装模块的
// 一个大的闭包来定义jquery对象,然后把大对象挂载在window全局变量上 (function(window, undefined) { var jQuery = function() {} // ... window.jQuery = window.$ = jQuery; })(window);
-
我们看看AMD和CMD是怎样来封装模块的
// AMD代表require.js(前置加载) // module1.js define(function (require, exports, module) { //内部变量数据 var data = 'atguigu.com' //内部函数 function show() { console.log('module1 show() ' + data) } //向外暴露 exports.show = show }) define(['module1'], function(module1){ ... return { ... }; }); // CMD代码sea.js(按需加载) // module1.js define(function (require, exports, module) { //内部变量数据 var data = 'atguigu.com' //内部函数 function show() { console.log('module1 show() ' + data) } //向外暴露 exports.show = show }) // main.js : 主(入口)模块 define(function (require) { var m1 = require('./module1') m1.show() }) // CommonJs:node 的支持格式 // 文件名: foo.js var $ = require('jquery'); var _ = require('underscore'); // methods function a(){}; // 私有方法,因为它没在module.exports中 (见下面) function b(){}; // 公共方法,因为它在module.exports中定义了 function c(){}; // 公共方法,因为它在module.exports中定义了 // 暴露公共方法 module.exports = { b: b, c: c };
-
让我们看es6原生的import和export是如何来导入导出模块的
// export circle.js // 第一组 export default function crc32() { // 输出 // ... } import crc32 from 'crc32'; // 输入 // 第二组 export function crc32() { // 输出 // ... }; import {crc32} from 'crc32'; // 输入 // 上面代码的两组写法,第一组是使用export default时,对应的import语句不需要使用大括号;第二组是不使用export default时,对应的import语句需要使用大括号。 // export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。 //上面写法是逐一指定要加载的方法,整体加载的写法如下。 export function crc31() { // 输出 // ... }; export function crc32() { // 输出 // ... }; import * as circle from './circle'; //本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。所以,下面的写法是有效的。 // modules.js function add(x, y) { return x * y; } export {add as default}; // 等同于 // export default add; // app.js import { default as foo } from 'modules'; // 等同于 // import foo from 'modules'; // 正是因为export default其实只是输出了一个default的变量;所以后面就不可以再跟变量声明的语句; //正确 export var a=1; //正确 var a=1; export default a; //错误; export default var a=1; 上面的代码中export default a其实是将a 赋值给default;所以最后一种语法报错; 所以本质上export dafault 是将变量赋值给default所以可以直接将值写在后面; export default 42 //正确;但是直接export 42就是错误; 上面的代码中export default a其实是将a 赋值给default;所以最后一种语法报错; 所以本质上export dafault 是将变量赋值给default所以可以直接将值写在后面;
四 数据请求ES6 promise和ES7 asyc...await
-
原始异步请求(回调地狱)
$.ajax({ url: '', dataType:'json', success: function(data) { // 获取data数据 传给下一个请求 var id = data.id; $.ajax({ url:'', data:{"id":id}, success:function(){ // ..... } }); } });
- promise
var ajaxPromise = new Promise(function(resolve){
resolve();
});
ajaxPromise.then(function(){
$.ajax({
url:'',
dataType:'json',
success: function(data) {
var id = data.id;
return id;
}
})
}).then(function(id){
$.ajax({
url:'',
dataType:'json',
data:{"id":id},
success: function(data){
console.log(data);
}
})
});
// 定义
function testPromise(ready) {
return new Promise(function(resolve,reject){
if(ready) {
resolve("hello world");
}else {
reject("No thanks");
}
});
};
// 方法调用
testPromise(true).then(function(msg){
console.log(msg);
},function(error){
console.log(error);
});
// Promise.all Promise.all可以接受一个元素为Promise对象的数组作为参数,当这个数组里面所有的promise对象都变为resolve时,该方法才会返回。
var promise1 = new Promise(function(resolve){
setTimeout(function(){
resolve(1);
},3000);
});
var promise2 = new Promise(function(resolve){
setTimeout(function(){
resolve(2);
},1000);
});
Promise.all([promise1,promise2]).then(function(value){
console.log(value); // 打印[1,2]
});
// Promise.race
// `delay`毫秒后执行resolve
function timerPromise(delay){
return new Promise(function(resolve){
setTimeout(function(){
resolve(delay);
},delay);
});
}
// 任何一个promise变为resolve或reject 的话程序就停止运行
Promise.race([
timerPromise(1),
timerPromise(32),
timerPromise(64),
timerPromise(128)
]).then(function (value) {
console.log(value); // => 1
});
- asyc 用同步的方式来写异步
function doubleAfter2seconds(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 * num)
}, 2000);
})
}
async function testResult () {
console.log('内部调用前') // 2
let result = await doubleAfter2seconds(30);
console.log(result); // 4
console.log('内部调用后') // 5
}
console.log('外部调用前') // 1
testResult();
console.log('外部调用后') // 3
// --- 依次输出
// 外部调用前
// 内部调用前
// 外部调用后
// --- 2s 之后输出
// 60
// 内部调用后
分析一下上面的执行顺序:
1、首先打印输出外部调用前,同步代码,顺序执行。
2、然后调用方法testResult(),打印输出内部调用前,同步代码,顺序执行。
3、再执行异步方法doubleAfter2seconds,
1>如果没用await关键字,此后的执行顺序应该是
内部调用后,外部调用后,2s 之后输出60
因为异步方法不阻塞其他代码的执行,最后再输出60
2>这里使用了await关键字,所以到这里后会等待异步返回结果,再往下执行。
4、当testResult函数内部await阻塞执行后,不会影响到testResult函数外面
async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。
所以,在调用testResult函数后,会继续向下执行,打印输出外部调用后
5、当2s之后,异步函数doubleAfter2seconds执行完成,返回结果,
打印输出60
6、因为await将异步变成同步,所以在输出60后,同步执行,再输出内部调用后
五 ES6新技巧
1. 解构赋值
数组的解构
// 基本
let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3
// 可忽略
let [a, , b] = [1, 2, 3];
// a = 1
// b = 3
// 嵌套解构
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3
// 字符串解构
let [a, b, c, d, e] = 'hello';
// a = 'h'
// b = 'e'
// c = 'l'
// d = 'l'
// e = 'o'
对象的解构
// 基本
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
// 可嵌套可忽略
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { }] } = obj;
// x = 'hello'
// 不完全解构
let obj = {p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
// x = undefined
// y = 'world'
// 剩余运算符
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40}
2.模板字符串
// 包含普通变量
let w = 'World'
console.log(`Hello ${w}`)
// 包含函数
function fn() {
return "Hello World";
}
`foo ${fn()} bar`
六 总结
ES6是对ES5功能的简化和扩展,es6的功能es5内的函数都可以实现。但是掌握了es6会在开发过程中让代码更加清晰和整洁。是需要掌握的一项技能。