作用域与闭包
作用域
全局作用域只有一个,每个函数又都有作用域(环境)。
- 编译器运行时会将变量定义在所在作用域
- 使用变量时会从当前作用域开始向上查找变量
- 作用域就像攀亲亲一样,晚辈总是可以向上辈要些东西
使用规范
作用域链只向上查找,找到全局window即终止,应该尽量不要在全局作用域中添加变量。
函数被执行后其环境变量将从内存中删除。下面函数在每次执行后将删除函数内部的total变量。
function count() {
let total = 0
}
count()
函数每次调用都会创建一个新作用域
let site = 'mkimq'
function a() {
let web = 'mkimq.com'
function b() {
let url = 'wwww.mkimq.com'
console.log(web)
console.log(site)
}
b()
}
a()
如果子函数被使用时父级环境将被保留
function fn() {
let n = 1
return function() {
let b = 1
return function() {
console.log(++n)
console.log(++b)
}
}
}
const a = fn()()
a()
a()
构造函数也是很好的环境例子,子函数被外部使用父级环境将被保留
function User() {
let a = 1
this.show = function() {
console.log(a++)
}
}
const a = new User()
a.show()
a.show()
const b = new User()
b.show()
let/const
使用 let/const 可以将变量声明在块作用域中(放在新的环境中,而不是全局中)
{
let a = 1
}
console.log(a)
if (true) {
var i = 2
}
console.log(i)
在没有let/const 的历史中使用以下方式产生作用域
var arr = []
for (var i = 0; i < 10; i++) {
(function(a) {
arr.push(() => a)
})(i)
}
console.log(arr[3]())
闭包使用
闭包指子函数可以访问外部作用域变量的函数特性,即使在子函数作用域外也可以访问。如果没有闭包那么在处理事件绑定,异步请求时都会变得困难。
- JS中的所有函数都是闭包
- 闭包一般在子函数本身作用域以外执行,即延伸作用域
基本示例
function fn() {
let name = 'mkimq'
return function() {
return name
}
}
const mk = fn()
console.log(mk())
闭包问题
- 内存泄漏
闭包特性中上级作用域会为函数保存数据,从而造成的如下所示的内存泄漏问题
<body>
<div desc="mkimq">在线文档</div>
<div desc="url">站点</div>
</body>
<script>
let divs = document.querySelectorAll('div')
divs.forEach(function(item) {
item.addEventListener('click', function() {
console.log(item.getAttribute('desc'))
})
})
</script>
下面通过清除不需要的数据解决内存泄漏问题
let divs = document.querySelectorAll('div')
divs.forEach(function(item) {
const desc = item.getAttribute('desc')
item.addEventListener('click', function() {
console.log(desc)
})
item = null
})
- this指向
this 总是指向调用该函数的对象,即函数在搜索this时只会搜索到当前活动对象。