[TOC]
数组(Array)也是一种复合数据类型,可以存储多种类型的数据。数组存储的数据称为元素,数组中存储的是有序元素,每一个元素都有对应的索引(index),索引是从0开始的正整数,0对应数组第一个元素,依次后推。数组中的元素是通过索引来操作的。
创建数组的格式:
添加数组格式:
修改数组元素同添加数组格式,只是把需要修改的元素通过索引修改即可。
读取数组某元素格式:
数组中的元素是可以非连续添加的,但是尽量不要添加非连续元素
length:
const arr = [1, 2, 3];
console.log(arr); //1,2,3
arr[0] = 3;
console.log(arr[0]); //3
arr[arr.length] = 4;
console.log(arr.length); //4
console.log(arr); //1,2,3,4
console.log(typeof arr); //object
可以通过 for 循环来遍历数组中的元素。
const arr = ["小明", "小方", "小兰"];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}; //正向遍历
for (let i = arr.length - 1; i >= 0; i--) {
console.log(arr[i]);
}; //反向遍历
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
};
};
//打印18岁同学的姓名和年龄
const arr1 = [
new Person("小明", 15),
new Person("小方", 18),
new Person("小兰", 16)
];
for (let i = 0; i < arr1.length; i++) {
if (arr1[i].age === 18) {
console.log(arr1[i].name + "," + arr1[i].age);
};
};
除了 for 循环遍历数组,还有 for-of 方法也可以遍历数组(for-of不仅可以遍历数组,它可以遍历一切可迭代对象,但是只能满足从前往后方式遍历)。数组中有几个元素, for-of循环体就循环多少次,每次循环会把元素赋值给变量。
格式:
const arr = ["小明", "小方", "小兰"];
for (let value of arr) {
console.log(value);
};
for (let value of "hello") {
console.log(value);
};
Array.isArray():
at():
concat():
indexOf():
lastIndexOf():
join():
slice():
const arr = ["小白", "小绿", "小蓝", "小紫", "小绿", "小黑"];
const arr1 = ["小明", "小兰", "小方"];
console.log(Array.isArray(arr)); //true
console.log(arr.at(0)); //小白
console.log(arr.at(3)); //小紫
console.log(arr.concat(arr1));
console.log(arr.indexOf("小绿", 3)); //4
console.log(arr.lastIndexOf("小绿", 3)); //1
console.log(arr.indexOf("小兰")); //-1
console.log(arr.lastIndexOf("小兰")); //-1
console.log(arr.join("-"));
console.log(arr.slice(0, 2)); //小白,小绿
console.log(arr.slice(0, -2)); //小白,小绿,小蓝,小紫
对象的复制必须要创建一个新的对象。当调用数组的 slice() 方法时,会新建一个对象存储截取的元素,可以完成复制。
const arr = ["小明", "小兰", "小方"];
const arr1 = arr; //这不是复制,修改元素后对两个数组都有影响
console.log(arr === arr1); //true,指向相同的对象
const arr3 = arr.slice(); //调用slice方法时会新创建一个对象用来存储截取的元素
console.log(arr === arr3); //false,两个数组指向的不是同一个对象
浅拷贝(shallow copy):
深拷贝(deep copy):
const arr = [{ name: "小明" }, { name: "小兰" }, { name: "小方" }];
const arr1 = arr.slice(); //浅拷贝,只复制对象本身
console.log(arr === arr1); //false
console.log(arr[0] === arr1[0]); //true
const arr2 = structuredClone(arr); //专门用来深拷贝的方法
console.log(arr === arr2); //false
console.log(arr[0] === arr2[0]); //false
arr2[0].name = "小红";
console.log(arr[0].name); //小明
console.log(arr2[0].name); //小红
浅复制(浅拷贝)的其他方式:
对对象的浅复制:
const arr = ["小明", "小兰", "小方"];
const arr1 = [...arr];
console.log(arr1);
const arr2 = ["小红", ...arr, "小绿"];
console.log(arr2);
const obj = {
name: "小明",
age: 18
};
const obj1 = Object.assign({}, obj);
console.log(obj1);
const obj2 = { ...obj };
console.log(obj2);
const obj3 = { name: "小兰", ...obj, address: "南京" }; //小兰会被小明覆盖
console.log(obj3);
push():
pop():
unshift():
shift():
splice():
reverse():
const arr = ["小明", "小兰", "小方"];
arr.push("小红", "小绿"); //小明,小兰,小方,小红,小绿
arr.pop(); //小明,小兰,小方,小红
arr.unshift("小白", "小黑"); //小白,小黑,小明,小兰,小方,小红
arr.shift(); //小黑,小明,小兰,小方,小红
console.log(arr);
const arr1 = ["北京", "上海", "广州", "深圳"];
arr1.splice(1, 2, "南京"); //北京,南京,深圳
arr1.splice(2, 0, "西安", "杭州"); //北京,南京,西安,杭州,深圳
arr1.splice(2, 2); //北京,南京,深圳
console.log(arr1);
const arr2 = [1, 2, 3, 4, 5];
let result = arr2.reverse();
console.log(result); //5,4,3,2,1
//去除数组中重复元素
const arr = [1, 2, 1, 1, 3, 4, 5, 5, 6, 7, 4];
/*方法一
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1);
j -= 1;
};
};
};
console.log(arr);
*/
/*方法二
for (let i = 0; i < arr.length; i++) {
let index = arr.indexOf(arr[i], i + 1);
if (index != -1) {
arr.splice(index, 1);
i--;
};
};
console.log(arr);
*/
//不破坏原数组
const newArray = [];
for (let ele of arr) {
if (newArray.indexOf(ele) === -1) {
newArray.push(ele);
};
};
console.log(newArray);
逻辑简单,运行缓慢,适合数据量小的排序。
const arr = [9, 5, 2, 8, 4, 6, 3, 5, 7, 1];
//选择排序
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
let temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
};
};
};
console.log(arr);
const arr = [9, 5, 2, 8, 4, 6, 3, 5, 7, 1];
//冒泡排序
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
};
};
};
console.log(arr);
如上文所写的排序代码仅仅局限应用于一个数组,可以将其封装为一个函数,将其中的数组作为形参,然后将其他任意数组作为实参调用此函数,就可以实现此功能的重复使用,而且大幅减少代码量。
如果一个函数的参数或返回值是函数,那么该函数称为高阶函数。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
};
};
let person = [
new Person("小明", 21),
new Person("小方", 18),
new Person("小兰", 16),
new Person("小红", 20)
];
function filter(arr, cb) {
const newArray = [];
for (let i = 0; i < arr.length; i++) {
if (cb(arr[i])) {
newArray.push(arr[i]);
};
};
return newArray;
};
let result = filter(person, a => a.age <= 18);
const arr1 = [1, 5, 4, 2, 3, 9, 8, 6];
result = filter(arr1, a => a <= 6);
console.log(result);
function fn() {
console.log("hello");
return "hello";
};
function outer(cb) {
return () => {
console.log("记录日志");
const result = cb();
return result;
};
};
let result = outer(fn);
result();
function test() {
console.log("test");
return "test";
};
let newTest = outer(test);
newTest();
闭包就是能访问到外部函数作用域中变量的函数。闭包是为了隐藏一些不想被其他人访问到的数据。
构成闭包的三个要件:
//闭包的简单例子
function fn() {
let num = 0;
return () => {
num++;
console.log(num);
};
};
const result = fn();
console.log(result);
result();
result();
result();
result();
result();
闭包原理:函数的作用域在函数被创建时就已经确定(词法作用域)。和调用位置无关。闭包利用的就是词法作用域。
let a = "全局变量a";
function fn() {
console.log(a);
};
function fn1() {
let a = "fn1中的a";
fn();
};
fn1(); //全局变量a
function fn2() {
let a = "fn2中的a";
function fn3() {
console.log(a);
};
return fn3;
};
const result = fn2();
console.log(result);
result(); //fn2中的a
闭包的生命周期:
相较于类,闭包会比较浪费内存空间。当执行次数少时使用闭包,当需要创建大量实例时使用类。
递归: 递归的核心思想是将大问题分解为小问题,小问题解决后大问题也就解决了。
递归的两个要件:
//阶乘求解
function jiecheng(num) {
if (num === 1) {
return 1;
};
return jiecheng(num - 1) * num;
};
const result = jiecheng(5);
console.log(result); //120
//斐波那契数列
/*
1 2 3 4 5 6 7 8 9 10
1 1 2 3 5 8 13 21 34 55
*/
function fn(num) {
if (num < 3) {
return 1;
};
return fn(num - 1) + fn(num - 2);
};
const result = fn(6);
console.log(result); //8
sort():
const arr = [1, 2, 5, 4, 8, 6, 3, 9, 7, 10];
arr.sort((a, b) => a - b); //1,2,3,4,5,6,7,8,9,10
// arr.sort((a, b) => b - a); //10,9,8,7,6,5,4,3,2,1
console.log(arr);
forEach():
const arr = [1, 2, 5, 4, 8, 6, 3, 9, 7, 10];
arr.forEach((element, index, array) => {
console.log(element, index, array);
});
filter():
const arr = [1, 2, 5, 4, 8, 6, 3, 9, 7, 10];
const result = arr.filter((ele) => ele % 2 === 0);
console.log(result); //2,4,8,6,10
map():
const arr1 = ["小明", "小红", "小方", "小兰"];
const arr2 = [1, 2, 5, 4, 8, 6, 3, 9, 7, 10];
const result1 = arr1.map((ele) => "<li> " + ele + "</li>");
const result2 = arr2.map((ele) => ele * 2);
console.log(result1); //['<li> 小明</li>', '<li> 小红</li>', '<li> 小方</li>', '<li> 小兰</li>']
console.log(result2); //[2, 4, 10, 8, 16, 12, 6, 18, 14, 20]
reduce():
const arr = [1, 2, 5, 4, 8, 6];
const result = arr.reduce((a, b) => a + b , 10);
/*
1,2 ----3
3,5 ----8
8,4 ----12
12,8 ---20
20,6 ---26
*/
console.log(result); //46 26+10
function fn(a, b, ...args) {
return args.reduce((a, b) => a + b);
};
const result = fn(1, 2, 4, 1);
console.log(result); //5
根据函数调用方式不同,函数中this的指向也不同:
调用函数的call()和apply()方法,与普通调用函数一致,返回的都是函数对象。
格式:
两个方法的第一个参数都是this的指向,如果函数有形参,那需要在第一个参数后开始编写实参。不同之处在于:
function fn(a, b) {
console.log(a, b, this);
};
const obj = { name: "小明", age: 18 };
fn.call(obj, 1, 2);
fn.apply(obj, [1, 2]);
函数的bind()方法会新建一个函数:
function fn(a, b) {
console.log(a, b, this);
};
const obj = { name: "小明", age: 18 };
const newFn = fn.bind(obj, 1, 2); //将新函数的this绑定为Obj,将参数绑定为1,2
newFn(3, 4); //1,2,obj
数组的解构: 数组的解构赋值也就是将数组元素拆开赋值给指定的变量。
const arr = [1, 2, 3];
[a, b, c] = arr;
console.log(a, b, c); //解构赋值
解构数组时可以通过 ...参数 的方式来获取多余的参数,放在一个数组中。
const arr = [1, 2, 3, 4, 5, 6];
[a, b, c, ...d] = arr;
console.log(a, b, c, d); //1,2,3,[4,5,6]
可以通过解构赋值快速的交换两个变量的值。
let a1 = 10;
let a2 = 20;
[a1, a2] = [a1, a2];
console.log(a1, a2); //20,10
const arr = [30, 40];
[arr[0], arr[1]] = [arr[1], arr[0]];
console.log(arr); //[40,30]
可以通过以下方法设定默认值,如果数组中有该元素则被数组中元素覆盖,如若没有则变量的值就是默认值。
const arr = [1, 2, 3];
[a, b, c = 5, d = 10] = arr;
console.log(a, b, c, d); //1,2,3,10
若一个数组中的元素也是数组,那我们称该数组为二维数组。
const arr = [["小明", 18, "男"], ["小红", 12, "女"]];
[[name, age, gender], obj] = arr;
console.log(name, age, gender, obj); //小明,18,男,["小红", 12, "女"]
对象的解构: 与数组解构类似。
const obj = { name: "小明", age: 18, gender: "男" };
let { name, age, gender } = obj; //声明变量的同时解构对象
console.log(name, age, gender); //小明,18,男
//若想修改属性名,可以在属性名后加冒号重新命名。
let { name: a, age: b, gender: c, address: d = "南京" } = obj;
console.log(a, b, c, d); //小明,18,男,南京
序列化是将对象转换为可存储的格式。JS中对象的序列化一般为将对象转换为JSON格式,也就是转换为字符串。
序列化的用途:
序列化的方法:
const obj = {
name: "小明",
age: 18,
gender: "男"
};
const str = JSON.stringify(obj);
console.log(typeof str, str); //string {"name":"小明","age":18,"gender":"男"}
const str1 = '{"name":"小方","age":18,"gender":"女"}';
const obj1 = JSON.parse(str);
const obj2 = JSON.parse(str1);
console.log(typeof obj1, obj1); //object
console.log(typeof obj2, obj2); //object
console.log(obj === obj1); //false
可以通过与JSON字符串的转换来实现深复制:
const obj = {
name: "小明",
age: 12,
friend: {
name: "小红",
age: 13
},
};
const obj1 = JSON.parse(JSON.stringify(obj));
obj.friend.name = "小方";
console.log(obj.friend.name); //小方
console.log(obj1.friend.name); //小红
Map用来存储键值对结构的数据(key-value)。
Object所存储的数据也可以说是键值对结构。但是Map与Object存在著区别:
创建:
属性和方法:
const map = new Map();
const obj = { name: "小方" };
map.set("name", "小明");
map.set("age", 18);
map.set(obj, 123);
console.log(map.get(obj));
console.log(map);
console.log(map.size); //3
console.log(map.delete("age")); //true
console.log(map.has("age")); //false
// map.clear();
将map转换为数组:
const map = new Map();
map.set("name", "男");
map.set("age", 18);
map.set("gender", "男");
const arr = Array.from(map);
const arr1 = [...map];
console.log(arr);
console.log(arr1);
遍历map:
const map = new Map();
map.set("name", "男");
map.set("age", 18);
map.set({}, () => { });
for (const [key, value] of map) {
console.log(key, value);
};
map.forEach((value, key, entry) => {
console.log(value, key, entry);
});
获取所有的key,value,entry:
const map = new Map();
map.set("name", "男");
map.set("age", 18);
map.set({}, () => { });
for (const key of map.keys()) {
console.log(key);
};
for (const value of map.values()) {
console.log(value);
};
for (const entry of map.entries()) {
console.log(entry);
};
Set用于创建一个集合,与数组类似,但是区别在于不能存储重复数据。
创建:
方法:
Math是一个工具类,为我们提供常用的数学常量和方法。
常用常量:
常用方法:
console.log(Math.abs(-5)); //5
console.log(Math.min(1, 5, 6, 23, 4)); //1
console.log(Math.max(1, 5, 6, 23, 4)); //23
console.log(Math.pow(2, 3)); //8
console.log(Math.sqrt(4)); //2
console.log(Math.floor(-3.2)); //-4
console.log(Math.ceil(3.3)); //4
console.log(Math.round(3.5)); //4
console.log(Math.trunc(3.3)); //3
console.log(Math.round(Math.random() * 4 + 2)); //2到6的任意值
JS中所有与时间有关的数据都由Date对象来表示。
创建:
Date对象的方法:
let d = new Date();
// d = new Date("2022-12-12T12:12:12");
console.log(d.getFullYear());
console.log(d.getMonth());
console.log(d.getDate());
console.log(d.getDay());
console.log(d.getTime());
console.log(Date.now());
日期的格式化:
参数:
let d = new Date();
console.log(d.toLocaleDateString());
console.log(d.toLocaleTimeString());
console.log(d.toLocaleString("zh-CN", { dateStyle: "long", timeStyle: "full" }));
JS中一共有五个包装类:
包装类可以将原始值临时的包装为一个对象,当对调用原始值的属性或方法时,JS解释器会临时将原始值包装为对应的对象,然后调用对象得到属性或方法。
let str = "很好";
str.name = "小明"; //临时将原始值包装为String对象
console.log(str.name); //undefined
字符串的方法:
字符串本质就是字符数组,很多方法与数组类似,如length获取数组长度,也可以通过索引获取字符串中对应的字符。
常用方法:
正则表达式就是一个规则,可以用来检查字符串是否符合定义的这个规则或者将符合规则的字符从字符串中提取出来,正则表达式也是对象。
创建:
方法:test(); ----- 检查字符串中是否符合规则
let reg = new RegExp("a", "i"); // i : 忽略大小写
let str = "abc";
let result = reg.test(str);
console.log(result); //true
正则表达式:
量词:
re.exec();
let re = /a(([a-z])c)/g;
let str = "abcajcakcalc";
let result = re.exec(str);
// console.log(result);
while (result) {
console.log(result[0], result[1], result[2]);
result = re.exec(str);
};
可以通过括号分组。i表示忽略大小写,g表示全局匹配。
//练习,输出字符串中的手机号码
let str = "adasdas15218493665dadasdsa13865489320fgdhgff17396458219";
let re = /1[3-9]\d{9}/g;
let re1 = /(1[3-9]\d)\d{4}(\d{4})/g;
let result1;
let result2;
while (result1 = re.exec(str)) {
console.log(result1[0]);
};
console.log("-----------");
while (result2 = re1.exec(str)) {
console.log(result2[1] + "****" + result2[2]);
};
字符串的正则方法:
本文章使用limfx的vscode插件快速发布