JS基础知识(三)数组,对象,字符串与部分新属性
一:数组,对象 部分常见方法可以去构造函数部分查看
(1) 数组
数组本质就是一个对象 所以对于一些对象的方法 数组也可以去使用 通过检查数组的类型就会发现数组的类型就是Object
数组的声明方法:
let arr = [1, 2, 3, 4, 5]
let arr1 = new Array(1, 2, 3, 4, 5)//构造函数式声明
数组常用方法:
let arr = [1, 2, 3];
//数组的赋值操作
arr[0] = 1
//将元素添加到数组的尾部 并返回数组的长度
arr.push(4);
//弹出数组末尾元素
arr.pop();
//添加新内容到数组开头 并且返回数组的长度
arr.unshift(0)
//arr.shift 删除第一个元素 
arr.shift()
//arr.splice(起始位置,删除几个元素)
arr.splice(1, 1)
//遍历数组
arr.forEach(item => console.log(item));
//fill填充数组
arr=[];
arr.fill(1) //将数组全部填充为1
//copyWithin复制数组的元素
let hhh=[1,2,3,4,5,6,7,8]
console.log(hhh.copyWithin(4,1,3));//(要粘贴的位置,复制目标的起始位置,复制目标的结束位置(不包括该位置))
常用方法:
push、pop、shift、unshift、slice、splice、map、filter、reduce
数组解构赋值:
//数组解构 将单元直批量赋值给变量的语法  本质是把数组元素赋值给变量
const arr=[100,60,80]
const [max,min,avg]=arr
console.log(max)//100
//交换两个变量
let a=1
let b=0;
[b,a]=[a,b]
console.log(a,b);//1//剩余参数解决 参数多 变量少问题
const [c,d,...arrr]=[1,2,3,4,5]
console.log(arrr);
//按需解构赋值
const [e,f,,r]=[1,3,4,5]
console.log(r);//5//多维数组解构
const arr1=[[1,2,3],1]
console.log(arr1[0][0]);// 1
const [t,y]=arr1
数组的查找方法
数组的查找方法和字符串的查找是一样的 可以理解为字符串本事就是一个字符数组 注意startsWith和endsWith方法不能使用
一些列用于检索的数组方法 const hd=['h','e','i','m','a','c','h','e','g','n','x','v','y','a','u','n']提前定义字符串
indexOf返回字符所在位置 如果找不到就返回-1 从字符串的左边开始查找console.log(hd.indexOf('d'));//返回检索字符所在的位置 找不到的返回负一 从左边开始查找includes有两个参数 参数一是要查询的位置 参数二是开始查找的位置console.log(hd.includes('h',8));//返回布尔类型 可以设置第二个参数从指定位置开始查找lastIndexOf返回字符串的位置console.log(hd.lastIndexOf('o'));//从最右边向左开始查找
数组的迭代器
1.什么是迭代器
迭代器是js中一种特殊的对象,它允许我们按顺序访问数组中的元素,这个迭代器对象是按照迭代器协议创建的next()方法 该方法要求返回一个钓鱼两个属性的对象: value属性(当前迭代的值) done属性(布尔值 表示迭代的状态 是否完成)
2.理解迭代器
可以理解迭代器为一个智能的指针,他知道当前指向哪个元素(value)并且知道当前迭代有没有完成(done)。
3.简单使用迭代器
迭代器工作原理:
const arr = [1, 2, 3];// 获取数组的迭代器对象
let iterator = arr.values(); 
// 使用迭代器逐个访问元素
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
利用迭代器包装的方法:
const arr = [1, 2, 3];// values() - 返回值的迭代器
for (let value of arr.values()) {console.log(value); // 1, 2, 3
}// keys() - 返回索引的迭代器
for (let key of arr.keys()) {console.log(key); // 0, 1, 2
}// entries() - 返回索引和值的迭代器
for (let [key, value] of arr.entries()) {console.log(key, value); // 0 1, 1 2, 2 3
}
4.迭代器的特点
- 一次性消费: 迭代器只能向前移动,不能后退
 - 惰性计算: 每次调用 
next方法才会计算下一个值 - 状态保持: 迭代器会记住当前位置
 
