规定了变量能够被访问的范围,离开了这个范围变量将不能被访问
局部作用域分为函数作用域和块作用域。
1. 函数内部声明的变量,在函数外部无法被访问
2. 函数的参数也是函数内部的局部变量
3. 不同函数内部声明的变量无法互相访问
4. 函数执行完毕后,函数内部的变量实际被清空了
function fn() {
let i = 1
console.log(i) // 1
}
fn()
console.log(i) // 局部变量不能访问
1. let 声明的变量会产生块作用域,var 不会产生块作用域
2. const 声明的常量也会产生块作用域
3. 不同代码块之间的变量无法互相访问
4. 推荐使用 let 或 const
for(let t = 1; t <= 3; t++) {
// 块作用域
console.log(t)
}
console.log(t) // 无法访问t
作用域链本质上是底层的变量查找机制。
Ø 在函数被执行时,会优先查找当前函数作用域中查找变量
Ø 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
堆栈空间分配区别:
1. 栈(操作系统): 由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面。
2. 堆(操作系统): 一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆里面。
IE采用的引用计数算法, 定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象:有对象引用就 + 1,多次引用就累加 ++,减少一个引用就 - 1,没有引用就清除,释放内存
致命问题:嵌套引用,两个对象互相引用,将不会被回收,导致内存泄漏
function fn() {
let o1 = {}
let o2 = {}
o1.a = o2
o2.a = o1
}
fn()
1. 标记清除算法将“不再使用的对象”定义为“无法达到的对象”。
2. 就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。 凡是能从根部到达的对象,都是还需要使用的。
3. 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
简单理解:闭包 = 内层函数 + 外层函数的变量
// 简单的写法
// function outer() {
// let a = 10
// function fn() {
// console.log(a)
// }
// fn()
// }
// outer()
// 因为 i 是全局变量,容易被修改
// 闭包形式 统计函数调用的次数
function count() {
let i = 0
function fn() {
i++
console.log(`函数被调用了${i}次`)
}
return fn
}
const fun = count()
// 闭包 = 内层函数 + 外层函数的变量
// 闭包作用:外层函数可以使用内层函数变量,实现数据私有
// 缺陷:内存泄漏
作用:实现数据私有,不会被污染
缺陷:内存泄漏
变量提升是 JavaScript 中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)
正因如此ES6引入了块级作用域,let 和 const来声明变量
只提升声明,不提升赋值
var num
console.log(`物品${num}件`) // 物品undefined
var num = 10
arguments是函数内部的的伪数组,它包含调用时的所有实参
function getSum() {
// arguments 动态参数 只存在于 函数里面
// 1.是伪数组
let sum = 0
console.log(arguments)
for(let i = 0; i < arguments.length; i++) {
sum += arguments[i]
}
console.log(sum)
}
getSum(2, 3, 4)
getSum(1, 2, 3, 4, 5)
总结: 1. arguments 是一个伪数组,只存在于函数中
2. arguments 的作用是动态获取函数的实参
3. 可以通过for循环依次得到传递过来的实参
// 1. ... 是语法符号,置于末函数之前,用于获取多余的实参
// 2. 借助 ... 获取的剩余实参,是个真数组
function getSum(a, b, ...arr) {
console.log(arr) // 使用不需要 ...
}
getSum(2, 3)
getSum(1, 2, 3)
典型运用场景: 求数组最大值(最小值)、合并数组等
const arr1 = [1, 2, 3, 4]
// 展开运算符 可以展开数组
// console.log(...arr)
// console.log(Math.max(1, 2, 3))
// ...arr = 1, 2, 3, 4
// 1.求数组最大值
console.log(Math.max(...arr1)) // 4
console.log(Math.min(...arr1)) // 1
// 2.合并数组
const arr2 = [3, 4, 5]
const arr = [...arr1, ...arr2]
console.log(arr)
剩余参数:函数参数使用,得到真数组
展开运算符:数组中使用,数组展开
// 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) => ({name: uname})
console.log(fn('呜呜呜'))
1. 普通函数有arguments 动态参数
2. 箭头函数没有 arguments 动态参数,但是有 剩余参数 ...arg
// 1.利用箭头函数求和
const getSum = (...arr) => {
let sum = 0
for(let i = 0; i < arr.length; i++) {
sum += arr[i]
}
return sum
}
console.log(getSum(2, 3))
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。
在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window,因此 DOM事件回调函数为了简便,还是不太推荐使用箭头函数
解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值。
分为:数组解构 和 对象解构
基本语法:
1. 赋值运算符 = 左侧的 [] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
2. 变量的顺序对应数组单元值的位置依次进行赋值操作
交换两个变量 [a, b] = [b, a]
// const arr = [100, 60, 80]
// // 数组解构赋值
// const [max, min, avg] = arr
// console.log(max)
let a = 2
let b = 3;
[b, a] = [a, b]
console.log(a, b)
注意:js 前面必须加分号情况
// // 1. 立即执行函数要加
(function() {}) ();
!function() {} ();
// 2. 使用数组时
const arr = [1, 2, 3]
const str = 'pink'; // 不加;变成'pink'[1, 2, 3]
[1, 2, 3].map(function(item) {
console.log(item)
})
数组解构几种情况:
// // 3.剩余参数 变量少 单元值多
// const [a, b, ...c] = [1, 2, 3, 4]
// console.log(a)
// console.log(b)
// console.log(c)
// // 4. 防止undefined 传递
// const [a = 0, b = 0, c = 0] = [1, 2]
// console.log(a)
// console.log(b)
// console.log(c)
// 5. 按需导入赋值
// const [a, b, , d] = [1, 2, 3, 4]
// console.log(a)
// console.log(b)
// console.log(d)
// const arr = [1, 2, [3, 4]]
// console.log(arr[0])
// console.log(arr[1])
// console.log(arr[2])
// console.log(arr[2][0])
// 6. 多维数组
const [a, b, [c, d]] = [1, 2, [3, 4]]
console.log(a)
console.log(b)
console.log(c)
console.log(d)
基本语法:
1. 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
2. 对象属性的值将被赋值给与属性名相同的变量
3. 注意解构的变量名不要和外面的变量名冲突否则报错
4.对象中找不到与变量名一致的属性时变量值为 undefine
// const uname = 'ls'
// // 对象解构的变量名 可以重新改名
// const {uname: username, gender, age} = { uname: 'zs', gender: '男', age: 18 }
// console.log(username)
// console.log(gender)
// console.log(age)
// 2. 解构数组对象
const pig = [
{
name: '佩奇',
age: 6
}
]
const [{ name, age }] = pig
console.log(name)
console.log(age)
多级对象解构
const pig = [{
name: '佩奇',
family: {
mother: '猪妈妈',
father: '猪爸爸',
bother: '乔治'
},
age: 18
}]
// 多级对象解构
const [{ name, family: { mother, father, bother } }] = pig
console.log(mother)
console.log(father)
console.log(bother)
主要使用场景: 遍历数组的每个元素
语法:
被遍历数组.forEach(function(当前数组元素, 当前元素索引值)) {
// 函数体
}
注意:
1. forEach 主要是遍历数组
2. 参数当前数组元素是必须要写的, 索引号可选
筛选数组符合条件的元素,并且返回筛选之后的新数组
语法:
被遍历数组.filter(function(当前数组元素, 当前元素索引值)) {
return 筛选条件
}
// 1. 渲染函数 封装
function render(arr) {
// 声明空字符串
let str = ''
// 遍历数组
arr.forEach(item => {
// 对象解构
const {name, price, picture} = item
str += `
${name}
${price}
`
})
document.querySelector('.list').innerHTML = str
}
render(goodsList) // 页面打开渲染页面
// 2. 过滤筛选
document.querySelector('.filter').addEventListener('click', e => {
// e.target.dataset.index 对象解构
const {tagName, dataset} = e.target
if(tagName === 'A') {
// console.log(dataset.index)
// arr 是返回的新数组
let arr = goodsList
switch(dataset.index) {
case '1':
arr = goodsList.filter(item => item.price > 0 && item.price <= 100)
break
case '2':
arr = goodsList.filter(item => item.price >= 100 && item.price <= 300)
break
case '3':
arr = goodsList.filter(item => item.price >= 300)
break
}
// console.log(arr)
render(arr)
}
})