- 1.不用委托事件删除动态新增元素的前提 12.22的待办事项案例
- 12.22随机点名思路:
- 2.开关思想:
- 3.localStorage: 本地存储 将数据存储到浏览器 (内存存储)(永久存储)
- 4.正则判断
- 5.面向对象:
- 6.数组对象api常用:
- 7.字符串常用api
- 8.了解工厂函数
- 9.(重点)构造函数
- 10.(重点)原型对象
- 11.面向对象的三大特征
- 12.(重点)原型链(框架底层基础)
- 13.instanceof 运算符
- 14.ES6类与继承
- 14.函数补充内容
- 1.this指向
- 2.函数上下文执行模式(call/apply/bind)
- 3.递归函数 :
- 4.闭包(closure):
- 5.总结梳理:
- Var的关键字特点:(预解析/没有块级)
- 1.ES6新语法
- 2.数组迭代方法
- 总结:推荐使用字面量方式声明数组,而不是 Array 构造函数
- 作用域链:
1.不用委托事件删除动态新增元素的前提 12.22的待办事项案例
必须发布成功了 新增成功了节点元素 才会有X按钮 否则在外发布点击事件外面 永远拿不到
// 一定要先有li,才可以获取删除按钮:
// doucument.querySelector():整个页面查询元素
// 父元素.querySelector():父元素中查找元素
1.1委托事件测试
<div>点我</div>
<ul>
<input type="text">
</ul>
<script>
let input = document.querySelector('input')
input.onkeydown = function (e) {
if (e.key == 'Enter') {
console.log(e.key);
console.log(input.value);
//创建空标签
let li = document.createElement('li')
// 3.2为空标签新增内容
li.innerHTML = `
<input type="checkbox" name="" id="">
<span>${input.value}</span>
<button class = "delete">x</button>
`
// 3.添加到DOM树 父元素.appendChild(子元素)
document.querySelector('ul').appendChild(li)
}
}
// 委托事件 处理动态新增元素、节点
document.querySelector('ul').onclick = function (e) {
if (e.target.classList.contains('delete')) {
this.removeChild(e.target.parentNode)
}
}
document.querySelector('div').onclick = function () {
this.style.backgroundColor = 'pink'
this.style.textDecoration = "line-through"
}
1.2删除对应勾选框测试:
<ul>
<li class="dis"><input type="checkbox" name="num" id="" checked>0</li>
<li><input type="checkbox" name="num" id="">1</li>
<li><input type="checkbox" name="num" id="">2</li>
<li><input type="checkbox" name="num" id="">3</li>
<li><input type="checkbox" name="num" id="">4</li>
</ul>
<button>删除</button>
<script>
let button = document.querySelector('button')
let inputList = document.querySelectorAll('input')
// 1.点击按钮
button.onclick = function () {
//遍历每个选择框
for (let i = 0; i < inputList.length; i++) {
console.log(inputList[i].checked);
// 如果选择框被勾选了
if (inputList[i].checked) {
// 就删除 这个选择框
let liList = document.querySelectorAll('li')
liList[i].classList.add('dis')
}
}
}
</script>
12.22随机点名思路:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
/* background-image: linear-gradient(rgb(255, 24, 24), rgb(37, 37, 255)); */
background-attachment: fixed;
transition: all 10s !important;
}
h1 {
text-align: center;
background-color: chartreuse;
transition: all 1s !important;
}
.sn {
margin: 10px;
color: #c07ac8;
}
.btn {
background-color: #9acc32;
color: #fff;
width: 50px;
height: 50px;
margin: 10px;
}
select {
width: 80px;
height: 26px;
background-color: #feebcd;
}
.uname ul li {
float: left;
list-style: none;
background-color: #000;
font-size: 14px;
color: #fff;
width: 70px;
height: 35px;
margin: 3px;
line-height: 35px;
text-align: center;
}
.uname ul li.active {
background-color: #acfe2f;
color: #c07ac8;
}
.sn span {
margin-left: 10px;
}
</style>
</head>
<body>
<!-- ### 题2:数据的基本添加和渲染
- 目标
- 能够收集页面元素的数据,并生成自己需要的数据
- 理解页面操作与数据之前的关联,理解页面操作本质上就是数据操作,这对后期及至项目的学习都有重大的意义
- 掌握页面元素数据的收集
- 能够理解封装的需要,实现功能的封装
- 效果:
- 需要掌握的技能
- 根据dom元素数据生成需要的数据对象
- 数组操作api的使用
- 添加数据之后的页面刷新--函数封装 -->
<h1>点名系统</h1>
<h2 class="sn">被选中的小伙伴: <span>彭雅娴</span></h2>
<button class="btn">开始</button>
时间:
<select name="" id="" class="timer">
<option value="10">10</option>
<option value="9">9</option>
<option value="8">8</option>
<option value="7">7</option>
<option value="6">6</option>
<option value="5">5</option>
<option value="4">4</option>
<option value="3">3</option>
<option value="2">2</option>
<option value="1">1</option>
</select>
<div class="uname">
<ul>
<li class="active">吕毕丹</li>
<!-- <li>孙庆材</li> -->
</ul>
</div>
<script>
let unameArr = [
'梦洁',
'雅静',
'韵寒',
'莉姿',
'沛玲',
'欣妍',
'歆瑶',
'凌菲',
'靖瑶',
'阮秋帛',
'赵同原',
'邹月蕴',
'马文松',
'熊可渊',
'唐飘遥',
'严 华',
'叶 霞',
'韵寒',
'莉姿',
'沛玲',
'欣妍',
'歆瑶',
'凌菲',
'靖瑶',
'阮秋帛',
'赵同原',
'邹月蕴',
'马文松',
'熊可渊',
'唐飘遥',
'严 华',
'叶 霞',
'韵寒',
'莉姿',
'沛玲',
'欣妍',
'歆瑶',
'凌菲',
'靖瑶',
'阮秋帛',
'赵同原',
'邹月蕴',
'马文松',
'熊可渊',
'唐飘遥',
'严 华',
'叶 霞',
'封 英',
'方顺清',
'何 芹',
'叶 霞',
'封 英',
'崔致敏',
'邹月蕴',
'马文松',
'夏 良',
'崔致敏',
'封腾麟',
'马文松',
'熊可渊',
'唐飘遥',
]
// 新增节点
// 循环得到每个li
for (let i = 0; i < unameArr.length; i++) {
// 1.创建空标签
let li = document.createElement('li')
// 2.将后台数据渲染到页面
li.innerText = unameArr[i]
// 3.添加到DOM树
document.querySelector('.uname ul').appendChild(li)
}
// 1.获取元素
let btn = document.querySelector('.btn')//按钮
let liList = document.querySelectorAll('.uname li')
// 2.事件注册 点击
btn.onclick = function () {
let timeID = setInterval(function () {
// 2.1事件处理 姓名随机li
// 2.2 得到随机下标
let rondomIndex = Math.floor(Math.random() * (unameArr.length)) //含最大值,含最小值
console.log(unameArr[rondomIndex]);
// 得到每个li 这个循环好像可以省略
for (let i = 0; i < liList.length; i++) {
//排他思想 狙击排他思想 找到含有active类名的兄弟 移除他 不移除就可以依次显示在页面
document.querySelector('.uname li.active').classList.remove('active')
// 通过随机下标 为自己添加样式
liList[rondomIndex].classList.add('active')
}
}, 200)
// 3.开启定时器
//3.1定义 自定义倒计时 取得时间框的对应下拉菜单value时间
let timer = document.querySelector('.timer').value
let timeID2 = setInterval(function () {
// 3.2 对应的下拉菜单时间time--
timer--
console.log(timer);
if (timer <= 0) {
// 清除随机点名倒计时
clearInterval(timeID)
// 清除自身定时器
clearInterval(timeID2)
// 将被点到的人 显示在页面
// document.querySelector('.sn span').innerText += document.querySelector('.uname li.active').innerText
let span = document.createElement('span')
span.innerText = document.querySelector('.uname li.active').innerText
document.querySelector('.sn').appendChild(span)
}
}, 1000)
}
//3.背景颜色变化 随机颜色
let timeID = setInterval(function () {
let h1 = document.querySelector('h1')
let randomColor1 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
let randomColor2 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
let randomColor3 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
// console.log("rgb" + "(" + randomColor + "," + randomColor1 + "," + randomColor2 + ")");
h1.style.color = "rgb" + "(" + randomColor1 + "," + randomColor2 + "," + randomColor3 + ")"
// document.body.style.backgroundImage = ""
// console.log("linear - gradient" + "(" + "rgb" + "(" + randomColor + "," + randomColor1 + "," + randomColor2 + ")" + "," + "rgb" + "(" + randomColor3 + "," + randomColor2 + "," + randomColor1 + ")" + ")")
}, 1000)
// let bg = setInterval(function () {
// let h1 = document.querySelector('h1')
// let randomColor1 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
// let randomColor2 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
// let randomColor3 = Math.floor(Math.random() * (255 - 0 + 1) + 0)
// document.body.style.backgroundImage = "linear-gradient" + "(" + "rgb" + "(" + randomColor1 + "," + randomColor2 + "," + randomColor3 + ")" + "," + "rgb" + "(" + randomColor3 + "," + randomColor2 + "," + randomColor1 + ")" + ")"
// }, 1000)
</script>
</body>
</html>
2.开关思想:
开关思想 : 用于解决一类问题的思路
判断数组中是否所有元素都满足条件
1.选择框是否全部勾选
2.表单是否全部填写
3.正则是否全部通过
let flag = true 全部成立
if(条件不满足){ //只要有一个条件不成立
flag =false //条件就不成立 后面就没有必要检查
break
}
全部条件 =flag
3.localStorage: 本地存储 将数据存储到浏览器 (内存存储)(永久存储)
2.语法: <br /> 存: localStorage.setItem('属性名', '属性值')<br /> 取:let 变量名 = localStorage.getItem('属性名')<br /> 删: localStorage.removeItem('属性名')<br /> 清空: localStorage.clear()<br /> <br /> 3.注意点<br /> 3.0 存储没有返回值 读取需要返回值<br /> 3.1 存储的数据只能是字符串类型。如果是其他数据类型则会自动调用toString()方法转成字符串<br /> 3.2 永久存储。除非自己删,否则一直存在于浏览器
*/
document.querySelector('.btn1').onclick = function () {
localStorage.setItem('uname', '张三')//硬盘存储
localStorage.setItem('password', '123456')
localStorage.setItem('age', 18)
localStorage.setItem('nickname', ['测试', '测试'])//存储的数据只能是字符串类型。如果是其他数据类型则会自动调用toString()方法转成字符串
}
取数据:
let 变量名= localStorage.getItem('age')//存储没有返回值 读取需要返回值
console.log(变量名)
sessionStorage.setItem('uname', '张三') // 临时存储---内存存储
/4.将用户名和密码存入 localStorage
let obj = {
username : username.value,
password : password.value
}
/* localStorage只能存储字符串类型,如果是对象需要转成JSON字符串 */
localStorage.setItem('user', JSON.stringify(obj) )
4.正则判断
5.面向对象:
- 面向对象不是一门技术,而是一种解决问题的思维方式
- 面向对象的本质是对面向过程的一种封装
面向对象:注重的是结果
面向过程:注重的是过程
6.数组对象api常用:
7.字符串常用api
<script>
let str = '风止意难平'
//1.字符串也可以像数组一样 通过下标获取某个字符串
console.log(str[1]);//止
//2.indexOf() 返回某个指定首字符串的位置下标
//应用:检查一个字符串中是否有某个字符
//有:则返回下标 没有:返回固定值 -1
let a = str.indexOf('难平')
console.log(a);//3 //有:则返回下标位置
let b = str.indexOf('嗯')
console.log(b) //-1 //没有:返回固定值 -1
// 3.split('分隔符') :切割字符串
//返回值一定是数组: 元素对应切割的每一个部分
let url = 'https://www.yuque.com?name="张三",age="18"'
let c = url.split('?')
console.log(c);// ['https://www.yuque.com', 'name="张三",age="18"']
d = c[1].split(',')
console.log(d);//['name="张三"', 'age="18"']
let f = d.join() //把数组元素拼接成一个字符串
console.log(f); //name = "张三", age = "18"
//4.substr(起始下标,截取长度) :截取部分字符串
let str2 = "好好学习,天天向上"
let l = str2.substr(5, 4)
console.log(l)//天天向上
// 5.replace("要替换的内容","替换成的文字")
let str3 = "好好学习,天天向上"
let m = str3.replace("好好学习,天天向上", "键盘敲烂,月薪过万")
console.log(m)//键盘敲烂,月薪过万
</script>
8.了解工厂函数
<script>
// 解决代码冗余方案: 封装函数
// 什么是工厂函数 : 创建多个对象的函数
function creatPerson(name, age, sex) {
// 1.声明对象
let p = {
// 2.属性名:属性值
namer: name,
ager: age
}
//2.对象.属性名 = 属性值
p.sex = sex
// 3. 返回值
return p
}
let zxy = creatPerson('张学友', 18, '男')
let ldh = creatPerson('刘德华', 20, "男")
console.log(zxy);
console.log(ldh);
</script>
9.(重点)构造函数
构造函数:作用于工厂函数一致,但是代码更少
构造函数:使用new来调用的函数
new 工作原理:
1.创建一个空对象{}
2.this指向这个对象 this = {}
3.对象赋值 this.属性名 = 属性值
4.返回这个对象
new 细节:
(1)构造函数首字母大写:为了提醒别人不要忘记new关键字
(2)构造函数里主动写了return:
如果是值类型:无效 继续返回new创建的对象
引用类型:有效 会覆盖new创建的对象
function Person(name, age) {
// 1.创建一个空对象{}
// 2.this指向这个对象 this.属性名 = {}
// 3.对象赋值
this.name = name
this.age = age
//4.返回这个对象
}
let zs = new Person('张三', 18)
let l4 = new Person('李四', 19)
console.log(zs, l4);
10.(重点)原型对象
1.原型对象:
- 创建函数的时候,系统会自动帮我们创建一个与之对应的对象,称为原型对象
2.原型对象作用:
- **解决内存浪费+ 变量污染**
3.构造函数 原型对象 实例对象的关系
3.1 prototype:属于构造函数,指向原型对象
作用:解决内存浪费+变量污染
3.2 proto:属于实例对象,指向原型对象
作用:实例对象 访问原型对象的成员
3.3 constructor:属于原型对象,指向构造函数
作用让实例对象知道自己是谁创建的
先有构造函数 再有原型对象 再有实例对象
11.面向对象的三大特征
封装:
1.混入式继承
遍历 父对象 的所有属性值,添加给 子对象
特点:每继承一次,就要执行一次循环
应用场景:单个对象继承
//继承:让一个对象拥有另一个对象的属性和方法
let father = {
house: {
address: '北京一环',
price: 100000000
},
car: {
brand: '劳斯莱斯',
price: 5000000
}
}
let son = {
name: 'ikun',
age: 30
}
//1.混入式
//解决方案:遍历父对象的所有属性值,添加给子对象
//特点:每继承一次,就要执行一次循环
//应用场景 : 单个对象继承
for (let key in father) {
son[key] = father[key]
}
console.log(son)
2.替换原型:
将父对象作为子对象构造函数的原型
特点:会丢失原型之前的成员变量
应用场景:多个对象继承
let father = {
house: {
address: '北京一环',
price: 100000000
},
car: {
brand: '劳斯莱斯',
price: 5000000
}
}
function Son(name, age) {
this.name = name
this.age = age
}
Son.prototype.sayHi = function () {
console.log('你好')
}
//让父对象成为子对象构造函数的原型
Son.prototype = father
console.log(Son.prototype);
//实例化对象
let son1 = new Son('ikun', 30)
console.log(son1)
let son2 = new Son('班长', 20)
console.log(son2)
3. 混合式继承
混入式 + 替换原型
特点 :遍历 父对象 所有的属性值,添加给 子对象构造函数 的原型
应用场景:多个对象继承,但是不会覆盖原来的默认原型
let father = {
house:{
address:'北京一环',
price:100000000
},
car:{
brand:'劳斯莱斯',
price:5000000
}
}
//1.构造函数
function Son (name,age ) {
this.name = name
this.age = age
}
//2.默认原型对象
Son.prototype.sayHi = function(){
console.log('你好')
}
//4.(混合式继承)将父对象的所有属性添加到子对象构造函数的原型中
for(let key in father){
Son.prototype[key] = father[key];
}
//3.实例化对象
let son1 = new Son('ikun',30)
console.log ( son1 )
let son2 = new Son('班长',20)
console.log ( son2 )
12.(重点)原型链(框架底层基础)
1.原型链:
每一个对象,都有proto指向自身的原型。 而原型也是对象,也有自己的
proto指向原型的原型,以此类推形成链式结构,称之为原型链。
2.对象访问原型链中成员的规则 :就近原则
当访问对象的成员,先看自己有没有。有则访问,没有则看原型有没有。原型有则访问,
没有则访问原型的原型,没有则继续往上找。以此类推,一直找到原型链终点 :null .
如果还找不到, 如果是属性 : 则获取undefined 如果是方法 ,则报错xxx is not defined .
原型对象里的成员 实例对象可以直接使用 因为实例对象里面有proto属性指向原型对象
3.原型链作用 : 继承
js语言 通过 原型链 实现面向对象继承
只有对象才有原型,这里一定要把基本数据类型string、number、boolean,和基本包装类型(特殊的引用类型对象)String、Number、Boolean区分开来,不要搞混淆
<script>
/*
1.原型链 :每一个对象,都有__proto__指向自身的原型。 而原型也是对象,也有自己的
__proto__指向原型的原型,以此类推形成链式结构,称之为原型链。
2.对象访问原型链中成员的规则 :就近原则
当访问对象的成员,先看自己有没有。有则访问,没有则看原型有没有。原型有则访问,
没有则访问原型的原型,没有则继续往上找。以此类推,一直找到原型链终点 : null.
还没有, 如果是属性 : 则获取undefined 如果是方法 ,则报错xxx is not defined
*/
//1.构造函数
function Person(name,age){
this.name = name;
this.age = age;
};
//2.原型对象
Person.prototype.sayHi = function(){
console.log('人生若只如初见,何事秋风悲画扇');
};
Person.prototype.type = '哺乳动物';
//3.实例化对象
let p1 = new Person('又又',18);
console.log(p1);
//请说出以下代码执行结果
console.log(p1.name);//又又 p1自己有name属性
console.log(p1.type);//哺乳动物 p1自己没有type,但是p1的原型有
console.log(p1.hobby);//undefined p1自己没有hobby,原型也没有
p1.sayHi();// 人生若只如初见,何事秋风悲画扇 p1自己没有这个方法,原型有
// p1.eat();//报错 xxx is not defined p1自己没有这个方法,原型也没有
//为什么不报错? p1自己没有这个方法,原型也没有这个方法,但是原型的原型有
p1.toString();
//查看p1的原型链
console.log(p1.__proto__.constructor);//Person
console.log(p1.__proto__ === Person.prototype);//true
//查看p1的原型的原型
console.log(p1.__proto__.__proto__.constructor);//Object
console.log(p1.__proto__.__proto__ === Object.prototype);//true
//查看p1的原型的原型的原型
console.log(p1.__proto__.__proto__.__proto__);//null
</script>
13.instanceof 运算符
instanceof运算符作用: 限制函数参数数据类型
例如: …… not of type …… 报错 函数参数数据类型错误
用于检测构造函数的prototype属性 是否出现在某个实例对象的原型链上
instanceof语法: 实例对象 instanceof 构造函数
let arr = [10,20,30]
//数组原型链 arr->Arr.prototype->Object.prototype->null
console.log ( arr instanceof Array )//true
console.log ( arr instanceof Object )//true
//报错: 参数不是节点
//instanceof关键字一般用在函数中,用于限制参数的数据类型
//例如 appendChild()参数只能是node节点,如果你传了其他类型就会报错。底层就会用到instanceof来检测参数数据类型
let h1 = document.createElement('h1')
h1.innerText = '我是h1标签'
document.body.appendChild(h1)
14.ES6类与继承
1.class关键字 :
作用: 声明一个类函数
相当于ES5的构造函数,只是代码的阅读性大大提高
1. 把构造函数和原型全部写在一个大括号里面,提高代码阅读性
2. 必须要使用new才可以调用class函数,否则会报错 提高代码规范
*class关键字语法:
class 构造函数名{
constructor(){
//(1)这里写构造函数代码
};
//(2)原型里面的方法写在下面
eat(){
};
play(){
};
};
class本质其实就是构造函数的另一种写法,本质还是给prototype添加成员
使用了class,一样可以继续使用ES5的prototype
左右两边的结果完全一致
2.extends关键字 : 作用:类继承
语法:<br />**class 子类函数 extends 父类函数 ** (extends不能脱离class单独使用)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/25421229/1640421632791-e6e509c4-e7a3-44f4-93fb-fa64063a9303.png#clientId=u76ff8b62-4ba3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=323&id=uf8a20314&margin=%5Bobject%20Object%5D&name=image.png&originHeight=336&originWidth=683&originalType=binary&ratio=1&rotation=0&showTitle=false&size=147749&status=done&style=none&taskId=u8a64225e-6d61-4c7a-9ba9-6a07c748a39&title=&width=657.4943237304688)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/25421229/1640433870091-99d1d425-94f2-4f0f-8f8e-f9550698b84f.png#clientId=u76ff8b62-4ba3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=364&id=uff4be596&margin=%5Bobject%20Object%5D&name=image.png&originHeight=624&originWidth=1227&originalType=binary&ratio=1&rotation=0&showTitle=false&size=231466&status=done&style=none&taskId=u54d85198-3314-4f9d-9c26-e86ad159b08&title=&width=715.4943237304688)
<script>
class Person {
//1.构造函数
constructor(name, age) {
this.name = name
this.age = age
}
//2.原型方法
eat() {
console.log('学习');
}
}
//3.实例多选
let p1 = new Person('李四', 20)
console.log(p1);// Person { name: '李四', age: 20 }
//extends关键字作用:类继承
// 语法: class 子类函数 extends 父类函数 (extends不能脱离class单独使用)
// 底层: 替换原型的原型继承(不会覆盖自构造器的原型)
// Student.prototype.__proto__ = Person.prototype
/*
s1.__proto__ == Student.prototype //s1指向的原型对象 等于构造函数的prototype属性指向
s1.__proto__.__proto__ == Student.prototype.__proto__ == Person.prototype
*/
class Student extends Person {
//子构造函数原型方法
learn() {
console.log('我今天学的很开心');
}
}
let s1 = new Student('张三', 22)
console.log(s1)//Student { name: '张三', age: 22 }
s1.learn()//调用子类自己的方法 //我今天学的很开心
s1.eat()//调用父类方法 //学习
</script>
3.super关键字 : 子类中调用父类的方法
如果在子类中,也写了 constructor函数,则必须要调用 super()否则程序报错
语法: super(父类构造函数参数1,父类构造函数参数2)
class 子类函数 extends 父类函数{
//子类构造函数
constructor(父类构造函数参数1,父类构造函数参数2,子类参数){
super(父类构造函数参数1,父类构造函数参数2)
this className = className
}
}
// 4.子类继承父类
class Teacher extends Person {
// 5.子类构造函数
constructor(name, age, className) {
//6.子类如果想拥有自己的额外属性,就需要加上constructor (父类构造函数参数1,父类构造函数参数2,自己的参数...)
//此时必须要调用super(父类构造函数参数1,父类构造函数参数2),否则报错
super(name, age)
this.className = className
}
//7.子构造函数原型方法
teacher() {
console.log('看书');
}
}
<script>
// 1.class类
class Person {
// 2.父类构造函数
constructor(name, age) {
this.name = name
this.age = age
}
//3.原型对象
sayHi() {
console.log('sayHI');
}
}
// 4.子类继承父类
class Teacher extends Person {
// 5.子类构造函数
constructor(name, age, className) {
//6.子类不能有自己的constructor,
// 如果要自己写constructor (父类构造函数参数1,父类构造函数参数2,自己的参数...)
// 则必须要调用super(父类构造函数参数1,父类构造函数参数2)
super(name, age)
this.className = className
}
//7.子构造函数原型方法
teacher() {
console.log('看书');
}
}
let t1 = new Teacher('张三', 20, '十一班')
console.log(t1);
</script>
14.函数补充内容
1.函数中的关键字: arguments
arguments 是一个伪数组arguments 的作用是动态获取函数的实参什么时候用arguments:参数不固定的时候用这个arguments注意:建议如果参数固定用形参,参数不固定用arguments
作用:arguments:获取函数所有的实参(伪数组)<br /> 场景: 例如 arr.push() 底层就是用arguments获取实参
function fn(a,b){
console.log(a,b)//10,20
console.log( arguments )//获取函数所有实参
}
fn(10,20,30,40,50)
2.rest参数(剩余参数) : …形参名
作用:获取所有的剩余参数(真数组)<br /> 场景: 绝大多数情况下,剩余参数可以替代arguments
1.剩余参数(rest参数) : 获取函数剩余的所有实参
* 是一个真数组
* 只能写在函数最后形参位置
2.场景:大多数情况下, rest参数可以替代arguments
arguments是老语法,浏览器兼容性好
rest参数:新语法,有些老的浏览器不支持
*/
function fn(a, b, ...c) {
console.log(arguments)//获取所有实参 10 20 30 40
console.log(a, b)//10 20
console.log(c)//[30,40] 获取剩余参数
}
fn(10, 20, 30, 40)
3.函数默认参数:
1.函数传参本质是:实参给形参赋值
2.函数形参和实参数量可以不一致,但是会按照顺序一一赋值
3.函数默认参数:
ES5 : 逻辑 或 中断
ES6 : 形参 = 默认值
function fn(a=10,b=20){
//找真 : 左边为真,返回左边的值。反之返回右边的值
// a = a || 10
// b = b || 20
console.log(a)//5
console.log(b)//20
}
fn(5)//底层 fn(5,undefined)
1.this指向
- 函数三种执行模式 : 全局函数 、 对象方法 、 构造函数
- this : 谁
调用
我,我就指向谁 (与函数的声明无关 取决于函数的调用)- 全局函数 :函数名( ) —————————>this指向window
- 对象方法 :对象名.方法名( ) ———-> this指向对象
- 构造函数 :new 函数名( )——————>this指向new创建的空对象
分析this指向心得 : this指向取决于函数调用,而函数有三种调用方式。所以this指向也会有三种情况
(1)优先用排除法分析, 如果有new关键字,则指向new创建的对象。 否则不是window就是对象
(2)如何判断this是window还是对象,就看调用是: 函数名() 还是 对象名.方法名()
let obj = {
name: 'ikun',
sayHi: function () {
function fn() {
console.log(this);
}
fn()// 普通函数调用 :函数名() fn调用了this 所以this指向window
}
}
obj.sayHi()//window
let obj1 = {
name: 'ikun',
sayHi: function () {
console.log(this);
}
}
obj1.sayHi() //对象方法调用 :对象名.方法名() 对象obj1调用了this 所以this指向obj1对象
function Fn() {
console.log(this);
}
new Fn()//构造函数调用 :new 函数名() 根据new执行流程/创建一个空对象-this指向这个对象-对象赋值-返回对象 这里的this指向new创建的空对象
共同的特点: this的指向无法动态修改
2.函数上下文执行模式(call/apply/bind)
2.1上下文模式注意点
- a.函数上下文三个方法:
call()
apply()
bind()
,它们定义在Function构造函数的原型中 - b.如果将this指向修改为值类型:(number,boolean,string),则函数会自动帮我们包装成对应的引用类型(基本包装类型)
- 值类型:
'123'
,1
,true
- 基本包装类型:
String('123')
,Number(1)
,Boolean(true)
- 值类型:
2.2函数上下文作用:可以动态修改函数中的this指向
2.3 异同点:
- 相同之处:都可以修改函数中this指向
- 不同点:
1.传参方式不同 : call是一一传参,apply是数组/伪数组传参
2.执行机制不同 : call、apply会立即执行函数, bind不会立即执行函数
2.3 语法
有3种写法,作用都是一样改this,应用场景不同
- call()语法:
函数名.call(this修改后的指向,参数1,参数2,...)
———————适用于函数原本形参 <= 1- apply()语法:
函数名.apply(this修改之后的指向,伪数组或者数组)
——————-适用于函数原本形参 >= 2- bind()语法:
函数名.bind(this修改后的指向)
bind()语法并不会立即执行函数,而是返回一个修改指向后的新函数,常用于回调函数
如果bind的时候传参,则参数也会绑定,之后无法传递实参
<script>
/*
1.函数三种执行方式 :
全局函数 : this指向window
对象方法: this指向对象
构造函数 : this指向new创建的对象
共同的特点: this的指向无法动态修改
2.函数上下文模式 :
2.1 作用: 可以动态修改函数中的this
2.2 语法: 有3种写法,作用都是一样改this,应用场景不同
a. 函数名.call(修改的this,arg1,arg2…………)
* 适用于函数原本形参 <= 1
b. 函数名.apply(修改的this,[数组或伪数组])
* 适用于函数原本形参 >= 2
c. 函数名.bind(修改的this,arg1,arg2…………)
* 特点:不会立即执行这个函数,而是返回修改this后的新函数
* 适用于不会立即执行的函数 : 事件处理函数,定时器函数
*/
// function fn(){
// //三种执行模式this无法动态修改
// //this = {age:18};
// console.log(this);
// };
// fn();//this:window
/* 上下文模式 */
function fn(a, b) {
console.log(this);
console.log(a + b);
};
//a. 函数名.call(修改的this,arg1,arg2…………)
//应用场景: 适用于函数原本形参 <= 1
fn(10, 20);//this:window 30
fn.call({ age: 18 }, 100, 200); //age:18 300
//b. 函数名.apply(修改的this,[数组或伪数组])
//应用场景: 适用于函数原本形参 >=2
//apply传参的时候会自动的遍历这个数组,然后按照循序逐一传参
fn.apply({ age: 88 }, [20, 30]);
//c. 函数名.bind(修改的this)
//特点:这个函数不会立即执行,而是返回一个修改this之后的新函数
//应用场景 : 事件处理函数,定时器
let newFn = fn.bind({ name: '坤坤' });
console.log(newFn);
newFn(50, 60);
//4. 定时器中的this一定是指向window
// 定时器:一段代码间隔事件执行 setTimeout(一段代码,间隔时间)
//4.1 具名函数
let test = function () {
console.log('我是具名函数');
console.log(this);
};
let newTest = test.bind({ name: '张三' })
setTimeout(newTest, 3000);
//4.2 匿名函数
setTimeout(function () {
console.log('我是定时器中的函数');
console.log(this);
}.bind({ name: '李四' }), 2000);
</script>
call()上下文调用
function fn(a,b){
console.log( this )
console.log(a+b)
}
fn(1,2)//this指向window
//函数名.call(修改的this,参数1,参数2,....)
fn.call({name:'张三'},10,20)
函数.call() 应用场景
Object.prototype.toString.call()万能检测数据类型
1.Object.prototype.toString() 得到固定格式字符串
2.检测数据类型固定格式语法:Object.prototype.toString.call()
得到一个固定格式字符串: [object 数据类型]**
/* call场景:数据类型检测 */
//1. typeof 数据 : 检测数据类型
/* typeof有两种数据类型无法检测: null,array 都会得到object */
console.log( typeof '123' )// 'string'
console.log( typeof 123 )//'number'
console.log( typeof true )//'boolean'
console.log( typeof undefined )//'undefined'
console.log( typeof null )//'object'
console.log( typeof [10,20,30] )//'object'
console.log( typeof function(){} )//'function'
console.log( typeof {name:'123'} )//'object'
//2. Object.prototype.toString() : 得到固定格式字符串
//"[object type]",其中 type 是对象的类型
//检测数据类型固定格式语法: Object.prototype.toString.call(数据)
console.log( Object.prototype.toString.call('123') )//[object String]
console.log( Object.prototype.toString.call(123) )//[object Number]
console.log( Object.prototype.toString.call(true) )//[object Boolean]
console.log( Object.prototype.toString.call(undefined) )//[object Undefined]
console.log( Object.prototype.toString.call(null) )//[object Null]
console.log( Object.prototype.toString.call([10,20,30]) )//[object Array]
console.log( Object.prototype.toString.call(function(){}) )//[object Function]
console.log( Object.prototype.toString.call({name:'123'}) )//[object Object]
apply()上下文调用
function fn(a,b){
console.log(this)
console.log(a+b)
}
fn(1,2)//普通函数 this->window
//(1)函数名.call(修改的this,参数1,参数2,....)
fn.call({name:'张三'},10,20)
//(2)函数名.apply(修改的this, 数组/伪数组 )
//apply传参的时候会自动的遍历这个数组,然后按照循序逐一传参 a = [30,40][0] b=[30,40][1]
fn.apply({name:'李四'},[30,40])
</script>
arr.push.apply(arr,伪数组)的应用场景:伪数组转真数组
<script>
/*
伪数组: 有数组三要素(下标、元素、长度),但是不能使用数组的方法
伪数组 本质是对象
伪数组不能使用数组方法: 伪数组的原型指向对象的原型,而不是Array的原型
*/
let obj = {
0: 10,
1: 20,
2: 30,
3: 40,
length: 4,//length 省略不了
}
console.log(obj[0]);
//将伪数组转成真数组 arr.push 往数组后面新增元素 返回的是数组的长度
let newArr = []
let a = newArr.push(obj[0], obj[1], obj[2], obj[3])
console.log(newArr)//[10, 20, 30, 40]
console.log(a)//数组长度 4
// ES5:上下文
let newArr1 = []
newArr1.push.apply(newArr1, obj)
//第一个参数:newArr1 本来this就是newArr 这里不需要修改this(相当于this不变)
//第二个参数:obj 借助apply特点:自动遍历伪数组/数组,逐一传参(自己省去了for循环)
console.log(newArr1);
// ES6推荐: Arr.from(伪数组) 伪数组转成真数组
let arr = Array.from(obj)
console.log(arr);
</script>
Math.max.apply(Math, arr)应用场景:求数组最大值
<script>
//求数组最大值
let arr = [10, 13, 12, 3, 14, 23, 32]
// 1.arr.sort() 数组排序 参数:回调函数 固定语法死记硬背
// arr.sort(function (a, b) {
// return a - b//从小到大排
// return b - a//从大到小排序
// })
arr.sort(function (a, b) {
return b - a
})
console.log(arr[0]);
//2.擂台思想
let max = 0
for (let i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i]
}
}
console.log(max)
// ES5: Math.max.apply(Math, 数组名)
//函数名.apply(修改的this,数组/伪数组)
//第一个参数 Math: Math调用了方法 this指向的就是Math,这里就不需要修改this(传Math,相当于this不变)
//第二个参数 arr :借助apply的特点.自动遍历数组/伪数组 逐一传参
let max3 = Math.max.apply(Math, arr)
console.log(max3);
//ES6(推荐):
let max4 = Math.max(...arr)
console.log(max4)
</script>
bind()上下文调用
function fn(a,b){
console.log(this)
console.log(a+b)
}
fn(1,2)//普通函数 this->window
//(1)函数名.call(修改的this,参数1,参数2,....)
fn.call({name:'张三'},10,20)
//(2)函数名.apply(修改的this, 数组/伪数组 )
//apply传参的时候会自动的遍历这个数组,然后按照循序逐一传参 a = [30,40][0] b=[30,40][1]
fn.apply({name:'李四'},[30,40])
//(3)函数名.bind(修改this)
//bind不会立即调用函数,而是得到一个修改this之后的新函数
let newFn = fn.bind({name:'王五'})
newFn(25,66)
bind()应用场景
bind场景主要是修改 ‘不需要立即执行的函数
//事件处理函数、定时器函数
//bind场景主要是修改 '不需要立即执行的函数'
// 事件处理函数、定时器函数
//1.定时器中的this一定是window
let fn = function(){
console.log( this )
}
//使用bind修改this
let newFn = fn.bind({name:'666'})
// fn() : 调用函数, 运行结果是函数返回值
// fn : 变量取值, 取出fn中存储的堆地址
setTimeout( newFn ,3000)
//下面这个写法和上面写法是一样的 : 函数是数据类型,也可以像其他数据一样直接使用语法
setTimeout(function(){
console.log(this)
}.bind({name:'干饭'}),3000)
上下文模式注意点
/*
1. 函数上下问执行模式 : 动态修改this
注意点 : 修改的this只能是引用类型
2.注意点: this一定是指向对象(引用类型)
a.如果修改的this是 : string,number,boolean则编译器会自动转成对应的对象类型。
基本包装类型 : new String() new Number() new Boolean()
b.如果修改的this是: undefined和null,则修改无效
*/
function fn() {
console.log(this);
};
fn.call('str');//String
fn.call(1);//Number
fn.call(true);//Boolean
//如果传的是undefined和null,或者不传。代码不会报错,也不会帮我们修改this,还是原来的window
fn.call(undefined);//Window
fn.call(null);//Window
fn.call();//Window
fn.call(window);//Window
3.递归函数 :
/ 高阶函数: 自调用函数、回调函数、闭包、递归 /
什么是递归函数? : 函数内部调用自己
注意点: 需要满足条件才会递归,否则会导致死循环
递归函数和循环功能类似
//一个函数递归
// function fn(){
// console.log('哈哈');
// fn();
// };
// fn();
//两个函数递归
// function fn1(){
// console.log('哈哈');
// fn2();
// };
// function fn2(){
// console.log('呵呵');
// fn1();
// };
// fn2();
//需求:写一个函数,打印三次 班长爱坤哥
let i = 1;
function fn(){
console.log('好好学习');
i++;
if(i <= 3){
fn();
};
//循环实现
// for(let i = 1;i<=3;i++){
// console.log('班长爱坤哥');
// };
};
fn();
递归应用场景:
2.1 浅拷贝与深拷贝
1.浅拷贝: 拷贝的是地址, 修改拷贝后的数据对原数据有影响
//声明一个对象
//拷贝: 把对象中存储的数据 拷贝一份赋值给其他对象
let obj = {
name:'ikun',
age:30,
hobby:['讲课','学生','学习'],
friend:{
name:'朋友',
sex:'男'
}
}
//浅拷贝: 拷贝的是地址
let obj1 = obj
//由于浅拷贝,拷贝的是地址。 所以修改拷贝后的数据,原来的数据也会变化
obj1.hobby = '美食'
console.log(obj1,obj)
2.深拷贝:拷贝的是数据, 修改拷贝后的数据对原数据没有影响
let obj = {
name:'ikun',
age:30,
hobby:['讲课','学生','学习'],
friend:{
name:'朋友',
sex:'男'
}
}
/*
(1)遍历obj,把所有的属性添加给newObj
(2)如果obj[key]是引用类型(数组、对象),则不能直接拷贝地址
(2.1)数组:给newObj声明一个空数组,然后遍历obj[key],把里面元素添加给newObj[key]
(2.2)对象:给newObj声明一个空对象,然后遍历obj[key],把里面元素添加给newObj[key]
(3)如果obj[key]不是引用类型,则直接赋值。结束递归
*/
function kaobei(newObj,obj){
for(let key in obj){
//判断 obj[key] 是不是数组类型
if( obj[key] instanceof Array ){
//声明一个空数组,然后继续拷贝数组里面的数据
newObj[key] = []
kaobei(newObj[key],obj[key])
}else if(obj[key] instanceof Object){
//声明一个空对象,然后继续拷贝数组里面的数据
newObj[key] = {}
kaobei(newObj[key],obj[key])
}else{
newObj[key] = obj[key]
}
}
}
//调用深拷贝函数
let newObj = {}
kaobei(newObj,obj)
//深拷贝:修改拷贝的数据,对原数据没有影响
newObj.hobby[0] = '111'
console.log( newObj,obj)
3.用JSON转换实现深拷贝
/* 2.深拷贝有两种实现方式
2.1 推荐使用:json转换
2.2 递归 :
*/
let obj = {
name:'ikun',
age:30,
hobby:['讲课','学生','学习'],
friend:{
name:'朋友',
sex:'男'
}
}
//(1)先把js对象转成json格式字符串 : JSON.stringify(js对象)
//json在把js转成json格式字符串的时候,底层会自动帮你深拷贝
// let json = JSON.stringify(obj)
// console.log( json )
//(2)再把刚才的json字符串,转成js对象 : JSON.parse( json格式 )
// let js = JSON.parse( json )
// js.hobby = '学习'
// console.log( js,obj )
//以上两个流程,可以简写成一行代码
let newObj = JSON.parse( JSON.stringify( obj ) )
newObj.friend = '小狗'
console.log(newObj,obj)
4.遍历dom树
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
.menu p {
width: 100px;
border: 3px solid;
margin: 5px;
}
.menu > div p {
margin-left: 10px;
border-color: red;
}
.menu > div > div p {
margin-left: 20px;
border-color: green;
}
.menu > div > div > div p {
margin-left: 30px;
border-color: yellow;
}
</style>
</head>
<body>
<div class="menu">
<!-- <div>
<p>第一级菜单</p>
<div>
<p>第二级菜单</p>
<div>
<p>第三级菜单</p>
</div>
</div>
</div> -->
</div>
<script>
//服务器返回一个不确定的数据结构,涉及到多重数组嵌套
let arr = [
{
type: '电子产品',
data: [
{
type: '手机',
data: ['iPhone手机', '小米手机', '华为手机']
},
{
type: '平板',
data: ['iPad', '平板小米', '平板华为']
},
{
type: '智能手表',
data: []
}
]
},
{
type: '生活家居',
data: [
{
type: '沙发',
data: ['真皮沙发', '布沙发']
},
{
type: '椅子',
data: ['餐椅', '电脑椅', '办公椅', '休闲椅']
},
{
type: '桌子',
data: ['办公桌']
}
]
},
{
type: '零食',
data: [
{
type: '水果',
data: []
},
{
type: '咖啡',
data: ['雀巢咖啡']
}
]
}
]
//arr:数据 father:父盒子
function addElement(arr,father){
//遍历数组,生成div>p添加到父元素中
for(let i = 0;i<arr.length;i++){
//(1)创建空标签
let div = document.createElement('div')
//(2)设置内容
div.innerHTML = `<p>${arr[i].type || arr[i]}</p>`
//(3)添加到父盒子
father.appendChild(div)
//如果菜单还有data,说明还有子菜单,则需要继续遍历添加
if( arr[i].data ){
addElement(arr[i].data,div)
}
}
}
let menu = document.querySelector('.menu')
//调用函数
addElement(arr,menu)
</script>
</body>
</html>
4.闭包(closure):
- 1.闭包是什么 : 闭包是一个可以访问其他函数内部变量的函数
- 或者理解为 闭包 = 函数 + 上下文引用 的组合
- 2.闭包的作用 : 解决变量污染
后期闭包出现较多的一般都会在回调函数里面
function fn(){
let a = 1
//在fn1函数中, 访问了其他函数fn内部的变量。 fn1 + a 形成闭包
function fn1(){
console.log(a)
}
fn1()
}
fn()
function test(){
let num = 1
setTimeout( function(){
console.log(num)
},2000)
}
test()
闭包场景:
<script>
/*
1.闭包(closure)是什么?
1.1 闭包是一个访问其他函数内部变量的函数
1.2 闭包 = 函数 + 上下文引用 的组合
总结:形成闭包要有两个条件,缺一不可。 (1)函数 (2)访问其他函数内部变量
2.闭包作用 : 变量污染
* 后期闭包出现较多的一般都会在回调函数里面
*/
//获取元素
let input = document.querySelector('input')
let button = document.querySelector('button')
button.onclick = function(){
//有一个变量存储搜索结果
let str = '6666'
//模拟网络请求:2秒钟之后获取搜索结果
setTimeout(function(){
alert(`搜索结束,本次搜索结果是${str}`)
},2000)
}
</script>
5.总结梳理:
5.1this三种指向
- 全局函数 :函数名( ) —————————>this指向window
- 对象方法 :对象名.方法名( ) ———-> this指向对象
- 构造函数 :new 函数名( )——————>this指向new创建的空对象
5.2call、apply、bind区别
- 相同点:都是修改函数this指向
- 不同点
- 传参方式不同: call用于单个参数,apply用于多个参数
- 执行机制不同: call与apply会立即执行, bind不会立即执行
- call、apply用一次修改一次
- bind;一次修改,终生有效
5.3闭包
- 什么是闭包:以下两种回答都可以
- 闭包是一个访问其他函数内部变量的函数
- 闭包是 函数 + 上下文代码组合
- 闭包作用:解决变量污染
5.4递归
- 什么是递归:函数内部调用自己
- 递归场景
- 深拷贝
- 遍历dom树
Var的关键字特点:(预解析/没有块级)
1.ES6新语法
1.1-变量声明let与const
重复定义 | 修改值 | 声明提升 | 块级作用域 | 循环 | |
---|---|---|---|---|---|
var | 可以 | 可以 | 会 | 严格来说不支持 | 可以 |
let | 不可以 | 可以 | 不会 | 支持 | 可以 |
const | 不可以 | 不可以(除数组和对象) | 不会 | 支持 | 不可以 |
1.没有预解析 : 变量必须要先声明,才能使用
2.有块级作用域 : if for 大括号里面声明的变量是局部变量
let 变量 : 可以修改变量中的数据
const 常量 : 只能在声明的时候赋值一次,不可修改
1.2-解构赋值语法
1.对象解构赋值
1.1 取出对象的值赋值给变量
ES6 解构赋值
let { name, age, sex } = obj
//1:取出 对象的属性 赋值 给变量
let obj = {
name: '张三',
age: 18,
sex: '男'
}
/* ES5 */
let name = obj.name;
let age = obj.age;
let sex = obj.sex;
console.log(name,age,sex);
/* ES6 */
/* a.这行代码本质:声明三个变量 name,age,sex。取出右边obj对象对应的属性名赋值给左边的变量*/
let {name,age,sex} = obj;
console.log(name,age,sex);
/* b.由于obj对象没有score属性,所以变量score的值为undefined。 */
let {name,score} = obj;
console.log(name,score);
/* c. sex:gender 的含义 : let gender = obj.sex */
let {name,sex:gender} = obj;
console.log(name,gender);
1.2 取出变量的值 赋值给对象的属性
ES6 解构赋值
let obj = { name, age, sex, }
//2:取出变量的值 赋值给对象的属性
let name = '李四';
let age = 20;
let sex = '男';
let sayHi = function(){
console.log('你好');
}
/* ES5 */
let person = {
name : name,
age : age,
gender : sex,
sayHi :sayHi
}
console.log(person);
/* ES6 */
let person = {
name,//等价于 name:name
age,
gender:sex,//如果属性名和变量名不一致,还是按照以前ES5的写法来赋值
sayHi,
play(){//等价于:play:function(){}
console.log('学习使我快乐');
}
};
console.log(person);
2.数组解构赋值
<script>
//数组解构赋值
let arr = [10, 20, 30]
/* ES5 */
// let n1 = arr[0];
// let n2 = arr[1];
// let n3 = arr[2];
// console.log(n1,n2,n3);
/* ES6 */
let [n1, n2, n3 = 50, n4 = 100] = arr
console.log(n1, n2, n3, n4) // 10 20 30 100
</script>
3.函数参数解构赋值
<script>
/*
1.学习目标 : 掌握函数参数的解构赋值
2.学习路线
(1)复习 函数 传参的本质是 实参给形参赋值的过程
(2)使用解构赋值语法 给函数传参
(3)使用解构赋值语法 设置 函数的默认参数
*/
/* (1) ES5 :函数传参 */
/**
* @description:
* @param {Object} obj:对象类型 {name:名字 age:年龄 sex:性别}
* @return:
*/
// function fn(obj){
// console.log(obj);
// //声明三个变量接收对象的参数值
// let name = obj.name;
// let age = obj.age;
// let sex = obj.sex;
// console.log(name,age,sex);
// };
// fn({
// name:'黑马李宗盛',
// age:32,
// sex:'男'
// });
/* (2) ES6 :函数传参 */
// function fn1({name,age,sex}){
// //声明三个变量接收对象的参数值
// console.log(name,age,sex);
// };
// fn1({
// name:'黑马李宗盛',
// age:32,
// sex:'男'
// });
//添加解构语法默认值
// function fn2({name = '班长',age = 38,sex:gender='女'}){
// //声明三个变量接收对象的参数值
// console.log(name,age,gender);
// };
// fn2({
// name:'黑马李宗盛',
// });
/* (3)函数默认参数 */
/* ES5:使用逻辑或短路运算实现 */
// function fn(n1,n2,n3){
// n1 = n1 || 10;
// n2 = n2 || 20;
// n3 = n3 || 30;
// console.log(n1,n2,n3);
// };
// fn(5,8);
// fn();
/* ES6:使用解构赋值语法默认值实现 */
function fn (n1 = 10, n2 = 20, n3 = 30) {
console.log(n1, n2, n3)
}
fn(5, 8)
fn()
</script>
4.小结:
1.1 取出对象的属性值 赋值给 变量
语法:let { name, age, sex } = 对象名
场景:如果函数参数是对象,就可以进行解构
1.2 取出变量的值 赋值给 对象的属性
let 对象名 = {name,age,sex}
场景:获取多个表单的数据,装入对象中
2.解构赋值的其他用法
2.1解构赋值的默认值
let {name,age = 18,sex} = obj //if(obj.age==undefined){age =18}else{age = obj.age}
2.2自定义解构变量名
let {sex:gender} = obj 相当于 let sex = obj.gender
1.3 取出数组的元素赋值给变量
let [n1,n2,n3,n4] = arr
1.4 取出变量的值 赋值给数组的元素
let arr = [n1,n2,n3,n4]
开发中,很多函数的参数是对象类型
function fn({ uname, password }) {
//函数参数进行解构赋值
console.log(uname, password);
}
fn({ uname: 'admin', password: '123456' })
1.3 箭头函数
1.箭头函数语法:
就是function简写 匿名函数才能转换为箭头函数
把function单词 替换成 箭头=>
把形参()移到箭头的左边
2.箭头函数其他用法:
如果只有一个形参,则可以省略形参小括号
a=>{return a2}
如果函数体只有一行,则可以省略大括号 此时需要省略return
a => a2
3.箭头函数的this指向:
- (1)
箭头函数
中没有this
: 这意味着 call() apply() bind()无法修改箭头函数中的this
- (2)箭头函数中的this指向 :
访问上一个作用域的this
- 说人话:函数在哪个作用域声明,this就是谁 (本质是通过作用域链访问上一个作用域中的this)
</script>
//1.匿名函数 //函数表达式
let fn = function () {
console.log(11111);
}
fn()
//2.箭头函数
let fn1 = () => {
console.log(2222);
}
fn1()
//一个形参 省略小括号 一行函数体 省略大括号和return
let fn2 = a => a * 2
console.log(fn2(3));
//多个形参
let fn3 = (d, w, c) => d + w + c
console.log(fn3(2, 3, 4))
//对象的解构 配合箭头函数
let { uname, pwd } = { uname: '张三', pwd: '123456' }
let fn4 = ({ uname, pwd }) => console.log({ uname, pwd });
fn4({ uname: '张三', pwd: '123456' })
</script>
1.4 展开运算符
展开运算符: …
作用:相当于遍历对象简写
作用场景:
1. 链接数组: arr1.push(…arr2)
2. 求数组最大值 :Math.max(…arr)
//1. 链接数组
let arr1 = [1, 3, 5, 7]
let arr2 = [2, 4, 6, 8, 'a']
arr1.push(...arr2)
console.log(arr1)//[1, 3, 5, 7, 2, 4, 6, 8, 'a']
//2.求数组最大值
let arr3 = [9, 3, 6, 8, 12]
let max = Math.max(...arr3)
console.log(max)
1.5数据类型set(集合)
- Set :集合。 ES6新增的数据类型,类似于ES5的数组
- Set与Array最大的区别 : Set不支持重复元素
- Set经典应用场景 : 数组去重
// 应用:数组去重
// 一行代码去重: let newArr = Array.from(new Set(数组))
//一行代码去重 : let newArr = [...new Set(数组)]
let arr = [12, 12, 34, 34, 42, 42]
//1.声明Set
let set = new Set(arr)
console.log(set); //{ 12, 34, 42 }
// 2.把set转成真数组
let newArr = Array.from(set)
console.log(newArr);// [12, 34, 42]
/* 一行代码实现数组去重 */
let newarr = Array.from(new Set(arr))
console.log(newarr);
</script>
2.数组迭代方法
2.1数组map遍历 : (映射数组)
- map场景 : 映射数组。 对数组每一个元素进行映射处理,得到一个全新数组
映射关系: 每一个元素 0.8
映射关系: 每一个元素 2 - map方法特点
(1)回调执行次数 === 数组长度
(2)本身返回值 : 隐射之后的新数组
(3)回调函数内部return
return 新数组元素值
如果不写return, 新数组每一个元素都变成undefined
let arr = [88,100,90,66,50]
//全场8折
let newArr = arr.map( (value,index)=>{
console.log(index,value)//下标 元素
return value*2
} )
console.log( newArr )
2.2数组filter遍历 : (筛选数组)
数组filter遍历 筛选数组找出数组中符合条件的元素 返回一个新数组
return true : 满足筛选条件,当前元素放入新数组中
return false : 不满足筛选条件,当前元素不放入新数组
参数是 : 回调函数(元素, 下标)=> { return 新元素 }
筛选已勾选的 求和
let arr = [20, 30, 40, 50, 60]
/*
let res = arr.filter(function (value, index) {
if (value % 3 == 0) {
return true
} else {
return false
}
})
console.log(res);//[30, 60]
*/
//ES6 简化后
let res1 = arr.filter(value => value % 3 == 0)
console.log(res1);//[30, 60]
2.2 filfer()应用场景
<script>
// 筛选4000-5000之间的产品
let arr1 = [
{
name: '苹果电脑',
price: 3000
},
{
name: '小米',
price: 4200
},
{
name: '平板',
price: 4800
},
{
name: '空调',
price: 2800
},
{
name: '电视机',
price: 3080
}
]
let newArr = arr1.filter(value => value.price >= 4000 && value.price <= 5000)
console.log(newArr);
</script>
2.3 数组forEach遍历 : (遍历数组)
1.forEach应用场景:用于遍历数组,相当于for循环另一种写法
2.注意点:
a. 回调函数执行次数 == 数组长度
数组中有多少个元素,回调函数就会执行几次
b. forEach函数没有返回值
c. 回调函数不需要return
就算手动return,也不会结束循环
let arr = [10, 55, 60, 88, 99, 70]
arr.forEach((value,index)=>{
console.log(index,value)
})
2.4 数组some遍历 : (判断是否存在满足条件的元素)
1.some应用场景:用于判断数组中是否存在满足条件的元素
2.注意点:
a. 回调函数执行次数 != 数组长度
some函数在遍历的时候可以中途结束
b. some函数返回一个布尔类型值
c. 回调函数返回布尔类型值用于结束遍历
return true; //遍历结束,且some函数返回值为true
(默认) return false; //遍历继续,且some函数返回值为false
//判断数组中是否存在小于0的元素
let arr = [10, 55, 60, 88, 99, 70]
let res = arr.some((value, index) => value < 0)
console.log(res)//false
2.5 数组every遍历(判断数组中是否所有元素都满足条件)
every应用场景:用于判断数组中是否所有元素都满足条件 (开关思想) (全选 反选)
every函数 返回一个布尔类型值
全部满足条件 返回true
有元素不满足条件 返回false
vue购物车案例 第2 - 5天
回调函数返回布尔类型值用于结束遍历
return true; //遍历继续,且every函数返回值为true
(默认)return false; //遍历结束,且every函数返回值为false
//需求:判断数组中没有负数
let arr = [23, 31, 60, -30, 90, 108, 260]
let res = arr.every(value => value > 0)
console.log(res)//false
2.6 数组findIndex方法 (获取符合条件的第一个元素(下标))
- 数组findIndex方法
作用: 获取符合条件的第一个元素位置(下标)
语法 : arr.findIndex((item, index) => { return true / false }) - 返回值:
符合则返回元素下标,
不符合则返回 - 1 - 应用场景 :
适用于数组中元素为对象类型,比传统for循环要高效
let arr = [
{ name: '张三', age: 20 },
{ name: '李四', age: 30 },
{ name: '王五', age: 25 },
{ name: '赵六', age: 33 },
{ name: '小七', age: 10 },
]
let res = arr.findIndex(value => value.name == '小七')
console.log(res);//符合则返回元素下标, 不符合则返回 - 1
2.7数组reduce 方法
- 作用 : 遍历数组元素,为每一个元素执行一次回调函数
- 语法 : arr.reduce( (sum,value)=>{ return sum + value } ,0)
- 返回值: 最终sum值
- 应用场景 : 数组求和/平均值/最大值/最小值
- 注意点:最好给一个初始值,避免空数组报错
//求和数组
let res = arr.reduce((sum, value, index) => sum + value, 0)//起始值加需要0 否则报错
console.log(res);
总结:推荐使用字面量方式声明数组,而不是 Array 构造函数
实例方法 forEach 用于遍历数组,替代 for 循环
实例方法 filter 过滤数组单元值,生成新数组
实例方法 map 迭代原数组,生成新数组
实例方法 concat 合并两个数组,生成新数组
实例方法 sort 对原数组单元值排序
实例方法 splice 删除或替换原数组单元
实例方法 indexOf 检索数组单元值 if(value.indexOf(‘垃圾’))判断存不存在 ( !=-1) 代表存在 -1就代表不存在
实例方法 reverse 反转数组
静态方法 from 伪数组转成数组
总结:推荐使用字面量方式声明字符串,而不是 String 构造函数
实例属性 length 用来获取字符串的度长
实例方法 split( ) 用来将 字符串 拆分 成数组
实例方法 join( ) 将 数组元素 拼接成 字符串
实例方法 toUpperCase( ) 用于将字母转换成大写
实例方法 toLowerCase( ) 用于将字母转换成小写
实例方法 slice( ) 用于字符串截取 可提取字符串的某个部分
实例方法 substr( ) 用于字符串截取
实例方法 indexOf( ) 检测是否包含某字符
实例方法 startsWith 检测是否以某字符开头
实例方法 endsWith 检测是否以某字符结尾
实例方法 replace 用于替换字符串,支持正则匹配
日期处理: Vue 第二天 和 nodeJS笔记里有
//1.原生的 new Date() 获取时间
let date = new Date();
console.log(newDate());//Sun Feb 13 2022 16:30:38 GMT+0800 (中国标准时间)
console.log(newDate().toLocaleDateString()); //2022/2/13
console.log(newDate().toLocaleTimeString()); //16:29:35
console.log(newDate().toLocaleString()); //2022/2/13 16:29:35
//2.使用moment.js 需要导入
//2.1 基本使用
let date1 = moment().format(‘YYYY-MM-DD’);//日期格式
console.log(date1);//2019-11-02
//2.2 如果moment传入了时间,就表示用传入的时间帮你生成对应的格式
let date2 = moment(‘2018/10/25 23:24:25’).format(‘YYYY年MM月DD日 HH时mm分ss秒’);
console.log(date2);//2018年10月25日 23时24分25秒
moment(接收的是data对象,不能是字符串).format(‘转成指定格式’)
作用域链:
当代码在一个环境中执行时,会创建变量对象的一个作用域链
由子级作用域返回父级作用域中寻找变量,就叫做作用域链