(2) 对象
声明对象
1.字面量直接创建:
let person = {name: 'Tom',age: 18,sayHi() {console.log('Hi, I am ' + this.name);}
}
console.log(person.name);
person.sayHi();
2.利用js内置方法构造对象
//2.new一个对象创建的是一个空对象   const obj2=new Object({name:2})
3.利用构造函数创建对象
构造函数的本质:任何函数都可以作为构造函数,通过 new 关键字调用
//构造函数是一种特殊的函数 作用是用来初始化对象的function Friden(name,age) {//第一个字母一定是大写开头 只能通过new执行this.name=namethis.age=age}const yuxuan=new Friden('yuxuan',18)//new的过程就是实例化console.log(yuxuan);
关于使用构造函数创建对象的一些问题
为什么能使用构造函数比如Friden()直接new 一个对象呢,当我们使用 new 关键字调用构造函数时,JavaScript 引擎会创建一个新对象,而构造函数内部使用的 this 就指向这个新创建的对象。因此,在构造函数中通过 this.xxx = xxx 设置的属性确实是直接挂载到这个新对象上的。这个过程在ES6中被直接简化成了语法糖Calss的形势
//使用Class语法糖 是基于原型的构造函数机制
class Person {constructor(name, age) {this.name = name;this.age = age;}sayHello() {console.log(`Hello, I'm ${this.name}`);}
}//本质上是一样的是 基于原型的构造函数机制 、
function Person(name, age) {this.name = name;this.age = age;
}Person.prototype.sayHello = function() {console.log(`Hello, I'm ${this.name}`);
};
4.实例属性与静态属性
实例属性:
function Pig(name){this.name = name;  // 实例属性
}const peiqi = new Pig('peiqi');
// name 属性属于实例对象,每个实例都有自己独立的副本
实例成员:
- 通过 
this.xxx在构造函数内定义 - 每个实例都有自己独立的副本
 - 通过实例对象访问:
instance.property 
静态属性:
静态成员:
- 直接在构造函数上定义:
Constructor.property - 所有实例共享同一份(实际上不属于实例)
 - 只能通过构造函数访问:
Constructor.property 
function Person(name) {this.name = name;
}Person.eyes = 2;  // 静态属性
Person.arms = 2;  // 静态属性
// 这些属性直接添加到构造函数本身,而不是实例对象
原型链关系:
- 实例对象的 
__proto__指向构造函数的prototype - 静态成员不在原型链上,无法通过实例访问,静态成员只是挂载到对象上的,根本就不存在原型链关系
 
对象的基本使用:
   let phone = {"goods-name": `xiaomi14`,money: 4000,address: `中国`}	
