数组方法
forEach()
forEach()是一个遍历方法,该方法会将数组中的每个元素都当作参数传递给****回调函数执行一次,这个回调函数又称作迭代器函数;
这个迭代器函数在很多方法中都有使用到,它有三个默认参数:
第一个:代表当前数组中正在处理的元素;
第二个:代表当前数组中正在处理的元素的索引,这个参数是可选的;
第三个:代表当前方法调用的数组,
直至到将数组的所有元素都传递入回调函数。
var numbers = [4, 9, 16, 25];
numbers.forEach(function(el,index){
//...对每个元素进行一些操作,比如push到另外一个数组
})
解释:会将4,9,16,25依次传入回调函数,4传入的时候,el是4,index是0,之后el是9,index是1,之后el是16,index是2,再之后el是25,index是3,此时数组numbers遍历结束,那么forEach()就不再执行;
注意的是,forEach中,break、return等关键字是不起作用的,forEach会将遍历整个数组
map()
map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一次提供的函数后的返回,其回调函数也是一个迭代器函数,因此也有3个默认参数,分别为:当前正在处理的元素,当前元素的索引,当前数组
const array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(function(el){
//...
});
console.log(map1);
// expected output: Array [2, 8, 18, 32]
filter()
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
const ages = [32, 33, 16, 40];
const age = ages.filter(function(el){
return el>18
});
//age:[32, 33, 40]
every()和some()
every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供),如果全部满足条件,则返回true,只要有一个不满足,则会直接返回false,并且剩下的不再进行检测;
some()方法则和every()不同,只要有一个符合指定条件,就会返回true,并且剩下的不在进行检测,如果全部不通过则返回false
const ages = [32, 33, 16, 40];
const age = ages.some(function(el){
return el>18
});
//age:true
reduce()
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
arr.reduce(function(prev,cur,index,arr){
...
}, init);
arr: 表示原数组;
prev: 表示上一次调用回调时的返回值,或者初始值 init;
cur: 表示当前正在处理的数组元素;
index: 表示当前正在处理的数组元素的索引,若提供 init 值,则索引为0,否则索引为1;
init: 表示初始值。
示例
先提供一个原始数组:
var arr = [3,9,4,3,6,0,9];
求和
let sum = arr.reduce((prev,cur)=>{
return prev + cur
},0)
取最大值
let maxNum = arr.reduce((prev,cur)=>{
return Math.max(prev,cur)
})
数组去重
let arr = arr.reduce((prev,cur)=>
prev.indexOf(cur) < 0 && prev.push(cur);
return prev;
},[])
let和const
let 声明的变量只在 let 命令所在的代码块内有效。const 声明一个只读的常量,一旦声明,常量的值就不能改变。
console.log(a); //报错,因为let定义的变量不会变量提升,必须在定义之后才能使用
let a = 10;
{
let a = 2;
console.log(a); // 2
}
console.log(a); //10
const PI = 3;
PI = 4; //报错,不允许修改
const array = []
array.push(1) //[1]
对应引用类型的const,const的意义在于不能改变它的类型,比如定义的是数组,不能将它变成对象
模版字符串
使用的是一对反引号(`),在内通过${},嵌入变量,模版字符串的好处就是在花括号内可以进行简单的逻辑运算和不再需要使用+号做拼接
let name = "oliver"
let element = `
<h1>${ax(name)},${name}</h1>
<p>你好</p>
<ul>
<li>1</li>
</ul>
`
function ax(a){
return a.toUpperCase();
}
箭头函数
单参数写法
//es5的常规写法
let ax = function (num){
return num * 2;
}
console.log(ax(2));
//示例1
let ax2 = (num) => {
return num * 2;
}
console.log(ax2(2));
//示例2
let ax3 = (num) => num * 2
console.log(ax2(2));
//示例3
let ax3 = num => num * 2
console.log(ax2(2));
多参数写法
//多参数前面必须有括号
let ax4 = (num,mun2) => num * mun2
console.log(ax4(2));
关于this的绑定
箭头函数的this是在定义函数时绑定的,不是在执行过程中绑定的。简单的说,函数在定义时,this就继承了定义函数的对象。
因此,定义箭头函数的时候,它里面的this已经固定完成,因此就没有那么多杂七杂八的问题;关于普通函数体内的this指向问题请看本知识库中的《彻底理解JS中的this指向》
具体示例:
const obj = {
a: function() {
console.log(this) //指向的是a
window.setTimeout(() => {
console.log(this) //指向的是a
}, 1000)
setTimeout(function(){
console.log(this) //指向的是window
},1000)
}
}
obj.a(obj)
增强对象字面量
对象字面量: 在ES5中当对象属性名和作用域中的变量名相同,那么原来的时候需要将key和value各写一次。但是,在ES6中只需要写一次,ES6会自动的帮你完成键到值的赋值。
对象方法字面量: 在ES5中当对象属性名对应的是一个方法,那么原来的时候需要写function关键字。但是,在ES6中可以完全省略。
//ES5常规写法
function createBookShop(array){
return {
array:array,
inventory:function(){
return this.array.reduce((title,book) => title + book.price,0);
},
priceForTitle:function(title){
return this.array.find(book => book.title === title).price;
}
}
}
//ES6简写
function createBookShop(array){
return {
//属性的简写,当key和value是相同值的时候,可以只写一个
array,
//方法的简写,可以将冒号和function关键字去掉,直接写方法
inventory(){
return this.array.reduce((title,book) => title + book.price,0);
},
priceForTitle(title){
return this.array.find(book => book.title === title).price;
}
}
}
展开运算符
MDN原文:可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。
个人理解:首先明确,这是一个运算符,它可以使得在写JS时,更加灵活,可以代替很多原来的方法,使得实现不再需要大段方法,只需要一个展开运算符就可以实现;
拷贝
可以将内容拷贝到新对象或数组上
let arr = [1, 2, 3]
let newArr = [...arr] //[1, 2, 3]
let obj = {a:1}
let newObj = {...a} //{a:1}
合并
可以将多个对象或者数组合并到一起,具体执行方式和Object.assign()一直,也就是,如果目标对象上的属性和新对象上的属性重复,那么后者将会覆盖前者
let obj1 = {
b: 1,
c: 10
}
let obj2 = {
a: 3,
b: 5
}
let target1 = {...obj1,...obj2} //{a: 3, b: 5, c: 10}
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6]
let target2 = [...arr1, ...arr2] //[1, 2, 3, 4, 5, 6]
代替apply
//假设有一个函数
function f(a,b,c){
console.log(a,b,c)
}
let args = [1,2,3];
// 以下三种方法结果相同
f.apply(null,args)
f(...args)
f(1,2,3)
//可以将参数转成数组
//代替 [].slice.call(arguments)
function f2(...args){
console.log(args) // [1,2,3]
}
f2(1,2,3)
和解构一起使用
let a = [1,2,3,4,5,6]
let [c,...d] = a
console.log(c); // 1
console.log(d); // [2,3,4,5,6]
函数参数默认值
**函数默认参数允许在没有值或undefined被传入时使用默认形参,**具体使用就是在形参中直接通过"="赋值一个值;
function first(x = 1, y = 2) {
console.log("x:"+x ,"y:"+ y);
}
first(); //x:1,y:2
first(100); //x:100,y:2
直接在形参里给参数赋值,假如方法的没有传递参数,那么对应的参数就是采用默认值进行赋值,如果有参数,那么传递的参数将覆盖掉默认值;
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5, 2));
// expected output: 10
console.log(multiply(5));
// expected output: 5
function test(num = 1) {
console.log(typeof num);
}
test(); // 'number' (num is set to 1)
test(undefined); // 'number' (num is set to 1 too)
// test with other falsy values:
test(''); // 'string' (num is set to '')
test(null); // 'object' (num is set to null)
默认参数可用于后面的默认参数
function greet(name, greeting, message = greeting + ' ' + name) {
return [name, greeting, message];
}
greet('David', 'Hi'); // ["David", "Hi", "Hi David"]
greet('David', 'Hi', 'Happy Birthday!'); // ["David", "Hi", "Happy Birthday!"]
示例中message的默认参数是第一个形参第二个形参组成的,也就代表了,默认参数是可以使用其之前定义的形参的;
解构
解构通俗点说,就是通过一种特定格式,快捷的读取对象/数组中的数据的方法;
解构对象
var User = {
'name' : 'oliver',
'age' : 18
};
//es5中读取数据
console.log( User.name, User.age ); //oliver, 18
//es6中的解构
let { name, age } = User; //执行了解构
console.log( name, age ); //oliver, 18
如果解构出来的变量,在对象中找不到,那么值就是undefined
let { name1, age1 } = User; //执行了解构
console.log( name1, age1 ); //underfined, underfined
- 右边如果是对象,左边解构的语法也要用对象格式, 如果解构的数据是数组,左边就用数组格式;
- name, age是局部变量;
- 解构出来的变量 一定要是 对象中能够查找到的属性名;
- 如果解构出来的属性在对象中查找不到,值为undefined;
解构出来的变量虽然是跟属性名称相同,但是他不再是属性,而是外部的变量,这个变量名也可以被修改, 不一定要跟属性同名,给解构出来的变量重新命名,方式如下:{ 旧的变量名 : 新的变量名}
var User = {
name : 'oliver',
age : 18
};
let { name:name1 , age:age1 } = User;
console.log( name1, age1 ); //oliver, 18
如果解构出来的变量不清楚是不是存在对象中,并且希望,如果存在那么就使用存在的值,如果不存在就使用自定义的默认值,这种时候就可以使用默认参数
var User = {
'name' : 'oliver',
'age' : 18
};
let { name, age, sex} = User;
console.log( name, age ,sex); //oliver, 18,underfined
//可以采用参数默认值
var User = {
'name' : 'oliver',
'age' : 18
};
let { name, age, sex="男"} = User;
console.log( name, age ,sex); //oliver, 18,男
解构数组
let arr = [ 10, 20, 30, 40 ];
// 数组用[]解构
let [ one, two ] = arr;
console.log( one, two ); //10 20
let arr = [ 10, 20, 30, 40 ];
// 对于不需要解构的值,可以用逗号占位
let [ , , , a ] = arr;
console.log( a ); //40
那如果是数组中有嵌套怎么办?同理,解构中也使用嵌套;
let arr = [ 10, [ 20, 30 ], 40 ];
console.log( arr[1][1] ); //30
let [ one, two, three, four ] = arr; //有没有let都可以
console.log( one, two, three, four ); //10 [20, 30] 40 undefined
[ one, [ two, three ], four ] = arr; //前面不要let,否则会报重定义错误
console.log( one, two, three, four ); //10 20 30 40
交换变量
在以前,我们交换变量,一般的做法是借助第三个变量, 而现在有了解构之后,我们可以直接交换
let a = 10, b = 20;
[ a, b ] = [ b, a ]; //前面不要加let,否则会报 重定义错误
console.log( a, b ); //20, 10
数组和对象混合
const people = [
{name:"aaa",age:111},
{name:"bbb",age:222},
{name:"ccc",age:333},
]
const [{age}] = people; //将数组中索引为0的对象解构出来
console.log(age); ///111
解构表达式传参
let User = {
name : 'oliver',
age : 18
};
function show( obj ){
console.log( obj ); //obj其实就是User对象
console.log( obj === User ); //true
//函数中可以访问在参数中解构出来的变量
console.log( name, age ); //oliver, 18
}
show( { name, age } = User ); //表达式传值,整个表达式的值 取决于右边,所以把User传给了obj
console.log( name, age ); //oliver, 18
//或者
function show( {name,age} ){ . //直接在参数的地方进行解构
//函数中可以访问在参数中解构出来的变量
console.log( name, age ); //oliver, 18
}
show( User );
嵌套对象的解构
let User = {
name : 'oliver',
age : 18,
class : {
group1 : {
one : '王超',
two : '马汉'
},
group2 : {
one : '张龙',
two : '赵虎'
}
}
};
//在User对象解构class, 在class中解构group1
let { class : { group1 } } = User;
console.log( group1.one ); //王超
console.log( group1.two ); //马汉
//在User对象解构class, 在class中解构group1, 在group1中解构one, two
({ class : { group1 : { one, two } } } = User);
console.log( one, two ); //王超, 马汉
//重命名group1 为 diyizu
({ class : { group1 : diyizu } } = User);
console.log( diyizu.one ); //王超
console.log( diyizu.two ); //马汉
//three 赋默认值
({ class : { group1 : { one, two, three = '展昭' } } } = User);
console.log( one, two, three );//王超,马汉,展昭
解构练习题
练习题1
对象与数组的混合解构,说出每个对应变量的值,能看懂,基本就算掌握了
let User = {
name : 'oliver',
age : 18,
class : {
group1 : {
one : '王超',
two : '马汉'
},
group2 : {
one : '张龙',
two : '赵虎'
}
},
arr : [ 10, 20, 30 ]
};
let { name, age, class : { group1, group2 }, arr : [ one, two, three ] } = User;
输出:oliver 18 王超 马汉 张龙 赵虎 10 20 30
练习题2
使用解构将数组 [ [1,2] , [10,1] , [0,9] ]转换成[ {x:1,y:2} , {x:10,y:1} , {x:0,y:9} ]
const array = [ [0,1] , [10,1] , [0,9] ]
//map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
let newArray = array.map((el) => {
let [x,y] = el;
console.log(x,y);
return {x:x,y:y} //简写:{x,y}
})
console.log(newArray);
//优化:解构可以直接写在参数里
let newArray = array.map(([x,y]) => {
return {x,y} //简写:{x:x,y:y}
})
console.log(newArray);
练习题3
将数组[1, [2], [3, [4, [5]]]]拍平,变成[1, 2, 3, 4, 5]
思路:看到多重嵌套,不可避免的就涉及到了递归的问题,通过递归将数的每个元算判断是否是数组;
const arr = [1, [2], [3, [4, [5]]]];
//给Array扩展一个flat
Array.prototype.flat = function () {
let arr = function (curarr) {
return curarr.reduce((tol, cur)=>{
return Array.isArray(cur) ? [...tol, ...arr(cur)] : [...tol, cur]
},[])
}
return arr(this)
}
console.log(arr.flat())