目录
一、作用域
- 作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问,
- 作用域分为:
1、局部作用域
局部作用域分为函数作用域和块作用域。
(1)函数作用域:
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
(2)块级作用域:
在 JavaScript 中使用 { } 包裹的代码称为代码块,代码块内部声明的变量外部将【有可能】无法被访问。
示例代码如下:
// for (var i = 1; i <= 3; i++) {
// // 块作用域
// console.log(i)
// }
// console.log(i)
// for (let i = 1; i <= 3; i++) {
// // 块作用域
// console.log(i)
// }
// for (let i = 1; i <= 3; i++) {
// // 块作用域
// console.log(i)
// }
if (true) {
let i = 10
}
console.log(i)
- let 声明的变量会产生块作用域,var 不会产生块作用域
- const 声明的常量也会产生块作用域
- 不同代码块之间的变量无法互相访问
- 推荐使用 let 或 const
2、全局作用域
<script>标签和.js文件的【最外层】就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。 全局作用域中声明的变量,任何其它作用域都可以被访问
注意:
- 为 window 对象动态添加的属性默认也是全局的,不推荐!
- 函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
- 尽可能少的声明全局变量,防止全局变量被污染
3、作用域链
作用域链本质上是底层的变量查找机制。
- 在函数被执行时,会优先查找当前函数作用域中查找变量
- 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
总结:
- 嵌套关系的作用域串联起来形成了作用域链
- 相同作用域链中按着从小到大的规则查找变量
- 子作用域能够访问父作用域,父级作用域无法访问子级作用域
4、JS垃圾回收机制
(1)什么是垃圾回收机制?
- 简称 GC
- JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾 回收器自动回收
(2)什么是内存泄漏?
- 不再用到的内存,没有及时释放,就叫做内存泄漏
(3)内存的生命周期是什么样的?
- 内存分配、内存使用、内存回收
- 全局变量一般不会回收; 一般情况下局部变量的值, 不用了, 会被自动 回收掉
示例代码如下:
for (let i = 1; i <= 3; i++) {
}
let num = 10
function fn() {
const str = 'andy'
// str = 'lily'
console.log(str)
}
fn()
fn()
fn()
5、 闭包
(1)概念:
一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
简单理解:闭包 = 内层函数 + 外层函数的变量
(2)闭包作用:
封闭数据,提供操作,外部也可以访问函数内部的变量
(3)闭包的基本格式:
// 简单的写法
function outer() {
let a = 10
function fn() {
console.log(a)
}
fn()
}
outer()
// 常见的闭包的形式 外部可以访问使用 函数内部的变量
function outer() {
let a = 100
function fn() {
console.log(a)
}
return fn
}
(4)闭包应用:
实现数据的私有,比如,我们要做个统计函数调用次数,函数调用一次,就++
示例代码如下:
// // 常见的写法2
// function outer() {
// let a = 100
// return function () {
// console.log(a)
// }
// }
// const fun = outer()
// fun() // 调用函数
// 外面要使用这个 10
// 闭包的应用
// 普通形式 统计函数调用的次数
// let i = 0
// function fn() {
// i++
// console.log(`函数被调用了${i}次`)
// }
// 因为 i 是全局变量,容易被修改
// 闭包形式 统计函数调用的次数
function count() {
let i = 0
function fn() {
i++
console.log(`函数被调用了${i}次`)
}
return fn
}
const fun = count()
6、变量提升
变量提升是 JavaScript 中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)
注意:
- 变量在未声明即被访问时会报语法错误
- 变量在var声明之前即被访问,变量的值为 undefined
- let/const 声明的变量不存在变量提升
- 变量提升出现在相同作用域当中
- 实际开发中推荐先声明再访问变量
示例代码如下:
// 1. 把所有var声明的变量提升到 当前作用域的最前面
// 2. 只提升声明, 不提升赋值
// var num
// console.log(num + '件')
// num = 10
// console.log(num)
function fn() {
console.log(num)
var num = 10
}
fn()
二、函数进阶
1、函数提升
函数提升与变量提升比较类似,是指函数在声明之前即可被调用。
总结:
- 函数提升能够使函数的声明调用更灵活
- 函数表达式不存在提升的现象
- 函数提升出现在相同作用域当中
示例代码如下:
var fun
// 1. 会把所有函数声明提升到当前作用域的最前面
// 2. 只提升函数声明,不提升函数调用
// fn()
// function fn() {
// console.log('函数提升')
// }
// fun()
// var fun = function () {
// console.log('函数表达式')
// }
// 函数表达式 必须先声明和赋值, 后调用 否则 报错
2、函数参数
(1)动态参数
arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参
总结:
- arguments 是一个伪数组,只存在于函数中
- arguments 的作用是动态获取函数的实参
- 可以通过for循环依次得到传递过来的实参
示例代码如下:
function getSum() {
// arguments 动态参数 只存在于 函数里面
// 是伪数组 里面存储的是传递过来的实参
// console.log(arguments) [2,3,4]
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, 2, 2, 3, 4)
(2)剩余参数
剩余参数允许我们将一个不定数量的参数表示为一个数组
- ... 是语法符号,置于最末函数形参之前,用于获取多余的实参
- 借助 ... 获取的剩余实参,是个真数组
示例代码如下:
function getSum(a, b, ...arr) {
console.log(arr) // 使用的时候不需要写 ...
}
getSum(2, 3)
getSum(1, 2, 3, 4, 5)
开发中,还是提倡多使用 剩余参数
(3)展开运算符
展开运算符(…),将一个数组进行展开
典型运用场景: 求数组最大值(最小值)、合并数组等
展开运算符 or 剩余参数
剩余参数:函数参数使用,得到真数组
展开运算符:数组中使用,数组展开
示例代码如下:
const arr1 = [1, 2, 3]
// 展开运算符 可以展开数组
// console.log(...arr)
// console.log(Math.max(1, 2, 3))
// ...arr1 === 1,2,3
// 1 求数组最大值
console.log(Math.max(...arr1)) // 3
console.log(Math.min(...arr1)) // 1
// 2. 合并数组
const arr2 = [3, 4, 5]
const arr = [...arr1, ...arr2]
console.log(arr)
3、 箭头函数
使用场景:箭头函数更适用于那些本来需要匿名函数的地方
(1)基本语法
示例代码如下:
const fn = function () {
console.log(123)
}
// 1. 箭头函数 基本语法
const fn = () => {
console.log(123)
}
fn()
const fn = (x) => {
console.log(x)
}
fn(1)
// 2. 只有一个形参的时候,可以省略小括号
const fn = x => {
console.log(x)
}
fn(1)
// 3. 只有一行代码的时候,我们可以省略大括号
const fn = x => console.log(x)
fn(1)
// 4. 只有一行代码的时候,可以省略return
const fn = x => x + x
console.log(fn(1))
// 5. 箭头函数可以直接返回一个对象
const fn = (uname) => ({ uname: uname })
console.log(fn('刘德华'))
(2)箭头函数参数
- 普通函数有arguments 动态参数
- 箭头函数没有 arguments 动态参数,但是有 剩余参数 ..args
示例代码如下:
// 1. 利用箭头函数来求和
const getSum = (...arr) => {
let sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
return sum
}
const result = getSum(2, 3, 4)
console.log(result) // 9
(3)箭头函数this
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。
在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window,因此 DOM事件回调函数为了简便,还是不太推荐使用箭头函数
示例代码如下:
// 以前this的指向: 谁调用的这个函数,this 就指向谁
// console.log(this) // window
// // 普通函数
// function fn() {
// console.log(this) // window
// }
// window.fn()
// // 对象方法里面的this
// const obj = {
// name: 'andy',
// sayHi: function () {
// console.log(this) // obj
// }
// }
// obj.sayHi()
// 2. 箭头函数的this 是上一层作用域的this 指向
// const fn = () => {
// console.log(this) // window
// }
// fn()
// 对象方法箭头函数 this
// const obj = {
// uname: 'pink老师',
// sayHi: () => {
// console.log(this) // this 指向谁? window
// }
// }
// obj.sayHi()
const obj = {
uname: 'pink老师',
sayHi: function () {
console.log(this) // obj
let i = 10
const count = () => {
console.log(this) // obj
}
count()
}
}
obj.sayHi()
三、解构赋值
1、数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。
(1)基本语法:
- 赋值运算符 = 左侧的 [] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
- 变量的顺序对应数组单元值的位置依次进行赋值操作
- 典型应用交互2个变量
注意: js前面必须加分号情况
示例代码如下:
// const arr = [100, 60, 80]
// 数组解构 赋值
// // const [max, min, avg] = arr
const [max, min, avg] = [100, 60, 80]
// // const max = arr[0]
// // const min = arr[1]
// // const avg = arr[2]
console.log(max) // 100
console.log(avg) // 80
// 交换2个变量的值
let a = 1
let b = 2;
[b, a] = [a, b]
console.log(a, b)
// 1. 立即执行函数要加
// (function () { })();
// (function () { })();
// 2. 使用数组的时候
// const arr = [1, 2, 3]
const str = 'pink';
[1, 2, 3].map(function (item) {
console.log(item)
})
let a = 1
let b = 2
;[b, a] = [a, b]
console.log(a, b)
- 变量多,单元值少
- 变量少,单元值多
- 利用剩余参数解决变量少,单元值多
- 防止有undefined传递单元值的情况,可以设置默认值:
- 按需导入,忽略某些返回值:
- 支持多维数组的结构:
示例代码如下:
<script>
// const pc = ['海尔', '联想', '小米', '方正'];
// [hr, lx, mi, fz] = pc
// console.log(hr, lx, mi, fz);
// function getValue() {
// return [100, 60]
// }
// [max, min] = getValue()
// console.log(max, min);
// const pc = ['海尔', '联想', '小米', '方正']
// const [hr, lx, mi, fz] = ['海尔', '联想', '小米', '方正']
// console.log(hr)
// console.log(lx)
// console.log(mi)
// console.log(fz)
// // 请将最大值和最小值函数返回值解构 max 和min 两个变量
// function getValue() {
// return [100, 60]
// }
// const [max, min] = getValue()
// console.log(max)
// console.log(min)
// 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] = [1, 2]
// const [a = 0, b = 0] = []
// console.log(a) // 1
// console.log(b) // 2
// 5. 按需导入赋值
// const [a, b, , d] = [1, 2, 3, 4]
// console.log(a) // 1
// console.log(b) // 2
// console.log(d) // 4
// 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]
const [a, b, [c, d]] = [1, 2, [3, 4]]
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3
console.log(d) // 4
</script>
2、 对象解构
(1)基本语法:
- 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
- 对象属性的值将被赋值给与属性名相同的变量
- 注意解构的变量名不要和外面的变量名冲突否则报错
- 对象中找不到与变量名一致的属性时变量值为 undefined
示例代码如下:
// 对象解构
// const obj = {
// uname: 'pink老师',
// age: 18
// }
// obj.uname
// obj.age
// const uname = 'red老师'
// 解构的语法
// const { uname, age } = {age: 18, uname: 'pink老师' }
// // 等价于 const uname = obj.uname
// // 要求属性名和变量名必须一直才可以
// console.log(uname)
// console.log(age)
(2)给新的变量名赋值:
可以从一个对象中提取变量并同时修改新的变量名
示例代码如下:
// 1. 对象解构的变量名 可以重新改名 旧变量名: 新变量名
// const { uname: username, age } = { uname: 'pink老师', age: 18 }
// console.log(username)
// console.log(age)
冒号表示“什么值:赋值给谁”
(3)数组对象解构:
对象解构赋值案例:
示例代码如下:
// 2. 解构数组对象
const pig = [
{
uname: '佩奇',
age: 6
}
]
const [{ uname, age }] = pig
console.log(uname)
console.log(age)
(4)多级对象解构
示例代码如下:
<script>
// 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)
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)
</script>
<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 } = msg
// console.log(data)
// 需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给 函数
// const { data } = msg
// msg 虽然很多属性,但是我们利用解构只要 data值
function render({ data }) {
// const { data } = arr
// 我们只要 data 数据
// 内部处理
console.log(data)
}
render(msg)
// 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
function render({ data: myData }) {
// 要求将 获取过来的 data数据 更名为 myData
// 内部处理
console.log(myData)
}
render(msg)
</script>
(5)补充一个遍历数组方法(forEach)
- forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数
示例代码如下:
// forEach 就是遍历 加强版的for循环 适合于遍历数组对象
const arr = ['red', 'green', 'pink']
const result = arr.forEach(function (item, index) {
console.log(item) // 数组元素 red green pink
console.log(index) // 索引号
})
// console.log(result)
注意:
- forEach 主要是遍历数组
- 参数当前数组元素是必须要写的, 索引号可选