//查找对象的属性document.write(phone.address)//修改对象属性phone.name = `xiaomi15`document.write(phone.name)//增加对象属性phone.color = `白色`document.write(phone.color)//删除对象属性delete phone.money//和c++很像document.write(phone.money)//打印是undefind//查找对对象console.log(phone["goods-name"])console.log(phone.money);//遍历对象与数组for (let key in game) {//key是变量名可以随便取 但是要和后面的game[key]对应console.log(game[key])}//此方法也可以遍历数组let arr = [1, 2, 3, 4, 5]for (let k in arr) {console.log(arr[k])}
对象的解构赋值:
//解构时属性名和对象名相同const{uanme,age}={uanme:'你好',age:18}console.log(uanme);
//对象解构变量名更改
const{uanme:username,age:age1}={uanme:'你好',age:19} 
//解构数组对象
const pig =[{uname:'佩奇',age:6}]
const[{uname,age}]=pig 
// 多级对象解构
const pig={name:'pigs',family:{mother:'mama',father:'baba'},age:6
}
const {name,family:{mother,father},age}=pig
二: 字符串与新增属性
(1) 字符串
1. 字符串转义符
转义符: “ \ ” 后面没跟需要转义的字符
" 这中间是"一个分号 " 如果我们想打印出这个字符串 中间存在一个“ 这就导致不能直接打印出这串字符 这时候需要使用转义符号
let str="我是一个 \" 分号"
console.log(str);
2. 字符串拼接与模板字符串,截取,替换
(1)字符串的拼接
拼接字符可以使用+来拼接左右两个字符,或者使用模板字符串。
let obj=[{title:'1'},{title:'2'},{title:'3'}]
function title(){return `${obj.map((item)=>{return `<li>`+item.title+`</li>`}).join('')}`
}function title2(){return `${obj.map((item)=>{return `<li>${item.title}</li>`}).join('')}`
}
// 两种写法
(2)字符串的截取
let hd='heimachegnxvyaun'
字符串的截取有三个方法
slice
slice(从哪开始截取)hd.slice(0)slice(从哪开始截取,到哪停止)hd.slice(0,3)slice(负数)从后面开始算
hd.slice(-3,-1)substr 已经被废弃!!!
substr(截取位置)hd.substr(0)substr(截取位置,到哪停止)hd.substr(0,3)substr(负数没有意义)
substring
substring(截取位置)hd.substring(0)substring(截取位置,截取个数)hd.substring(0,3)substring(截取位置,截取个数)hd.substring(-3,6)
3. 标签模版
其实就是一个函数 但是存在两个参数 一个是字符串 一个是一个数组
字符串存储的是未填充的字符串 数组存储的是填充的值
function tag(String,...vars){// vars 接收所有插值变量组成的数组  所以前一个String是用来存储要被输出的字符串的 然后字符串里面的插入值存在vars里面console.log(vars);  // 输出: ["houdenren.com", "后盾人"]// String 接收模板字符串被插值分割后的部分console.log(String); // 输出: ["在线编程网", ",作者", ""]
}let uname='后盾人'
let web ='houdenren.com'
tag(`在线编程网${web},作者${uname}`);
// 其实主要的作用是对标签中的变量或者字符进行二次处理
4. 字符串检索
一些列用于在字符串中做检索的字符串方法 const hd='houdunren'提前定义字符串
indexOf返回字符所在位置 如果找不到就返回-1 从字符串的左边开始查找console.log(hd.indexOf('d'));//返回检索字符所在的位置 找不到的返回负一 从左边开始查找includes有两个参数 参数一是要查询的位置 参数二是开始查找的位置console.log(hd.includes('h',8));//返回布尔类型 可以设置第二个参数从指定位置开始查找lastIndexOf返回字符串的位置console.log(hd.lastIndexOf('o'));//从最右边向左开始查找startsWith判断字符是否是字符串的起始值console.log(hd.startsWith('h'));//判断字符串起始值endsWith判断字符串结束符console.log(hd.endsWith('h'));//判断字符串结束符
(2)Symbol
Symbol 是 ES6 引入的一种新的原始数据类型,主要用于创建唯一的标识符。可以作为一些属性名的标识符,比如为对象添加唯一的属性名,也可以对属性进行私有化 不让外部来遍历该属性. 可以有效避免属性重名的问题
特点:
- 唯一性:每个 
Symbol值都是唯一的,即使使用相同的描述创建 - 隐藏性:
Symbol属性不会出现在常规的遍历中(如for...in或Object.keys) - 不可枚举:
Symbol属性默认不可枚举,不会被JSON.stringify()序列化 
let symbol = Symbol();
let obj = {name: "李四",[symbol]: "张三",
};
// 普通遍历遍历不到 symbol 属性
for (let key in obj) {console.log(key); //name
}// 通过for of Reflect.ownKeys()可以遍历到对象里面的 symbol 属性
for (let key of Reflect.ownKeys(obj)) {console.log(key);//name Symbol()
}属性私有化:
let site = Symbol();
class User {constructor(name) {this.name = name;this[site] = "houdunren";}getName() {return `${this.name} ${this[site]}`;}
}let newuser=new User("lisi")
console.log(newuser.getName()); //lisi houdunren// 不能获取到symbol属性
for (let key in newuser) {console.log(newuser[key]); // lisi
}
基本用法:
声明symbol变量 访问的时候需要使用symbol变量的description属性来访问
// 创建 Symbol
let yf = Symbol("我是逸飞");
console.log(yf); // Symbol(我是逸飞)
console.log(yf.description); // 提取Symbol的描述// 全局共享的 Symbol
let gs1 = Symbol.for('global');
let gs2 = Symbol.for('global');
console.log(gs1 === gs2); // true
(3)Set与__WeakSet__
set:
set是一个集合数据类型 和数组相似 但是set中不能存在重复的数据 本质和数组一样是一个引用类型的数据结构
set列表定义及其常见方法:
- 通过set构造函数直接构造set列表对象 可以在构造函数中传入一个数组 初始化set
 - add增加元素
 - delete删除某个具体元素 clear删除全部元素
 - size返回元素个数
 - '…'和数组一样可以展开列表
 - has方法判断某个元素否存在
 
