函数的环境与作用域
理解什么是环境与作用域
环境,用我们的生活举例子,环境就好像我们的周边设施,学校,超市,药店,公园等,各种设施构成了我们的生活环境。
作用域,就好像这些周边设施一般都是只能服务于周边的居民,不可能说有人大老远千里迢迢来超市买瓶水再千里迢迢赶回去吧,所以说作用域指的就是这些设施的服务范围。
说完了生活的栗子,再返回到代码上吧。环境可以理解为我们定义的一些变量(数据、内存),而作用域指的就是这些变量的作用范围。
let person = '彭于晏';
// console.log(age); // age is not defined
console.log(person); // 彭于晏
function func() {
let age = 18;
console.log(age); // 18
console.log(person); // 彭于晏
}
func();
上面这段代码中,person就好像超市,而age可以理解为我家,我可以去逛超市,可以访问到超市的person变量,但是超市不能访问我家,不能访问到我家的age变量。定义的person和age变量就是相当于环境,person变量的环境可以作用域全局,而age变量只能作用于函数内部,外部无法访问。
如果我们进一步套娃,在函数里再定义一个函数:
这时候有定义了一个dairy环境,作用域比作是我的房间,我的房间的日记只有我能看,我家里其他人都不能看,超市的人更不能看,所以dairy的作用域仅限于fn函数内部。
let person = '彭于晏';
// console.log(age); // age is not defined
// console.log(dairy); // dairy is not defined
function func() {
let age = 18;
// console.log(dairy); // dairy is not defined
function fn() {
let dairy = '日记'
console.log(person); // 彭于晏
console.log(age); // 18
console.log(dairy); // 日记
}
}
func();
关于环境的生命周期
我们已经把环境比作设施了,周边的公共设施的存在是因为有人在用,假如一个地区人都走了,那么这些设施也没有存在的意义了,那么就会面临着被拆迁。所以一块数据(环境)使用完了之后,不会再用了,那么它的生命周期也就结束了,会被回收。
我们举一个栗子,我们希望实现一个累加的功能
function func() {
let n = 1;
function sum() {
console.log(`今天是学习JS的第${++n}天`);
}
sum();
}
func(); // 今天是学习JS的第2天
func(); // 今天是学习JS的第2天
func(); // 今天是学习JS的第2天
我们知道函数的语句从上到下执行完就没了,就会被清除,如果反复调用也只会重新执行。
那么一个环境要怎么样才能不被清理呢?有人用就不会被清理掉。
所以我们不妨让这个函数一直被使用,将函数赋值给fn,实则是将函数的地址赋值给了fn,所以第一次调用fn的时候,n=2,那么这个地址保存的n就为2,所以再一次调用fn的时候,在n=2的基础上再加1,依次累加。
function func() {
let n = 1;
return function() {
console.log(`今天是学习JS的第${++n}天`);
}
}
let fn = func();
fn(); // 今天是学习JS的第2天
fn(); // 今天是学习JS的第3天
fn(); // 今天是学习JS的第4天
fn(); // 今天是学习JS的第5天
闭包
什么是闭包?
其实上面讲到环境与作用域的时候,用嵌套函数举的例子都是闭包。那么什么是闭包呢?闭包指的就是能够访问父作用域的函数。 也就是说我在自己的房间里,可以去访问我家,也可以访问外面的各种设施,这种情况就是闭包。
闭包代码实例
举几个例子加深印象:
function init() {
let user = '彭于晏';
// func函数时定义字init函数内的
function func() {
// func函数内没有定义局部变量,却可以访问外部函数的变量
console.log(user); // 彭于晏
}
func();
};
init();
获取对应区间的数据:
let arr = [98, 1, 123, 41, 54, 7, 41, 92, 72, 95]
function between(start, end) {
return arr
// filter参数里的箭头函数是between函数的子函数
// 子函数可以访问到父函数的start和end变量
.filter(value => value >= start && value <= end)
// 对过滤后的数组进行排序
.sort((a, b) => a - b)
}
console.log(between(50, 100)); // [54, 72, 92, 95, 98]
根据字段进行排序:
let array = [{
name: '张三',
score: 88,
age: 19
}, {
name: '李四',
score: 78,
age: 20
}, {
name: '王五',
score: 82,
age: 27
}, {
name: '刘六',
score: 59,
age: 22
}, {
name: '林七',
score: 42,
age: 52
}, {
name: '赵八',
score: 67,
age: 25
}, {
name: '郑九',
score: 32,
age: 34
}];
function order(field) {
// order函数内部有sort的排序函数
// 排序函数可以访问到order的field变量
return array.sort((a, b) => a[field] > b[field] ? 1 : -1)
}
let fn = order('score');
console.log(fn);
调整字体大小:
<button>25px</button>
<button>40px</button>
<p>我就是个p</p>
let btns = document.querySelectorAll("button");
let p = document.querySelector("p");
btns.forEach((item) => {
// 点击事件时forEach方法的内部函数
item.addEventListener("click", () => {
// 点击函数没有声明item的变量却可以使用父函数的item
p.style.fontSize = item.innerHTML;
});
});
点击25px按钮时,字体大小变成25px
点击40px按钮时,字体大小变成40px
前面花大篇幅讲解环境与作用域就是为了后面可以更好的理解闭包到底是个什么玩意儿,希望本文对大家有帮助,如有错误请指正,谢谢!