一、作用域
作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问,作用域分为全局作用域和局部作用域。
局部作用域
局部作用域分为函数作用域和块作用域。
函数作用域
<script>
// 声明 counter 函数
function counter(x, y) {
// 函数内部声明的变量
const s = x + y
console.log(s) // 18
}
// 设用 counter 函数
counter(10, 8)
// 访问变量 s
console.log(s)// 报错
</script>
<script>
{
// age 只能在该代码块中被访问
let age = 18;
console.log(age); // 正常
}
// 超出了 age 的作用域
console.log(age) // 报错
let flag = true;
if(flag) {
// str 只能在该代码块中被访问
let str = 'hello world!'
console.log(str); // 正常
}
// 超出了 age 的作用域
console.log(str); // 报错
for(let t = 1; t <= 6; t++) {
// t 只能在该代码块中被访问
console.log(t); // 正常
}
// 超出了 t 的作用域
console.log(t); // 报错
</script>
<script>
// 必须要有值
const version = '1.0.0';
// 不能重新赋值
// version = '1.0.1';
// 常量值为对象类型
const user = {
name: '小明',
age: 18
}
// 不能重新赋值
user = {};
// 属性和方法允许被修改
user.name = '小小明';
user.gender = '男';
</script>
let
和
const
经常不加区分的使用,如果担心某个值会不小被修改时,则只能使用
const
声明成常量。
全局作用域
<script>
// 此处是全局
function sayHi() {
// 此处为局部
}
// 此处为全局
</script>
<script>
// 全局变量 name
const name = '小明'
// 函数作用域中访问全局
function sayHi() {
// 此处为局部
console.log('你好' + name)
}
// 全局变量 flag 和 x
const flag = true
let x = 10
// 块作用域中访问全局
if(flag) {
let y = 5
console.log(x + y) // x 是全局的
}
</script>
总结:
-
为
window
对象动态添加的属性默认也是全局的,不推荐! -
函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
-
尽可能少的声明全局变量,防止全局变量被污染
JavaScript 中的作用域是程序被执行时的底层机制,了解这一机制有助于规范代码书写习惯,避免因作用域导致的语法错误。
作用域链
JS垃圾回收机制
闭包
闭包是一种比较特殊和函数,使用闭包能够访问函数作用域中的变量。从代码形式上看闭包是一个做为返回值的函数
总结:
-
闭包本质仍是函数,只不是从函数内部返回的
-
闭包能够创建外部可访问的隔离作用域,避免全局变量污染
-
过度使用闭包可能造成内存泄漏
注:回调函数也能访问函数内部的局部变量。
<script>
function get() {
let a = 1
function out() {
a++
console.log(a)
}
return out
}
let f = get()
f()
f()
f()
//闭包的作用: 延长局部变量的生命周期,让局部变量活的久一些
//缺点: 占用内存
</script>
变量提升
变量提升是 JavaScript 中比较“奇怪”的现象,它允许在变量声明之前即被访问
注:关于变量提升的原理分析会涉及较为复杂的词法分析等知识,而开发中使用 let
可以轻松规避变量的提升
二、函数
函数提升
函数提升与变量提升比较类似,是指函数在声明之前即可被调用。
<script>
// 调用函数
foo()
// 声明函数
function foo() {
console.log('声明之前即被调用...')
}
// 不存在提升现象
bar() // 错误
var bar = function () {
console.log('函数表达式不存在提升现象...')
}
</script>
总结:
-
函数提升能够使函数的声明调用更灵活
-
函数表达式不存在提升的现象
-
函数提升出现在相同作用域当中
参数
函数参数的使用细节,能够提升函数应用的灵活度
默认值
<script>
// 设置参数默认值
function sayHi(name="小明", age=18) {
document.write(`<p>大家好,我叫${name},我今年${age}岁了。</p>`);
}
// 调用函数
sayHi();
sayHi('小红');
sayHi('小刚', 21);
</script>
总结:
-
声明函数时为形参赋值即为参数的默认值
-
如果参数未自定义默认值时,参数的默认值为
undefined
-
调用函数时没有传入对应实参时,参数的默认值被当做实参传入
动态参数
arguments
是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参。
<script>
// 求生函数,计算所有参数的和
function sum() {
// console.log(arguments)
let s = 0
for(let i = 0; i < arguments.length; i++) {
s += arguments[i]
}
console.log(s)
}
// 调用求和函数
sum(5, 10)// 两个参数和
sum(1, 2, 4) // 三个参数和
</script>
总结:
-
arguments
是一个伪数组 -
arguments
的作用是动态获取函数的实参
剩余参数
<script>
// ... 可以获取所有剩余参数 是一个真数组
// 定义函数,不知道写几个参数,...变量名
function get(a,...args) {
let sum = 0
for (let i = 0; i < args.length; i++) {
sum += args[i]
}
console.log(sum)
}
get(1, 2)
get(1, 2, 3, 4)
get(1, 2, 3, 4, 5)
</script>
总结:
-
...
是语法符号,置于最末函数形参之前,用于获取多余的实参 -
借助
...
获取的剩余实参,是个真数组
arguments和...的区别
<script>
function out(a,b) {
console.log(arguments)
}
out(1,2,3)
//1,2,3
function get(a,...args) {
console.log(args)
}
get(1,2,3)
//2,3
</script>
展开运算符
<script>
//展开运算符 可用于合并数组 求最大值和最小值 ...
const arr = [1,2,3,4,5]
const out = Math.max(...arr)
console.log(out)
</script>
<script>
const obj1 = {
uname: '张三',
age: 18
}
const obj2 = {
...obj1,
age: 11
}
obj2.uname = '王五'
console.log(obj2)
console.log(obj1)
</script>
箭头函数
<script>
//箭头函数: 优化函数的写法
const out = function () {
console.log(14)
}
out()
const fn = () => {
console.log(14)
}
fn()
const go = a => {
console.log(14)
}
go()
//只有一个形参,括号可以省略不写
const to = (a, b) => {
console.log(14)
}
to()
// const get = (x, y) => {
// return x + y
// }
// const res = get(1, 2)
// console.log(res)
const get = (x, y) => x + y
const res = get(1, 2)
console.log(res)
//如果函数体只有一条代码,那么大括号和return可以省略不写
const ge = () => ({ uname: '张三', age: 14 })
const ar = ge()
console.log(ar)
//如果函数体只有一条代码,并且返回值是一个对象,需要在对象外使用()
</script>
总结:
-
箭头函数属于表达式函数,因此不存在函数提升
-
箭头函数只有一个参数时可以省略圆括号
()
-
箭头函数函数体只有一行代码时可以省略花括号
{}
,并自动做为返回值被返回 -
箭头函数中没有
arguments
,只能使用...
动态获取实参
<script>
const get = (...arr) => {
let sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
return sum
}
const res = get(1, 2, 3, 4, 5)
alert(res)
//箭头函数里面没有arguments
</script>
<button>按钮</button>
<script>
// const btn = document.querySelector('button')
// btn.addEventListener('click',function() {
// console.log(this)
// })
const btn = document.querySelector('button')
btn.addEventListener('click',() => console.log(this))
//事件注册 处理函数 尽量不用箭头函数
</script>
<button>按钮</button>
<script>
// const btn = document.querySelector('button')
// btn.addEventListener('click', function () {
// setTimeout(() => { this.disabled = true }, 2000)
// })
//事件注册 处理函数 尽量不用箭头函数
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
setTimeout(function () {
this.disabled = true
}, 2000)
})
</script>
解构赋值
解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值,分为数组解构、对象解构两大类型。
数组解构
<script>
const [hr, lx, mi, fz] = ['海尔', '联想', '小米', '方正']
console.log(hr, lx, mi, fz);
//交换两个变量的值
let a = 1
let b = 2
;[b, a] = [a, b]
console.log(a, b)
function get() {
return [100, 60]
}
const [max, min] = get()
document.write(max, min)
</script>
<script>
const [a,[b,[c,d],e],f] = [1,[2,[3,4],5],6]
console.log(a)
console.log(b)
console.log(c)
console.log(d)
console.log(e)
console.log(f)
console.log(a,[b,[c,d],e],f)
</script>
总结:
-
赋值运算符
=
左侧的[]
用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量 -
变量的顺序对应数组单元值的位置依次进行赋值操作
-
变量的数量大于单元值数量时,多余的变量将被赋值为
undefined
-
变量的数量小于单元值数量时,可以通过
...
获取剩余单元值,但只能置于最末位 -
允许初始化变量的默认值,且只有单元值为
undefined
时默认值才会生效
注:支持多维解构赋值,比较复杂后续有应用需求时再进一步分析
对象解构
<script>
// 普通对象
const user = {
name: '小明',
age: 18
};
// 批量声明变量 name age
// 同时将数组单元值 小明 18 依次赋值给变量 name age
const {name, age} = user
console.log(name) // 小明
console.log(age) // 18
</script>
<script>
const obj = {
uname: '张三',
age: 14
}
const {uname: hello,age} = obj
console.log(hello)
//解构时 变量名需要和 对象里面的属性名保持一致
</script>
<script>
const pig = {
name: '佩奇',
family: {
mother: '猪妈妈',
father: '猪爸爸',
sister: '乔治'
},
age: 6
}
const { name, age, family: { mother, father, sister } } = pig
console.log(name, age, mother, father, sister)
</script>
总结:
-
赋值运算符
=
左侧的{}
用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量 -
对象属性的值将被赋值给与属性名相同的变量
-
对象中找不到与变量名一致的属性时变量值为
undefined
-
允许初始化变量的默认值,属性不存在或单元值为
undefined
时默认值才会生效
注:支持多维解构赋值,比较复杂后续有应用需求时再进一步分析
遍历数组 forEach 方法
<script>
const arr = [1, 2, 3, 4, 5]
let sum = 0
// arr.forEach(function(item){
// sum = sum + item
// })
// console.log(sum)
arr.forEach(a => sum = sum + a)
console.log(sum)
</script>
筛选数组 filter 方法
<script>
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
// const newArr = []
// arr.forEach(function (item) {
// if (item >= 5) {
// newArr.push(item)
// }
// })
const newArr = arr.filter(item => item >= 5)
console.log(newArr)
</script>