变量的生命周期
- 看几个小例子
//问题一: a什么时候被赋值,或者a什么时候出现在内存中?什么时候消失?
<script>
var a = 1 //当代码执行到这一行时才有1
// => window.a = 1 所以当页面关闭时a消失,或者刷新又有了新的a
</script>
//问题二: a什么时候被赋值,或者a什么时候出现在内存中?什么时候消失?
<script>
function fn(){
var a = 1 //当函数调用后a才出现
}
//浏览器执行到这一行
fn() //执行完成以后a消失
// fn() 重新调用,重新赋值a
</script>
//问题三: 如果a被引用者,a什么时候被赋值,或者a什么时候出现在内存中?什么时候消失?
<script>
function fn(){
var a = {name : 2} //执行完不会消失,因为被引用,当window页面关闭,消失
var b = 2 // 执行完就消失了,因为没有东西引用它
window.f = a
}
fn()
console.log(window.f) //1
window.f = {name : 3} //a没有被引用,消失
</script>
总结一下:
- 默认作用域消失时,内存就被回收
- 如果内存被引用着,则不能回收
var 作用域
- 就近原则
- 词法作用域
- 同名的不同变量
一直觉得看例子最直接
//问题一: a=1,指的是哪个a
<script>
var a
function f1(){
var a
function f2(){
var a //同一作用域级的a 就近原则
a = 1
}
}
//函数同理
function f2(){}
function f1(){
function f2(){
f2() //指向父级f2
}
}
</script>
<script>
//词法作用域 ,不同作用域的a是不同的变量
var a
function f1(){
var a
function f2(){
var a
f3()
a = 1
}
}
function f3(){
a = 2 //指向第一个a
}
立即执行函数
-想得到一个独立的作用域.声明一个函数
fucntion f1(){
var a
a = 1
console.log(a)
}
f1()
=>
//但是我想不想要全局变量f1怎么办啊,改一下
function (){
var a
a = 1
}() //语法会报错?怎么办呢
=>
(function (){
var a
a = 1
})()
或者
!function (){
var a
a = 1
}() //需要的是一个独立的作用域
函数前面可以加 + - ~ 都可以成为立即执行函数
//立即执行函数可以加参数
var a = 100
!function (a){ //形参声明的变量,值是第一个参数
var a = arguments[0]
var a
a = 1
console.log(a) //1
}(/*没有参数*/)
console.log(a) //100
var a = 100
!function (a){
console.log(a) //99 新的作用域
}(99)
//问题: 实参a 和形参a 是同一个a 吗?
var a = 100
!function (a){ //新声明的a,只是恰好名字相同,a可以是b,c,i,h不同的名字
console.log(a) //100 赋值时var a =100
}(a) //这个a是var a = 100
变量(声明)提升
- 浏览器在执行代码之前,会先把所有声明提升到作用域的顶部
- 你却从来不知道去提升一下变量
- 只要你提升变量,面试题就是小 case
就喜欢例子
// 问题: a 的值是多少
var a = 100
function a(){}
==>
var a
function a(){}
a = 100
console.log(a) // 100
//问题: a 的值是什么
var a = 100
var a = function (){}
function a(){}
console.log(a)
// 以下代码执行时,三次打印分别输出什么?为什么?手动提升作用域
function add1(i){
console.log("函数声明:"+(i+1));
}
add1(1); // 101
var add1 = function(i){
console.log("函数表达式:"+(i+10));
}
add1(1); // 11
function add1(i) {
console.log("函数声明:"+(i+100));
}
add1(1); // 11
时机(异步)
button.onclick = function(){
console.log("A") //当用户操作,才打印
}
console.log("B") //先打印
还有setTimeout
复习了上面那么多只是,就是为了做一些面试题目:
<ul>
<li>选项1</li>
<li>选项2</li>
<li>选项3</li>
<li>选项4</li>
<li>选项5</li>
<li>选项6</li>
</ul>
var items = document.querySelectorAll('li')
for(var i=0; i<items.length; i++){
items[i].onclick = function(){
console.log(i) //每次结果都是li的长度,为什么呢??
}
}
==>
//提升一下变量,在观察
var items
var i
items = document.querySelectorAll('li')
for(i=0; i<items.length; i++){
//i = 0,1,2,3,4,5,6
items[i].onclick = function(){
console.log(i) //点击每一次打印都是6 怎么解决这个问题呢?
}
}
console.log(i) //6
var items
var i
items = document.querySelectorAll('li')
for(i=0; i<items.length; i++){
//创建一个函数作用域
var temp = function(j){
console.log(j)
}
//把函数i作为参数传进去,得到的是6个不同的值
temp(i) //i = 0,1,2,3,4,5
items[i].onclick = function(){
console.log(i)
}
}
==> 再改一下
var items
var i
items = document.querySelectorAll('li')
for(i=0; i<items.length; i++){
//创建一个函数作用域
var temp = function(j){
// j1 = 0, j2 = 1 ...
console.log(j)
items[j].onclick = function(){
// 每点击一次,出现的值不同
console.log(j)
}
}
//把函数i作为参数传进去
temp(i)
}
==>
var items
var i
items = document.querySelectorAll('li')
for(i=0; i<items.length; i++){
//立即执行函数
!function(j){
items[j].onclick = function(){
// 每点击一次,出现的值不同
console.log(j)
}
//i作为参数传进去
}(i)
}
另一种方法
//修改一下上面
var items
var i
items = document.querySelectorAll('li')
for(i=0; i<items.length; i++){
function temp(j){
//return一个函数
return function(){
console.log(j)
}
}
var fn = temp(i) //创建一个新的作用域
items[i].onclick = fn //等于一个函数
}
==>
var items
var i
items = document.querySelectorAll('li')
for(i=0; i<items.length; i++){
items[i].onclick = function(i){
//return一个函数
return function(){
console.log(i)
}
}(i)
}
又一个题目
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i
};
}
//var fn = fnArr[3]
//fn() 调用时才有i,先执行for循环
console.log( fnArr[3]() ) // 10,怎么输出1,2,3...,9
var fnArr = []
for (var i = 0; i < 10; i ++) {
fnArr[i] = (function(j){
return function(){
return j
}
})(i)
}
console.log( fnArr[3]() ) // 3
var fnArr = []
for (var i = 0; i < 10; i ++) {
(function(i){
fnArr[i] = function(){
return i
}
})(i)
}
console.log( fnArr[3]() ) // 3
//使用ES6语法let,创建一个作用域
var fnArr = []
for (let i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i
}
}
console.log( fnArr[3]() ) // 3
我们做了两个面试题目,发现在ES5
中使用立即执行函数就可以打印出我们需要的东西
- 立即执行函数创造了新的作用域
- 造成上面的原因是闭包
- 闭包作用暴露局部变量
网友评论