以下是我JS进阶学习记录:
mdn手册:https://developer.mozilla.org/zh-CN/
文章内容源自b站的黑马程序员的pink老师:https://www.bilibili.com/video/BV1Y84y1L7Nn/?spm_id_from=333.999.0.0&vd_source=fb5a1624baac73f8cef6c60124746736
重点:
① 利用构造函数创建对象(构造函数实例化执行过程)、
② Object.keys()获得对象所有的属性名返回数组形式、
③ Object.values()获得对象所有的属性值返回数组形式、
④ 数组的常用方法【forEach语法、filter筛选数组、reduce方法求和、map()、find()、every()、some()】、
⑤String常用方法【str.split()、str.substring()、starsWith()、includes()】、
⑥toFixed()保留小数点后几位
一、垃圾回收机制
1.1垃圾回收机制说明
1、全局变量一般不会回收(关闭页面才回收)
2、一般情况下局部变量,不用了,会被自动回收掉
3、内存泄漏:程序中分配的内存由于某种原因,程序未释放或无法释放叫做内存泄漏。
4、拓展 - js垃圾回收机制 - 算法说明
(1) 栈:由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面。
(2) 堆:一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆里面。
1.2 介绍两种常见的浏览器垃圾回收算法:引用计数法和标记清除法
1.2.1 引用计数法 ( IE浏览器常用这个方法,现代浏览器一般不用了。)
有一个缺点:嵌套引用(循环引用),如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄漏。
function fn(){
let o1 = {}
let o2 = {}
o1.a = o2
o2.a = o1
return '引用计数无法回收'
}
fn()
1.2.2 标记清除法。
找到就给你标记起来,找不到就垃圾回收。
for(let i = 1;i<=3;i++){
}
let num =10
function fn(){
const str = 'andy'
// str = 'lily'
console.log(str);
}
fn()
二、闭包
闭包简单来说就是:里层函数 + 外层函数的变量 形成一个闭包。
function outer() {
// a 是外层函数的变量
let a = 10
// fn 是 里层函数
function fn() {
console.log(a);
}
fn()
}
outer()
那么看一下下面这个是不是闭包呢?
function outer() {
function fn() {
console.log(22);
}
fn()
}
outer()
这个并不是闭包哈,因为缺少了外层的变量。请大家睁大双眼仔细看哦!
常见的闭包形式:外部可以访问使用函数内部的变量
function outer(){
let a = 100
function fn(){
console.log(a);
}
return fn
}
// outer() === fn === function fn(){}
// const fun = function fn(){}
const fun = outer()
fun() //调用函数
// 外面要使用这个100
我们说了那么久闭包,闭包到底用来干什么的呢?用来:统计函数调用的次数
let i = 0
function fn(){
i++
console.log(`函数被调用了${i}次`);
}
fn() //1
fn() //2
由于以上的 i 是全局变量,容易被人纂改,所以就需要用闭包。
function fn2(){
let i = 0
function fun(){
i++
console.log(`函数被调用了${i}次`);
}
return fun
}
const result = fn2()
result() //2
result() //3
此处,本来 i 用完一次就应该被回收了,但是有 return ,又重新执行了一次,所以没有被回收,一直在使用,这就叫做内存泄漏了。所以闭包有可能会导致内存泄漏的风险。
因为 result 是全局作用域,只有关闭页面才会销毁,只要页面不关闭,他就一直执行,函数内部就会一直 return fun ,而return fun 就找到 i ,i 就内存泄漏了。
闭包简写:
function outer(){
let i = 1
return function(){
console.log(i);
}
}
const fun = outer()
fun() //调用fun
闭包的作用:
1、外部可以访问函数内部的变量,也可以实现数据私有。
2、闭包很有用,因为他允许将函数与其所操作的某些数据(环境)关联起来。
闭包的问题:容易引起内存泄漏
三、变量提升
3.1 变量提升
3.1.1 let / const 声明的变量不存在变量提升
3.1.2 把所有 var 声明的变量提升到当前作用域的最前面
3.1.3 只提升声明,不提升赋值
console.log(num + '件'); //undefined件
var num = 10
提升后相当于以下写法:
var num
console.log(num + '件'); //undefined件
num = 10
3.2 函数提升
3.2.1 会把所有函数声明提升到当前作用域的最前面
3.2.2 只提升函数声明,不提升函数调用
fn()
function fn(){
console.log('函数提升')
}
以下为提升后:
function fn(){
console.log('函数提升')
}
fn()
3.2.3 函数表达式 必须先声明和赋值,后调用,否则报错。
fun()
var fun = function(){
console.log('函数表达式') //报错
}
函数表达式提升后为:
var fun
fun()
fun = function(){
console.log('函数表达式) //报错
}
四、函数动态参数、函数剩余参数、展开运算符
4.1 函数动态参数
函数动态参数 arguments 只存在于 函数里面,并且是个伪数组,也就是有索引号下标,但是没有数组常用方法,有属于自己的方法
function getSum(){
console.log(arguments) //控制台输出的是[2,3,4]和[1,2,3,4,5,6]
let sum = 0
for(let i = 0; i < arguments.length; i++){
sum += arguments[i]
}
console.log(sum) //控制台输出的是数组的和
}
getSum(2,3,4)
getSum(1,2,3,4,5,6)
4.2 函数剩余参数
剩余参数和动态参数的区别:
1、…是语法符号,置于最末函数形参之前,用于获取多余的实参。
2、借助 … 获取的是剩余实参,是个真数组。
3、开发中,建议多使用剩余参数,因为箭头函数没有arguments。
剩余参数
前面三个点 … 后面是数组变量名
function getSum(...arr){
console.log(arr)
let sum = 0
for(let i = 0; i < arr.length; i++){
sum += arr[i]
}
console.log(sum)
}
getSum(2,3)
getSum(1,2,3)
如果客户需要至少两个参数,也就是两个及以上。
function getSum(a,b,...arr){
console.log(arr) //使用的时候不需要写三个点...
}
getSum(1,2,3) //[3]
getSum(3,5) //[]
getSum(1,3,5,7,9,11) //[5,7,9,11]
4.3 展开运算符
展开运算符(…)跟 函数剩余参数(…arr)特别相似,注意区分一下。
展开运算符(…)将一个数组进行展开,不会修改原数组
运用场景:求数组的最大值(最小值)、合并数组等。
const arr1 = [1,2,3]
console.log(...arr1) //1 2 3
4.3.1 求数组最大值
数组本身没有求最大值最小值的方法,但是 Math.max() 方法有。比如:console.log(Math.max(1,2,3)) //3
const arr1 = [1,2,3]
console.log(...arr1) //1 2 3
console.log(Math.max(...arr1)) //3
console.log(Math.min(...arr1)) //1
4.3.2 合并数组
const arr1 = [1,2,3]
const arr2 = [3,4,5]
const arr = [...arr1,...arr2]
console.log(arr)
五、箭头函数
5.1 箭头函数用于匿名函数,有匿名函数的地方就可以替换成箭头函数。
function fn(){
console.log(123)
}
等价于:
const fn = function(){
console.log(123)
}
箭头函数属于表达式函数,不存在函数提升。
普通函数有 arguments 动态参数
箭头函数没有 arguments 动态参数,但是有剩余参数 …args
const fn = () => {
console.log(123)
}
fn()
5.2 箭头函数的参数
const fn = (x) => {
console.log(x)
}
fn(1)
5.3 箭头函数的参数如果只有一个,可以省略小括号
const fn = x => {
console.log(x)
}
fn(2)
5.4 箭头函数只有一行代码的时候,可以省略大括号
const fn = y => console.log(y)
fn(2)
5.5 箭头函数只有一行代码的时候,也可以省掉return
const fn = y => {
return y+y
}
console.log(fn(2))
等价于:
const fn = y => y + y
console.log(fn(2))
5.6 阻止事件对象 用箭头函数的简写
const p = document.querySelector('p')
p.addEventListener('click',e => e.preventDefault())
5.7 箭头函数可以直接返回一个对象
const fn = function(uname){
return {name:uname}
}
console.log(fn('某某某'))
等价于:
({属性名:属性值}),这里花括号对象外面还必须要包一层小括号,不然就是undefined
const fn = (uname) => ({name:uname})
console.log(fn('某某某'))
5.8 箭头函数求和
const getSum = (...arr) => {
let sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
return sum
}
console.log(getSum(1, 2, 3))
5.9 箭头函数的 this 指向问题(重要)
以前 this 的指向:谁调用,this就指向谁。
箭头函数的 this :指向上一层作用域
5.9.1 普通的 this 指向
console.log(this); //window
普通函数:
因为 fn() = window.fn(),所以下面的代码也是指向window。
function fn(){
console.log(this); //window
}
普通的对象的方法调用:
const obj = {
name:'andy',
sayHi:function(){
console.log(this) //因为是obj调用了这个方法,所以指向的是obj
}
}
obj.sayHi()
5.9.2 箭头函数的 this 指向
函数:
const fn2 = () => {
console.log(this) //指向window
}
fn2()
对象方法箭头函数:
const obj2 {
uname:'谁谁谁',
sayHi: () => {
console.log(this); //window
//指向上一层作用域的this,也就是谁调用了obj2,this就指向谁,所以是window.obj2.sayHi()
}
}
obj2.sayHi()
const obj3 = {
uname:'谁谁谁',
sayHi: function(){
console.log(this) //obj3
let i = 10
const count = () => {
console.log(this) //obj3
}
count()
}
}
obj3.sayHi()
使用箭头函数前需要考虑函数中的 this 的值,DOM 事件回调函数为了简便,还是不太推荐使用箭头函数。
比如:
普通函数,此时 this 指向了 DOM对象 btn
btn.addEventListener('click',function(){
console.log(this) //btn
})
箭头函数,此时 this 指向了 window
btn.addEventListener('click',() => {
console.log(this) //window
})
所以使用时需要小心这种情况。
六、数组解构
6.1 数组解构说明
数组解构:将数组的单元值快速批量赋值给一系列变量的简洁语法。
右侧数组的单元值将被赋值给左侧的变量。
const arr = [100, 60, 80]
//数组解构 赋值 一一对应
const [max, min, avg] = arr
console.log(max); //100
console.log(min); //60
console.log(avg); //80
1、数组解构的典型应用:交换两个变量
2、变量需要用 let 命名,变量声明完记得加分号;
必须加分号的两种情况:
(1)立即执行函数
(function(){})();
;(function(){})()
//分号加在前面或者后面都可以
//或者是
(function(){}());
;(function(){}())
(2)数组解构
数组开头的,特别是前面有语句的一定要注意加分号;
;[b, a] = [a, b]
交换两个变量:
let a = 1
let b = 2;
[b, a] = [a, b] //这里没有像上面那样加个const或者let声明,是应为前面两个a,b变量已经声明了。
console.log(a, b)
6.2 数组解构之数组遍历
[1,2,3].map(function(ele){
console.log(ele)
})
6.3 数组解构细节
1、变量多,单元值少 undefined
const [a,b,c,d] = [1,2,3]
console.log(a); //1
console.log(b); //2
console.log(c); //3
console.log(d); //undefined
2、变量少,单元值多
const [a,b] = [1,2,3]
console.log(a); //1
console.log(b); //2
3、利用剩余参数解决:变量少,单元值多
const [a,b,...c] = [1,2,3,4]
console.log(a); //1
console.log(b); //2
console.log(c); //[3,4] 真数组
4、变量多,单元值少。防止 undefined 传递过来
声明时给个默认值
const [a = 0, b = 0, c = 0, d = 0] = [1, 2, 3]
console.log(a); //1
console.log(b); //2
console.log(c); //3
console.log(d); //0
5、按需导入赋值,忽略某些返回值
const [a, b, , d] = [1, 2, 3, 4]
console.log(a); //1
console.log(b); //2
console.log(d); //4
6、支持多维数组的解构
const arr = [1,2,[3,4]]
console.log(arr[0]); //1
console.log(arr[1]); //2
console.log(arr[2]); //[3,4]
console.log(arr[2][0]); //3
或者
const arr = [1, 2, [3, 4]]
const [a, b, c] = [1, 2, [3, 4]]
console.log(a); //1
console.log(b); //2
console.log(c); //[3,4]
或者 多维数组解构,只要二维数组里的3
const [a, b, [c,d]] = [1, 2, [3, 4]]
console.log(a); //1
console.log(b); //2
console.log(c); //3
七、对象解构
1、声明的变量名字和属性名字必须要一样
const {uname,age} = {uname: '谁谁谁',age: 18}
// 等价于 cosnt uname = obj.uname
console.log(uname);
console.log(age);
2、对象解构的变量名可以重新改名,防止跟普通变量名有冲突 旧变量名:新变量名
const {uname:username,age} = {uname:'谁谁谁',age:18}
console.log(username);
console.log(age);
3、解构数组对象
声明有中括号就写中括号,有花括号就写花括号。
const pig = [
{
uname: '佩奇',
age: 5
}
]
const [{uname,age}] = pig
console.log(uname);
console.log(age);
4、多级对象解构
const pig = {
name: '佩奇',
family: {
mother: '猪妈妈',
father: '猪爸爸',
sister: '乔治'
},
age: 6
};
const {name,family:{mother,father,sister}} = pig
console.log(name);
console.log(mother);
console.log(father);
console.log(sister);
5、数组对象解构 + 多级解构
const person = [
{
name:'佩奇',
family:{
mother:'猪妈妈',
father:'猪爸爸',
sister:'乔治'
},
age:6
}
]
const [{name,family:{mother,father,sister}}] = person
console.log(name);
console.log(mother);
console.log(father);
console.log(sister);
6、多级对象解构案例练习
// 1.这是后台传递过来的数据
const msg = {
"code":200,
"msg":"获取新闻列表成功",
"data":[
{
"id":1,
"title":"5G商用自己,三大运用商收入下降",
"count":58
},
{
"id":2,
"title":"国际媒体头条速览",
"count":56
},
{
"id":3,
"title":"乌克兰和俄罗斯持续冲突",
"count":1669
}
]
}
// 需求1: 请将以上 msg 对象采用对象解构的方式,只选出 data 方法后面使用渲染页面
// const {data} = msg
// console.log(data);
// 需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给函数
// 以前的写法或者思维
// const {data} = msg
// function render(arr){
// console.log(arr);
// }
// render(data)
// 等价于
// function render(arr){
// const {data} = arr
// console.log(data);
// }
// render(msg)
// 传参的时候顺便解构了
// function render({data}){
// console.log(data);
// }
// render(msg)
// 需求3:为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为myData
function render({data:myData}){
// 要求将获取过来的 data数据 更名为myData
console.log(myData);
}
render(msg)
// msg 虽然很多属性,但是我们利用解构只要data的值
// 改新名字的方法:旧的写前面,新的写后面
八、 forEach语法 <重点>
forEach 跟 map 很像,但是 map() 是需要返回值 return ,forEach没有return。
map数组遍历+return返回值 也是重点
forEach 只能遍历数组,不能遍历对象。
arr.map(function(){
return
})
map会返回空数组[] , forEach则是 undefined
在forEach参数中,当前数组元素是必须要写的,索引号可选
forEach 是加强版的 for 循环,适合于遍历数组对象,数据越复杂,forEach 作用就越明显
const arr = ['red','green','pink']
const re = arr.forEach(function(item,index){
console.log(item); //数组元素 red green pink
console.log(index); //索引号 0 1 2
})
// console.log(re); //不返回值,所以结果是undefined
渲染商品列表案例(用到forEach遍历+解构)
const goodsList = [{
id: '4001172',
name: '逞心如意手摇咖啡磨豆机咖啡豆研磨机',
price: '289.00',
picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg'
},
{
id: '4001594',
name: '日式黑陶功夫茶组双侧把茶具礼盒装',
price: '288.00',
picture: 'https://yanxuan-item.nosdn.127.net/3346b7b92f9563c7a7e24c7ead883f18.jpg',
},
{
id: '4001009',
name: '竹制干泡茶盘正方形沥水茶台品茶盘',
price: '109.00',
picture: 'https://yanxuan-item.nosdn.127.net/2d942d6bc94f1e230763e1a5a3b379e1.png',
},
{
id: '4001874',
name: '古法温酒汝瓷酒具套装白酒杯莲花温酒器',
price: '488.00',
picture: 'https://yanxuan-item.nosdn.127.net/44e51622800e4fceb6bee8e616da85fd.png',
},
{
id: '4001649',
name: '大师监制龙泉青瓷茶叶罐',
price: '139.00',
picture: 'https://yanxuan-item.nosdn.127.net/4356c9fc150753775fe56b465314f1eb.png',
},
{
id: '3997185',
name: '与众不同的口感汝瓷白酒杯套组1壶4杯',
price: '108.00',
picture: 'https://yanxuan-item.nosdn.127.net/8e21c794dfd3a4e8573273ddae50bce2.jpg',
},
{
id: '3997403',
name: '手工吹制更厚实白酒杯壶套装6壶6杯',
price: '99.00',
picture: 'https://yanxuan-item.nosdn.127.net/af2371a65f60bce152a61fc22745ff3f.jpg',
},
{
id: '3998274',
name: '德国百年工艺高端水晶玻璃红酒杯2支装',
price: '139.00',
picture: 'https://yanxuan-item.nosdn.127.net/8896b897b3ec6639bbd1134d66b9715c.jpg',
}
]
// 核心思路:有多少条数据,就渲染多少模块,然后生成对应的html结构标签,赋值给list标签即可
// 1.利用forEach遍历数组里面的数据
// 2.拿到数据,利用字符串拼接生成结构添加到页面中
// 3.注意:传递参数的时候,可以使用对象解构
// 1. 声明一个字符串变量
let str = ``
// 2. 遍历数据
goodsList.forEach(item=> {
// console.log(item); //可以得到每一个数组元素 对象
// const {id} = item 对象解构
const {name,price,picture} = item
str += `
<div class="item">
<img src=${picture} alt="">
<p class="name">${name}</p>
<p class="price">${price}</p>
</div>
`
})
// 3. 生成的 字符串 添加给 list
document.querySelector('.list').innerHTML = str
九、filter 筛选数组<重点>
返回符合条件的新数组
const arr = [10,20,30]
const newArr = arr.filter(item => item>=20)
console.log(newArr); //[20,30]
价格筛选案例:
const goodsList = [{
id: '4001172',
name: '逞心如意手摇咖啡磨豆机咖啡豆研磨机',
price: '289.00',
picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg'
},
{
id: '4001594',
name: '日式黑陶功夫茶组双侧把茶具礼盒装',
price: '288.00',
picture: 'https://yanxuan-item.nosdn.127.net/3346b7b92f9563c7a7e24c7ead883f18.jpg',
},
{
id: '4001009',
name: '竹制干泡茶盘正方形沥水茶台品茶盘',
price: '109.00',
picture: 'https://yanxuan-item.nosdn.127.net/2d942d6bc94f1e230763e1a5a3b379e1.png',
},
{
id: '4001874',
name: '古法温酒汝瓷酒具套装白酒杯莲花温酒器',
price: '488.00',
picture: 'https://yanxuan-item.nosdn.127.net/44e51622800e4fceb6bee8e616da85fd.png',
},
{
id: '4001649',
name: '大师监制龙泉青瓷茶叶罐',
price: '139.00',
picture: 'https://yanxuan-item.nosdn.127.net/4356c9fc150753775fe56b465314f1eb.png',
},
{
id: '3997185',
name: '与众不同的口感汝瓷白酒杯套组1壶4杯',
price: '108.00',
picture: 'https://yanxuan-item.nosdn.127.net/8e21c794dfd3a4e8573273ddae50bce2.jpg',
},
{
id: '3997403',
name: '手工吹制更厚实白酒杯壶套装6壶6杯',
price: '99.00',
picture: 'https://yanxuan-item.nosdn.127.net/af2371a65f60bce152a61fc22745ff3f.jpg',
},
{
id: '3998274',
name: '德国百年工艺高端水晶玻璃红酒杯2支装',
price: '139.00',
picture: 'https://yanxuan-item.nosdn.127.net/8896b897b3ec6639bbd1134d66b9715c.jpg',
}]
// 1. 渲染函数 封装
function render(arr) {
let str = ``
arr.forEach(item => {
const {name,price,picture} = item
str += `
<div class="item">
<img src=${picture} alt="">
<p class="name">${name}</p>
<p class="price">${price}</p>
</div>
`
})
// 追加给list
document.querySelector('.list').innerHTML = str
}
render(goodsList) //页面一打开就需要渲染
// 2. 过滤筛选
document.querySelector('.filter').addEventListener('click', e => {
// e.target.dataset.index e.target.tagName
const {tagName,dataset} = e.target
// 判断
if (tagName === 'A') {
// arr返回的是新数组
// 为什么这里设置的不是空数组?因为如果点击的a不是1不是2不是3,那就是其他的,就直接跳到渲染函数,渲染函数调用的是arr,如果此时arr设置为空数组,则页面渲染为空,所以给goodslist好一点
let arr = goodsList
if(dataset.index === '1'){
arr = goodsList.filter(item=> item.price>=0 && item.price<=100)
}else if(dataset.index==='2'){
arr = goodsList.filter(item=>item.price>=100 && item.price <=300)
}else if(dataset.index==='3'){
arr = goodsList.filter(item=>item.price>=300)
}
// console.log(arr);
// 渲染函数
render(arr)
}
})
十、创建对象(以构造函数为主进行讲解)【以下都是属于内置构造函数】
10.1 创建对象有三种方式
10.1.1 利用对象字面量创建对象
const o = {
name:'佩奇'
}
10.1.2 利用 new Object 创建对象
const obj = new Object({
uname:'佩奇'
})
console.log(obj)
10.1.3 利用构造函数创建对象
构造函数:是一种特殊的函数,主要用来初始化对象。
有两个约定:
1、函数的命名以大写字母开头
2、他们只能由 “new” 操作符来执行使用。
构造函数内部无须写return,写了也没用
function Pig(uname,age){
//this 指向调用函数的对象 obj,uname是形参,name是属性
this.name = uname
this.age = age
}
console.log(new Pig('佩奇', 6))
console.log(new Pig('乔治', 3))
10.1.3.1 构造函数实例化执行过程 <重点>
1、创建新的空对象
2、构造函数 this 指向新的空对象
3、执行构造函数代码,修改 this,添加新的属性
4、返回新的对象,构造函数里面不需要写return
10.1.3.2 实例成员和静态成员
1、实例成员:实例对象上的属性和方法属于实例成员。
function Pig(name){
this.name = name
}
const peiqi = new Pig('佩奇')
const qiaozhi = new Pig('乔治')
peiqi.name = '小猪佩奇' //peiqi.name的 name 就是实例属性
peiqi.sayHi = () => { //实例方法
console.log('hi~')
}
//为构造函数传入参数,创建结构相同但值不同的对象
console.log(peiqi)
console.log(qiaozhi)
2、静态成员:构造函数上的属性和方法称为静态成员
function Pig(name){
this.name = name
}
Pig.eyes = 2 //静态属性
Pig.sayHi = function(){
console.log(this) //指向Pig构造函数
}
Pig.sayHi() //静态方法
console.log(Pig.eyes) //2
静态成员只能构造函数来访问。
静态方法中的 this 指向构造函数。
十一、基本包装类型
其实字符串、数值、布尔等基本类型也都有专门的构造函数,这些我们称为包装类型。
js中几乎所有的数据都可以基于构造函数创建。
比如:
const str = 'red'
console.log(str.length) //3
const num = 12
console.log(num.toFixed(2)) //12.00 保留两位小数点
js底层完成,把简单数据类型包装为引用数据类型
const str = new String('red')
内置构造函数:
引用类型:Object、Array、RegExp、Date等
包装类型:String、Number、Boolean等。
十二、Object静态方法
12.1 Object.keys()获得对象所有的属性名,返回数组形式<重点>
const o = {
uname:'pink',
age:18
}
//获得对象所有的属性名
console.log(Object.keys(o)); //返回数组['uname','age']
12.2 Object.values()获得对象所有的属性值,返回数组形式<重点>
const o = {
uname:'pink',
age:18
}
//获得对象所有的属性名
console.log(Object.values(o)); //返回数组['pink',18]
12.3 Object.assign()对象的拷贝以及给对象添加属性
12.3.1 拷贝对象
const o = {
uname:'red',
age:18
}
const oo = {}
Object.assign(oo,o) //将o对象拷贝给oo对象
console.log(oo)
12.3.2 给对象添加属性
const o = {
uname:'pink',
age:18
}
Object.assign(o,{gender:'女'})
console.log(o);
结果是:
十三、数组reduce方法:求和 <重点>
基本语法:
const arr = [1,3,5]
arr.reduce(function(上一次值,当前值){}, 初始值)
13.1 没有初始值
const arr = [1, 3, 5]
const total = arr.reduce(function(prev, current){
return prev + current
})
console.log(total) //9
13.2 有初始值(除了把数组里的数字相加外,还要加上初始值)
const arr = [1, 3, 5]
const total = arr.reduce(function(prev, current){
return prev + current
},10)
console.log(total) //19
13.3 利用箭头函数的写法
const arr = [1, 3, 5]
const total = arr.reduce((prev, current) => prev + current,10)
console.log(total)
13.4 reduce的执行过程:
1、如果没有起始值,则上一次值以数组的第一个数组元素的值
2、每一次循环,把返回值给作为下一次循环的 上一次值
3、如果有起始值,则起始值作为上一次值。
在数组对象中使用reduce计算薪资案例
const arr = [{
name: '张三',
salary: 10000
}, {
name: '李四',
salary: 10000
}, {
name: '王五',
salary: 10000
}]
// 计算薪资案例
const result = arr.reduce((prev, current) => prev + current.salary, 0)
console.log(result); //30000
// 工资提升 30%
const up = arr.reduce((prev, current) => prev + current.salary * 1.3, 0)
console.log(up); //39000
十四、Array常用方法
数组常见的其他方法如下:
14.1 find() <重点>
find() 查找元素,返回符合测试条件的第一个数组元素值,如果没有符合条件的则返回undefined
在数组查找:
const arrs = ['red','blue','green']
const re = arrs.find(function(item){
return item === 'blue'
})
console.log(re); //blue
在数组对象查找:
const arr = [{
name: '小米',
price: 1999
}, {
name: '华为',
price: 3999
}]
const mi = arr.find(item => item.name === '小米')
console.log(mi)
14.2 every() <重点>
every 每一个是否都符合条件,如果都符合返回true,否则返回false
const arr1 = [10,20,30]
const flag = arr1.every(item => item >=10) //全部都大于等于10,就返回true
console.log(flag); //true
const flag2 = arr1.every(item => item >=20)
console.log(flag2) //false
14.3 some() <重点>
some() 只要有一个符合条件,返回true,否则返回false
const arr2 = [10,20,30]
const flag2 = arr2.some(item => item >=20) //只要有一个大于等于20,就返回true
console.log(flag2); //true
14.4 Array.from() 将伪数组转换为真数组
先插播一个无关的小案例。
将size和color里面的值拼接为字符串之后,写到div标签里面。
思路:获得所有的属性值,然后拼接字符串就可以了。
1、获得所有属性值:Object.values() 返回的是数组。
2、拼接数组是join(’ ') 这样就可以转换为字符串了。
const spec = { size:'40cm*40cm',color:'黑色' }
document.querySelector('div').innerHTML = Object.values('spec').join('/')
注意:join(‘’) 是 数组 转换成 字符串 。split(‘’) 是 字符串 转换为 数组。两个是死对头
14.4.1 Array.from(arr) 把伪数组转换为真数组
html部分:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
javascript:
const lis = document.querySelectorAll('ul li')
const liss = Array.from(lis)
//如果是伪数组用 pop() 方法,会报错。现在转换为了真数组,就不会报错了。
liss.pop() //删除数组最后一个元素 li
console.log(liss)
十五、String常见方法
15.1 str.split()
str.split() 字符串转换为数组 和join()相反
const str = 'pink, red'
const arr = str.split(',')
console.log(arr) //字符串转换为数组,以逗号分隔
const str1 = '2024-11-28'
const arr1 = str1.split('-') //字符串转换为数组,以-分隔
console.log(arr1) //['2024', '11', '28']
15.2 str.substring()
字符串的截取 , 以前用substr() 已废弃,现在用 substring(开始的索引号[, 结束的索引号]), 结束索引号可选可不选。
const str = '我今天又要上班了'
console.log(str.substring(5,6)) //上
15.2.1 如果省略了 结束索引号 ,默认取到最后
const str = '我今天又要上班了'
console.log(str.substring(5)) //上班了
15.2.2 结束的索引号不包含想要截取的部分
比如想要获取到 “上班” 两个字。
const str = '我今天又要上班了'
console.log(str.substring(5,7)) //上班 如果结束索引号取6,是不包含6的,所以取7才能取到班字
15.3 startsWith()
startsWidth() 判断是不是以某个字符开头,还有个可选的索引号,表示从哪开始检索
const str = 'pink老师上课中'
console.log(str.startsWith('pink')) //true
console.log(str.startsWith('pink',2)) //false
console.log(str.startsWith('老师',4)) //true
15.4 includes()
includes() 判断一个字符是否包含在一个字符串中,返回 true 或 false 。开始查找的索引号是可选可不选的。
const str = '我是pink老师'
console.log(str.includes('pink')); //true
console.log(str.includes('pink',2)); //true
15.5 小案例 - 赠品
思路:
1、把字符串拆分为数组,这样两个赠品就拆分开了,用哪个方法? split(‘,’)
2、利用map遍历数组,同时把数组元素生成到span里面,并且返回。
3、因为返回的是数组,所以需要转换为字符串,用哪个方法? join(’ ')
const gift = '50g的茶叶,清洗球'
document.querySelector('div').innerHTML = gift.split(',').map(item => `<span>【赠品】 ${item}</span><br>`).join('')
运行结果:
十六、toFixed()
toFixed() 是Number 内置的构造函数,用于创建数值。
toFixed() 可以让数字指定保留的小数位数。
还会四舍五入
const num = 10,123
console.log(num.toFixed()) //10
const num = 10.923
console.log(num.toFixed()) //11
const num = 10.923
console.log(num.toFixed(2)) //10.92
十七、数字强制转换为字符串的两种方法
const num = 10
console.log(String(num)) //1、String()
console.log(num.toString()) //2、num.toString()
购物车展示案例
// 分析业务模块:
// 1、 把整体的结构直接生成然后渲染到大盒子.list里面
// 2、 哪个方法可以遍历的同时还有返回值(map方法)
// 3、 最后计算总价模块,哪个方法可以求和?(reduce方法)
// 1. 先利用map来遍历,有多少条数据,就渲染多少相同商品
// ---可以先写死数据
// ---注意map返回值是数组,我们需要用join转换为字符串
// ---把返回的字符串 赋值 给 list大盒子 的 innerHTML
// 2. 更换数据
// ---先更换不需要处理的数据 ,图片 ,商品名称, 单价 , 数量
// ---采取对象解构的方式
// ---注意 单价要保留2位小数,488.00 toFixed(2)
// 2. 更换数据 - 处理 规格文字 模块
// ---获取 每个对象里面的spec,上面对象解构添加spec
// ---获得所有属性值是:Object.values() 返回的是数组
// ---拼接数组是join('') 这样就可以转换为字符串了
// 2. 更换数据 - 处理 赠品 模块
// ---获取 每个对象里面的gift,上面对象解构添加gift
// ---判断是否有gift属性,没有的话不需要渲染
// ---利用变成的字符串然后写到p.name里面
// 3. 分析业务模块:
// ---计算 合计 模块
// ---求和用到数组 reduce 方法 累计器
// ---根据数据里面的数量和单价累加和即可
// ---注意 reduce 方法有2个参数,第一个是回调函数,第二个是初始值,这里写0
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {padding: 0;margin: 0;box-sizing: border-box;border: 0;}
.list {width: 990px;margin: 100px auto 0;}
.item {padding: 15px;transition: all 0.5s;display: flex;border-top: 1px solid #e4e4e4;}
.item:nth-child(4n) {margin-left: 0;}
.item:hover {cursor: pointer;background-color: #f5f5f5;}
.item img {width: 80px;height: 80px;margin-right: 10px;}
.item .name {font-size: 18px;margin-right: 10px;color: #333;flex: 2;}
.item .name .tag {display: block;padding: 2px;font-size: 12px;color: #999; }
.item .price, .item .sub-total {font-size: 18px;color: firebrick;flex: 1;}
.item .price::before,.item .sub-total::before, .amount::before {content: "¥";font-size: 12px;}
.item .spec {flex: 2;color: #888;font-size: 14px;}
.item .count {flex: 1;color: #aaa;}
.total {width: 990px;margin: 0 auto;display: flex;justify-content: flex-end;border-top: 1px solid #e4e4e4;padding: 20px;}
.total .amount {font-size: 18px;color: firebrick;font-weight: bold;margin-right: 50px; }
</style>
</head>
<body>
<div class="list"> </div>
<div class="total">
<div>合计:<span class="amount">1000.00</span></div>
</div>
<script>
const goodsList = [{
id: '4001172',
name: '逞心如意手摇咖啡磨豆机咖啡豆研磨机',
price: 289,
picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
count: 2,
spec: {color: '白色'}
},
{
id: '4001009',
name: '竹制干泡茶盘正方形沥水茶台品茶盘',
price: 109,
picture: 'https://yanxuan-item.nosdn.127.net/2d942d6bc94f1e230763e1a5a3b379e1.png',
count: 3,
spec: {size: '40cm*40cm',color: '黑色'}
},
{
id: '4001874',
name: '古法温酒汝瓷酒具套装白酒杯莲花温酒器',
price: 488,
picture: 'https://yanxuan-item.nosdn.127.net/44e51622800e4fceb6bee8e616da85fd.png',
count: 1,
spec: {color: '青色',sum: '一大四小'}
},
{
id: '4001649',
name: '大师监制龙泉青瓷茶叶罐',
price: 139,
picture: 'https://yanxuan-item.nosdn.127.net/4356c9fc150753775fe56b465314f1eb.png',
count: 1,
spec: {size: '小号',color: '紫色'},
gift: '50g茶叶,清洗球,宝马,奔驰'
}]
// 1. 根据数据渲染页面
document.querySelector('.list').innerHTML = goodsList.map(item => {
// console.log(item); //每一条对象
// 对象解构
const {picture,name,count,price,spec,gift} = item
// 规格文字模块处理
const text = spec? Object.values(spec).join('/'):''
// 计算小计模块 单价 * 数量 保留两位小数
// 关于小数的计算精度问题:0.1+0.2=0.3000000004
// 解决方案:乘10再除10
// (0.1*100 + 0.2*100) / 100 === 0.3
// 而两位小数点就是要 *100
const subTotal = ((price * 100 * count) / 100).toFixed(2)
// 处理赠品模块
const str = gift ? gift.split(',').map(item => `<span class="tag">【赠品】 ${item}</span>`).join('') :''
return `
<div class="item">
<img src=${picture} alt="">
<p class="name">${name} ${str}</p>
<p class="spec">${text}</p>
<p class="price">${price.toFixed(2)}</p>
<p class="count">x${count}</p>
<p class="sub-total">${subTotal}</p>
</div>
`
}).join('')
// 3. 合计模块
const total = goodsList.reduce((prev,item) => prev + (item.price * 100 * item.count) / 100,0)
document.querySelector('.amount').innerHTML = total.toFixed(2)
</script>
</body>
</html>
持续学习中。。。
javascript进阶学习记录(下): https://blog.youkuaiyun.com/huang_99/article/details/144718982