// set是一个集合数据类型 和数组很类似 但是set中不能含有重复的数据
let set =new Set([1,2,3,4,5])
set.add(1);
set.add(1);
set.add(1);
console.log(set);// 如果定义的时候 set里面是字符串就会把字符串的个字符当作元素
let sets=new Set("hdcms")
console.log(sets);//Set(5) { 'h', 'd', 'c', 'm', 's' }//增
set.add(1);
set.add(2);//删
// 单个删除
set.delete(1);
// 清空全部
// set.clear();// 查询方法
// 查询set里面的元素个数
console.log(set.size); // 2// 展开语法查看set
console.log([...set]); // [1,2]// 判断某个元素是否存在,返回布尔类型
console.log(set.has(1)); // true
set的遍历
let set=new Set([1,2,3,4,5,6])
set.forEach(function(value,index,set){//使用forEach遍历setconsole.log(value);
})for (const value of set) {//使用forof遍历setconsole.log(value);
}
weakset:
weakset和set类型一样都是只能存储唯一值的列表 区别就是weakset是一个弱引用类型的数据结构,并且weakset不能够遍历,并且weakset只能存储对象引用,不能存储基本类型值.
什么是弱引用:
先观察一下set与weakset的区别
Set: 对象即使不再被其他地方引用,只要还在Set中就不会被垃圾回收WeakSet: 对象如果只被WeakSet引用,会被垃圾回收机制自动清理
不难理解出 弱引用就是不用阻止垃圾回收器去回收被weakset引用的对象
如果我们定义一个对象a 分别用不同的变量去引用a 每一次引用都会标记一次引用次数 但是用weakset去引用这个对象就不会添加引用次数 当变量都被清除之后对象a的引用次数也会归零 weakset不会阻止a被垃圾回收
使用示例
let nodes=new WeakSet()
let divs=[{div:1},{div:2}]
divs.forEach(element => {
nodes.add(element)
});
console.log(nodes);
//删除 
nodes.delete(divs[0])
console.log(nodes);
//同样使用has判断元素是否存在
console.log(nodes.has(divs[1]));
//weakset不可遍历 并且只能储存对象
(4)Map与weakMap
Map:
简单理解map
map就是键值对的集合 可以近似理解为用键名代替数组索引的类数组
map类型和obj类型的区别:对象的键只能是字符串类型,map的键名可以是各种类型
声明Map:
- 通过Map构造函数直接构造map变量
 - 使用set方法设置键值对 参数一是键名 参数二是值
 - map的键名可以是任意类型
 
// 声明Map类型
let map = new Map();
// 键名为字符串 Map方法添加数据不是添加 叫设置数据
map.set("name", "李四");
// 键名为数字
map.set(1, "数字");
// 键名为方法
map.set(function() {}, "方法作为键名");
// 对象键名
map.set({}, "对象键名");
console.log(map);//或者直接来定义
let map2=new Map([[{},"对象"],[[],'数组'],[function(){},'函数']])
console.log(map2);
Map的增删改查
- set增
 - delete删除某个具体键值对 clear全部删除
 
let map = new Map();
// 增
map.set('name','张三')
map.set('age',18)
// 删除
// 单个删除,返回布尔值
console.log(map.delete('age'));
// 全部删除,没有返回值
// map.clear()
// 查询
console.log(map.get('name'));遍历Map变量:
- map可以和数组一样进行遍历,也可以使用forEach方法
 - keys方法 返回所有的键
 - values方法 返回所有的值
 - entries方法 返回键值对
 
let map = new Map();
map.set("name", "李四");
map.set("age", "15");// 遍历所有的key
console.log(map.keys());//返回所有的键// 返回所有的值
console.log(map.values());// 返回值和键
console.log(map.entries());// for of 遍历键名
for (let key of map.keys()) {console.log(key);
}// for of 遍历值
for (let val of map.values()) {console.log(val);
}// 对象同时接收键和值
for (let [key, val] of map.entries()) {console.log(key, val);
}// forEach 遍历 map,第一个值是键值,第二个值是键名
map.forEach((item, key) => {console.log(item, key);
});
将Map类型转换为数组:
map虽然作为键值对的集合,也可以将键和值单独转换成两个单独的数组 使用展开语法配合不同的方法即可
let hd=new Map([['中文','chinese'],['nihao','hello']])
console.log([...hd]);//将键值对转化为二维数组
console.log([...hd.keys()]);//单独转换键
console.log([...hd.entries()]);//将键值对转化为数组
console.log([...hd.values()]);//将值转化为数组
let newhd=[...hd].filter((item)=>{return item[1].includes('chinese')
})
console.log(newhd);WeakMap:
WeakMap和Map基本类似,不一样的是WeakMap的键只能是对象,而且不会增加对象的引用. 和WeakSet一样 WeakMap不能遍历 没有size属性 不能cleat直接清除 不能使用foreach方法
常见的使用
验证弱引用特性
let obj={name:'list'
}
let weakmap=new WeakMap();
weakmap.set(obj,'houdunren')// 验证WeakMap中是否有这个键
console.log(weakmap.has(obj));  // 会输出 true// 获取值
console.log(weakmap.get(obj));  // 会输出 'houdunren'obj = null;console.log(weakmap);setTimeout(() => {// 倒计时一秒后再打印结果为空console.log(weakmap);
}, 1000);