javascript<——>进阶
一、作用域:变量可以被访问的范围
1.局部作用域
1.1函数作用域
在函数内部声明的变量,在函数内部被访问的,外部无法直接访问。
总结:1、函数内部声明的变量,在函数外部无法直接访问
2、函数的参数也是函数内部的局部变量
3、不同函数内部声明的变量无法互相访问
4、函数执行完毕后,函数内部的变量事件被清空
1.2块作用域
有“{}”的包裹的代码称为代码块,内部声明的变量外部将【有可能】无法被访问
###let /const声明的变量产生块作用域,var声明的不会产生块级作用域
2.全局作用域
写在<script>标签的或者写在js文件的,尽可能少声明全局作用域,防止全局变量被污染
3.作用域链
本质是底层的变量查找机制:(1)函数执行时,优先查找当前函数作用域中查找
(2)查不到则依次逐级查找父级作用域直到全局作用域
###子作用域可以访问父作用域,但是父作用域无法访问子作用域
###相同作用域链按从小到大规则查找变量
4.js垃圾回收机制(GC)
4.1内存生命周期
(1)分配内存:声明变量、函数、对象时,系统自动分配内存
(2)内存使用:读写内存,使用函数,变量
(3)内存回收:使用完毕,垃圾回收机制自动回收不在使用的内存
###全局变量一般不会回收(关闭页面回收),
(4)内存泄漏:程序中分配的内存由于某种原因,程序无法释放或者未释放。
❤️算法说明:
1.栈(操作系统):由操作系统自动分配释放函数的参数值,局部变量等,基本数据类型放到栈里面
2.堆(操作系统):一般由程序员分配释放,不释放,则垃圾回收机制回收。复杂数据类型放到堆里面
4.2垃圾回收算法
(1)引用计数法
跟踪记录被引用的次数,引用一次则记录一次,多次引用会累加++,减少一次引用就--
次数为0,回收
缺点:嵌套使用。两个对象相互引用,尽管它们不再使用,GC不会进行回收,内存泄漏
(2)标记清除法
核心:1.将“不再使用的对象”定义为“无法达到的对象”。
2.从根部出发定时扫描内存中的对象。凡是从根部到达的对象,都是还需要使用的。
3.无法触及的对象被标记为不再使用,稍后进行回收。
5.闭包
一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数作用域,会产生内存泄漏
闭包=内层函数+外层函数的变量
<script>
function outer(){let a = 1function fn(){console.log(a)}return fn
}
const fun = outer()
fun()
</script>
//外层函数可以访问内层函数的变量
//const fun = outer() = fn = function fn(){}
//调用fun()
作用:闭包的应用:实现数据的私有,封闭数据。做个统计函数调用次数,调用一次就++
外部可以访问函数内部的变量
问题:内存泄漏
<script>function count(){let i = 0function fn(){i++console.log(`函数被调用了${i}次`) }return fn
}
const fun = count()</script>
6.变量提升
把var声明的变量提升到 当前作用域的最前面。
只提升声明,不提升赋值
总结:
-
变量在未声明即被访问时会报语法错误
-
变量在声明之前即被访问,变量的值为
undefined
-
let
声明的变量不存在变量提升,推荐使用let
-
变量提升出现在相同作用域当中
-
实际开发中推荐先声明再访问变量
<script>
function(){console.log(num)var num = 10
}
fun()等价于
function(){var numconsole.log(num)num = 10
}
fun()
</script>
二、函数进阶
1.函数提升(只提升声明,不提升调用)
声明之前调用
函数表达式 必须先声明和赋值,后调用,否则报错。
总结:1、函数提升能够使函数的声明调用更灵活
2、函数表达式不存在提升的现象
3、函数提升出现在相同作用域当中
2.函数参数
###默认值
总结:1、声明函数时为形参赋值即为参数的默认值
2、如果参数未自定义默认值时,参数的默认值为undefined
3、调用函数时没有差引入对应实参时,参数的默认值被当做实参传入
2.1动态参数
arguments是一个伪数组 只存在于 函数里面
作用是动态的获取函数的实参
可以通过for循环依次得到传递过来的参数
2.2剩余参数
(1)...是语法符号,置于最末函数形参之前,用于获取多余的实参
(2)借助...获取的剩余参数,是个真数组
<script>function getSum(a,b,...arr){console.log(arr);}getSum(2,3,4,5,6,7)getSum(1,2,3,4,5,6,7,8,9)</script>
##使用场景:用于获取多余的实参
##和动态参数的区别是什么?
动态参数是伪数组;
剩余参数是真数组
❤️展开运算符【数组中使用】:将数组的数字全展开
const arr = [1,2,3,4,5]console.log(...arr)console.log(Math.max(...arr));//求最大值console.log(Math.min(...arr));//求最小值
合并数组:
const arr1 = [1,2,3]const arr2 = [4,5,6]const arr3 = [...arr1,...arr2]console.log(arr3);// [1,2,3,4,5,6]
3.箭头函数
(1)基本语法
const fn=()=>{
}
##只有一个形参时,可以省略小括号
const fn = x=>{
}
##只有一行代码时,可以省略大括号和return
const fn = (x,y) =>x+y
console.log(fn(1,2))
##箭头函数可以直接返回同一个对象
const fn1 = uname=>({uname:name})
console.log('刘德华')
总结:
1、箭头函数属于表达式函数,因此不存在函数提升
2、箭头函数只有一个参数时可以省略圆括号()
3、箭头函数函数体只有一行时代码可以省略花括号{},并自动作为返回值被返回
(2)箭头函数参数
只有剩余参数,没有动态参数arguments
(3)箭头函数this
箭头函数不会创建自己的this,从自己的作用域链的上一层沿用this
三、解构赋值
1.数组解构 :
将数组的单元值快速批量赋值给一系列变量的简介语法
<script>// 以下代码是否会正常执行,如果不会,如何改正const [min, avg, max] = [100,200,300];//必须加分号(function() {console.log(min);})();</script>
<script>const arr = [60,100,80]const [max,min avg] = arr
//将按照顺序赋值给变量console.log(max)
</script>
交换变量
<script>
let a = 1
let b = 2;
const [b,a]=[a,b]
console.log(a,b)
</script>
<script>1. 变量多, 单元值少 , undefinedconst [a, b, c, d] = [1, 2, 3]console.log(a) // 1console.log(b) // 2console.log(c) // 3console.log(d) // undefined2. 变量少, 单元值多const [a, b] = [1, 2, 3]console.log(a) // 1console.log(b) // 23. 剩余参数 变量少, 单元值多const [a, b, ...c] = [1, 2, 3, 4]console.log(a) // 1console.log(b) // 2console.log(c) // [3, 4] 真数组4. 防止 undefined 传递const [a = 0, b = 0] = [1, 2]const [a = 0, b = 0] = []console.log(a) // 1console.log(b) // 25. 按需导入赋值const [a, b, , d] = [1, 2, 3, 4]console.log(a) // 1console.log(b) // 2console.log(d) // 4const arr = [1, 2, [3, 4]]console.log(arr[0]) // 1console.log(arr[1]) // 2console.log(arr[2]) // [3,4]console.log(arr[2][0]) // 3// 多维数组解构const [a, b, [c, d]] = [1, 2, [3, 4]]console.log(a) // 1console.log(b) // 2console.log(c) // 3console.log(d) // 4</script>
2.对象解构
<script>// 对象解构const obj = {uname: 'pink老师',age: 18}obj.unameobj.age //const uname = 'red老师'解构的语法const { uname, age } = {age: 18, uname: 'pink老师' }// 等价于 const uname = obj.uname//要求属性名和变量名必须一致才可以
</script>
3.多级对象解构
<script>const pig = {name: '佩奇',family: {mother: '猪妈妈',father: '猪爸爸',sister: '乔治'},age: 6}// // 多级对象解构const { name, family: { mother, father, sister } } = pig
</script>
4.多级数组对象解构
<script>//多级对象数组解构const person = [{name: '佩奇',family: {mother: '猪妈妈',father: '猪爸爸',sister: '乔治'},age: 6}]const [{ name, family: { mother, father, sister } }] = personconsole.log(name)console.log(mother)console.log(father)console.log(sister)</script>
5.多级对象解构案例
<script>// 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} = msgconsole.log(data) // 需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给 函数function render({data}){console.log(data)}render(msg)// 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myDatafunction render({data:myData}){console.log(myData)}
</script>
###forEach()方法
用于调用数组的每个元素,并将元素传递给回调函数
注意:
1、forEach主要是遍历数组
2、参数当前数组元素使必须要写的,索引号可选。
<script>// forEach 就是遍历 加强版的for循环 适合于遍历数组对象const arr = ['red', 'green', 'pink']const result = arr.forEach(function (item, index) {console.log(item) // 数组元素 red green pinkconsole.log(index) // 索引号})// console.log(result)</script>
###筛选数组filter方法
filter()方法创建的一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
使用场景:筛选数组符合条件的元素,并返回筛选之后元素的新数组
语法:
<script>const arr = [10, 20, 30]// const newArr = arr.filter(function (item, index) {// // console.log(item)// // console.log(index)// return item >= 20// })// 返回的符合条件的新数组const newArr = arr.filter(item => item >= 20)console.log(newArr)</script>
###渲染商品案例
<script>// 2. 初始化数据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',},]function render(arr) {let str = ''arr.forEach(item => {const { name, price, picture } = itemstr += `<div class="item"><img src="${picture}" alt=""><p class="name">${name}</p><p class="price">${price}</p></div>`});document.querySelector('.list').innerHTML = str}render(goodsList)document.querySelector('.filter').addEventListener('click', e => {const { tagName, dataset } = e.targetif (tagName == 'A') {//console.log(11);let arr = goodsListif (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)}render(arr)}})</script>
四、构造函数与数据常用函数
1.深入对象
1.1创建对象三种方式
(1)利用对象字面量创建对象
const 0 = {
name : 'abc'
}
(2)利用new Object创建对象
const 0 = new Object({name:'abc'})
(3)利用构造函数创建对象
1.2构造函数
一种特殊的函数,主要用来初始化对象。
##使用new关键字的调用函数行为被称为实例化
##实例化构造函数时没有参数可以省略
##构造函数内部无需写return,返回值即新创建的对象
##构造函数内部的return返回值无效,所以不要写return
##new Object()new Date()也是实例化构造函数
两个约定:
<1>命名大写字母开头
<2>它们只能由“new”操作符来执行
<script>function Pig(uname,age){this.uname = unamethis.age = age
}const p = new Pig('peiqi',6)const q = new Pig('qiaozhi',3)console.log(p,q)
</script>
❤️实例化执行过程
(1)创建新对象
(2)构造函数this指向新对象
(3)执行函数构造代码,修改this,添加新属性
(4)返回新的对象
1.3实例成员和静态成员
1.3.1实例成员
通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实例方法)
1.3.2静态成员
构造函数的属性和方法被称为静态成员
<script>function Pig(name){this.name = name
}
Pig.eyes = 2//静态属性
Pig.sayHi = function(){//静态方法console.log(this)
}
Pig.sayHi()
console.log(Pig.eyes)//2
</script>
说明:
1.静态成员只能构造函数来访问
2.静态方法中的this指向构造函数
2.内置构造函数
1.Object
.keys(obj):返回所有属性名
.values(obj):获得所有的属性值【返回的是数组】
const o = { uname: 'pink', age: 18 }// 1.获得所有的属性名console.log(Object.keys(o)) //返回数组['uname', 'age']// 2. 获得所有的属性值console.log(Object.values(o)) // ['pink', 18]
.assign(new,old):对象拷贝
// 3. 对象的拷贝const o = { uname: 'pink', age: 18 }// const oo = {}// Object.assign(oo, o)// console.log(oo)//对象中添加属性Object.assign(o, { gender: '女' })console.log(o)
2.Array
方法 | 作用 | 说明 |
filter | 过滤数组 | 返回新数组,返回的是筛选满足条件的数组与数组元素 |
forEach | 遍历数组 | 不返回数组,经常用于查找遍历数组元素 |
map | 迭代数组 | 返回新数组,返回的是处理后的数组元素,想要使用返回的新数组 |
reduce | 累计器 | 返回累计处理的结果,经常用于求和等 |
join | 拼接 | 数组元素拼接成字符串,返回字符串 |
find | 查找元素 | 返回符合测试条件的第一个数组,如果没有符合条件的则返回undefined |
every | 检测数组所有元素是否都符合指定条件 | 如果都符合,返回true,否则返回false |
sort | 排序 | 对原数组单元值进行排序 |
from | 伪数组转换为真数组 | 返回真数组 |
<script>const arr = [1,3,5,7,9]const result = arr.reduce((a,b)=>(a+b),10)console.log(result)//35
</script>
(1)reduce(function(prev,curret){return prev+curret},start)
prev:代表初始值
curret:代表累加的值
start:初始值具体数字
###
1.如果没有初始值,则上一次的值以数组的第一个数组元素的值。
2.每一次循环,把返回值给作为 下一次循环的上一次值。
3.如果有初始值,则起始值作为上一次值。
<script>const arr = [{name: '张三',salary: 10000}, {name: '李四',salary: 10000}, {name: '王五',salary: 20000},]const money = arr.reduce((prev, item) => prev + item.salary * 1.3, 0)console.log(money)</script>
包装类型:
字符串、数值、布尔类型数据是js底层使用Object构造函数“包装”来的,即为包装类型
3.String
.split【‘分隔符’】:把字符串转换成数组(和join相反)
.substring【第一个索引,最后一个索引】截取字符串
.startsWith(检测字符串,【检测位置的索引号】),检测是否以某字符开头
.includes(搜索的字符串,检测的字符串位置索号),判断一的字符串是否包含在另一个字符串中,根据情况返回true或false
.replace(代替字符串)
4.Number
.toFixed:设置保留小数位的长度,四舍五入。
3.综合案例
五、深入面向对象
1.编程思想
(1)面向过程
分析解决问题的步骤,然后用函数一步一步地实现,使用的时候调用
优点:性能高
缺点:没有面向易维护,复用,扩展
(2)面向对象(oop)
优点:易维护、复用、扩展,使系统更加易于维护,更加灵活
缺点:性能低
分解为一个个对象,然后对象之间分工和合作。
###特性:
【1】封装性:封装代码,js面向对象可以通过构造函数实现的封装,同时将变量和函数组合到一起并能通过this实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间互相不影响。
总结:
1、构造函数体现了面向对象的封装特性
2、构造函数实例创建的对象彼此独立,互不影响。
【2】继承性:继承接口
【3】多态性
2.构造函数
存在浪费内存的问题
3.原型对象
构造函数通过原型分配的函数是所有对象所共享的
###js规定,每一个构造函数都有一个prototype属性,指向另一个对象,所以我们也称为原型对象
###这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存
###我们可以把那些不变的方法,直接定义为prototype对象上,这样所有对象的实例就可以共享这些方法
###构造函数和原型对象中的this都指向实例化的对象
原型作用:
(1)共享方法
(2)可以把那些不变的方法,直接定义在prototype对象上
构造函数和原型里面的this指向谁?
指向实例化对象
constructor 属性
在哪里? 每个原型对象里面都有个constructor 属性(constructor 构造函数)
作用:该属性指向该原型对象的构造函数, 简单理解,就是指向我的爸爸,我是有爸爸的孩子
使用场景:
如果有多个对象的方法,我们可以给原型对象采取对象形式赋值.
但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了
此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。
对象原型
构造函数创建实例对象和含有原型对象(prototype),实例对象的__proto__指向原型对象,实例对象的__protype__和原型对象的constructor指向构造函数。
对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype
原型对象的属性和方法,就是因为对象有__proto__ 原型的存在。
注意:
-
__proto__是JS非标准属性
-
[[prototype]]和__proto__意义相同
-
用来表明当前实例对象指向哪个原型对象prototype
-
__proto__对象原型里面也有一个 constructor属性,指向创建该实例对象的构造函数
原型继承
继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript 中大多是借助原型对象实现继承
的特性。
龙生龙、凤生凤、老鼠的儿子会打洞描述的正是继承的含义。
<body><script>// 继续抽取 公共的部分放到原型上// const Person1 = {// eyes: 2,// head: 1// }// const Person2 = {// eyes: 2,// head: 1// }// 构造函数 new 出来的对象 结构一样,但是对象不一样function Person() {this.eyes = 2this.head = 1}// console.log(new Person)// 女人 构造函数 继承 想要 继承 Personfunction Woman() {}// Woman 通过原型来继承 Person// 父构造函数(父类) 子构造函数(子类)// 子类的原型 = new 父类 Woman.prototype = new Person() // {eyes: 2, head: 1} // 指回原来的构造函数Woman.prototype.constructor = Woman// 给女人添加一个方法 生孩子Woman.prototype.baby = function () {console.log('宝贝')}const red = new Woman()console.log(red)// console.log(Woman.prototype)// 男人 构造函数 继承 想要 继承 Personfunction Man() {}// 通过 原型继承 PersonMan.prototype = new Person()Man.prototype.constructor = Manconst pink = new Man()console.log(pink)</script>
</body>
原型链(查找规则)
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链
<body><script>// function Objetc() {}console.log(Object.prototype)console.log(Object.prototype.__proto__)function Person() {}const ldh = new Person()// console.log(ldh.__proto__ === Person.prototype)// console.log(Person.prototype.__proto__ === Object.prototype)console.log(ldh instanceof Person)console.log(ldh instanceof Object)console.log(ldh instanceof Array)console.log([1, 2, 3] instanceof Array)console.log(Array instanceof Object)</script>
</body>
① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
② 如果没有就查找它的原型(也就是__ proto__指向的 prototype 原型对象)
③ 如果还没有就查找原型对象的原型(Object的原型对象)
④ 依此类推一直找到 Object 为止(null)
⑤ __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
⑥ 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
六、深浅拷贝
只针对引用数据类型
1.浅拷贝
拷贝的是地址
常见方法:
(1)拷贝对象:Object.assgin()/展开运算符{…obj}拷贝对象
(2)拷贝数组:Array/ptotype.concat()或者{…arr}
2.深拷贝
拷贝的是对象,不是地址
2.1递归实现深拷贝
1.深拷贝实现拷贝出来的新对象不会影响旧对象 ,通过函数递归实现
2.普通拷贝的话直接赋值就可以,遇到数组再次调用这个递归函数
3.如果遇到对象,就调用递归函数解决对象,【先array,后对象】
<body><script>const obj = {uname: 'pink',age: 18,hobby: ['乒乓球', '足球'],family: {baby: '小pink'}}const o = {}// 拷贝函数function deepCopy(newObj, oldObj) {debuggerfor (let k in oldObj) {// 处理数组的问题 一定先写数组 在写 对象 不能颠倒if (oldObj[k] instanceof Array) {newObj[k] = []// newObj[k] 接收 [] hobby// oldObj[k] ['乒乓球', '足球']deepCopy(newObj[k], oldObj[k])} else if (oldObj[k] instanceof Object) {newObj[k] = {}deepCopy(newObj[k], oldObj[k])}else {// k 属性名 uname age oldObj[k] 属性值 18// newObj[k] === o.uname 给新对象添加属性newObj[k] = oldObj[k]}}}deepCopy(o, obj) // 函数调用 两个参数 o 新对象 obj 旧对象console.log(o)o.age = 20o.hobby[0] = '篮球'o.family.baby = '老pink'console.log(obj)console.log([1, 23] instanceof Object)// 复习// const obj = {// uname: 'pink',// age: 18,// hobby: ['乒乓球', '足球']// }// function deepCopy({ }, oldObj) {// // k 属性名 oldObj[k] 属性值// for (let k in oldObj) {// // 处理数组的问题 k 变量// newObj[k] = oldObj[k]// // o.uname = 'pink'// // newObj.k = 'pink'// }// }</script>
</body>
2.2js库lodash里面cloneDeep
<body><!-- 先引用 --><script src="./lodash.min.js"></script><script>const obj = {uname: 'pink',age: 18,hobby: ['乒乓球', '足球'],family: {baby: '小pink'}}const o = _.cloneDeep(obj)console.log(o)o.family.baby = '老pink'console.log(obj)</script>
</body>
2.3JSON序列化
<body><script>const obj = {uname: 'pink',age: 18,hobby: ['乒乓球', '足球'],family: {baby: '小pink'}}// 把对象转换为 JSON 字符串// console.log(JSON.stringify(obj))const o = JSON.parse(JSON.stringify(obj))console.log(o)o.family.baby = '123'console.log(obj)</script>
</body>
七、异常处理
异常处理指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行
throw抛异常
1、throw抛出异常信息后,程序也会终止执行
2、throw后面跟的是错误提示信息
3、Error对象配合throw使用,能够设置更详细的错误信息
<script>function counter(x, y) {if(!x || !y) {// throw '参数不能为空!';throw new Error('参数不能为空!')}return x + y}counter()
</script>
try/catch捕获错误信息
-
try...catch
用于捕获错误信息 -
将预估可能发生错误的代码写在
try
代码段中 -
如果
try
代码段中出现错误后,会执行catch
代码段,并截获到错误信息
<script>function foo() {try {// 查找 DOM 节点const p = document.querySelector('.p')p.style.color = 'red'} catch (error) {// try 代码段中执行有错误时,会执行 catch 代码段// 查看错误信息console.log(error.message)// 终止代码继续执行return}finally {//不管代码报不报错误,仍然执行该行代码alert('执行')}console.log('如果出现错误,我的语句不会执行')}foo()
</script>
debugger
八、处理this
1.普通函数this指向
谁调用就指向谁
严格模式下指向undefined
2.箭头函数this指向
箭头函数中的 this
与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this
!箭头函数中访问的 this
不过是箭头函数所在作用域的 this
变量。
总结:1.函数内部不存在this,沿用上一级的this
2.不适用:构造函数、原型函数、dom事件函数等
3.适用:需要使用上层的this的地方
4.如果正确的话,它会在很多地方带来方便。
3.改变this指向
(1)call
<script>// 普通函数function sayHi() {console.log(this);}let user = {name: '小明',age: 18}let student = {name: '小红',age: 16}// 调用函数并指定 this 的值sayHi.call(user); // this 值为 usersayHi.call(student); // this 值为 student// 求和函数function counter(x, y) {return x + y;}// 调用 counter 函数,并传入参数let result = counter.call(null, 5, 10);console.log(result);
</script>
总结:
-
call方法能够在调用函数的同时指定
this
的值 -
使用
call
方法调用函数时,第1个参数为this
指定的值 -
call
方法的其余参数会依次自动传入函数做为函数的参数
(2)apply
总结:
-
apply
方法能够在调用函数的同时指定this
的值 -
使用
apply
方法调用函数时,第1个参数为this
指定的值 -
apply
方法第2个参数为数组,数组的单元值依次自动传入函数做为函数的参数
call和apply的区别是?
都是调用函数,都能够改变this指向。但它们的参数不一样,apply传递的必须是数组
(3)bind
不会调用函数,但是能改变this的指向
返回值是个函数,但是这个函数里面的this是更改过的obj
主要应用场景:
call调用函数并且可以传递参数
apply经常跟数组有关系,比如借助数学对象实现求最大值和最小值
bind不调用函数,但是还想改变this指向,比如改变定时器内部的this指向
九、防抖节流
防抖(debounce):
单位时间内,频繁触发事件,只执行最后一次
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间
使用场景:输入手机号,邮箱验证输入检测
节流(throttle):
单位时间内,频繁出发事件,只执行一次
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数
使用场景:鼠标移动mousemove,页面尺寸缩放resize、滚动条滚动scroll