一. 作用域
1. 函数能封闭主定义域
一个变量如果定义在了一个function里面,那么这个变量就是一个局部变量,只在这个function里面有定义。出了这个function,就如同没有定义过一样。
function fn(){ var a = 3; //定义在一个函数里面的变量,局部变量,只有在函数里面有定义 console.log("我是函数里面的语句,所以我知道a值是" + a); } fn(); console.log("我是函数外面的语句,我认为a的值是" + a);
a被var在了function里面,所以现在这个a变量只在函数内定义
JavaScript变量作用域非常的简单,没有块级作用域,管理住作用域的只有一个东西:函数。
如果一个变量,没有定义在任何的function中,那么它将在全部程序范围内都有定义:
就是你在js的任何位置都能够使用它
var a = 10; //定义在全局范围内的一个变量,全局变量,在程序任何一个角落都有定义 function fn(){ console.log("我是函数里面的语句,我认识全局变量a,他的值为" + a); } fn(); console.log("函数外面的语句,我也也认识a,它的值为" + a)
总结:
1、定义在function里面的变量,叫做局部变量,只在function里面有定义,出了function没有定义的。
2、定义在全局范围内的,没写在任何function里面的,叫做全局变量,全局都都认识。
2. 作用域链
当遇见一个变量时,JS引擎会从其所在的作用域依次向外层查找,查找会在找到第一个匹配的标识符的时候停止.
function outer(){ var a = 3; //a的作用域就是outer function inner(){ var b = 5; //b的作用域就是inner console.log(a); //能够正常输出3,a在本层没有定义,就是找上层 console.log(b); //能够正常输出5 } inner(); } outer(); console.log(a); //报错,因为a的作用域outer
多层嵌套,如果有同名的变量,那么就会发生“遮蔽效应”:
var a = 2; //全局变量 function fn(){ var a = 3; //就把外层的a给遮蔽了,这函数内部看不见外层的a了。 console.log(a); //输出3,变量在当前作用域寻找,找到了a的定义值为5 } fn(); console.log(a); //输出2,变量在当前作用域寻找,找到了a的定义值为1
作用域链:一个变量在使用的时候,就会在当前层去寻找它是否被定义,如果找不到,就找上一层function,直到找到全局变量,如果全局也没有,就报错。
var a = 1; //全局变量 var b = 2; //全局变量 function outer(){ var a = 3; //遮蔽了外层的a,a局部变量 function inner(){ var b = 4; //遮蔽了外层的b,b局部变量 console.log(a); //① 输出3,a现在在当前层找不到定义的,所以就上一层寻找 console.log(b); //② 输出4 } inner(); //调用函数 console.log(a); //③ 输出3 console.log(b); //④ 输出2 b现在在当前层找不到定义的,所以就上一层寻找 } outer(); //执行函数,控制权交给了outer console.log(a); // ⑤ 输出1 console.log(b); // ⑥ 输出2
3. 不写var就自动成全局变量了
// var a, 相当于在全局var了一个a function fn(){ a = 3; //这个a第一次赋值的时候,并没有var过, //所以就自动的在全局的范围帮你var了一次 } fn(); console.log(a);
这是JS的一个机理,如果遇见了一个标识符,从来没有var过,并且还赋值了:
num = 12;
那么就会自动帮你在全局范围内定义var num;
告诉我们一个道理,变量要老老实实写var。
4.函数的参数,会默认定义为这个函数的局部变量
function fn(a,b,c,d){ // var a,b,c,d; 相当于在函数内部var四个变量,是局部变量 }
a,b,c,d就是一个fn内部的局部变量,出了fn就没有定义。
5.全局变量的作用
5.1 通信,共同操作同一个变量
两个函数同时操作同一个变量,一个增加,一个减少,函数和函数通信。
var num = 0; function add(){ num++; } function remove(){ num--; }
5.2 累加,重复调用函数的时候,不会重置
var num = 0; function baoshu(){ num++; console.log(num); } baoshu(); //1 baoshu(); //2 baoshu(); //3
如果num定义在baoshu里面,每次执行函数就会把num重置为0:
function baoshu(){ var num = 0; num++; console.log(num); } baoshu(); //1 baoshu(); //1 baoshu(); //1
6.函数的定义也有作用域
//这个函数返回a的平方加b的平方 function pingfanghe(a,b){ return pingfang(a) + pingfang(b); //返回m的平方 function pingfang(m){ return Math.pow(m,2) } }
// 现在相求4的平方,想输出16
pingfang(4); //报错,因为全局作用域下,没有一个函数叫做pingfang
机理:
function big{ function small{ } small(); //可以运行 } small(); //不能运行,因为小函数定义在了大函数里面,离开大函数没有作用域。
二. 闭包
1. 闭包
function outer(){ var a = 33; function inner(){ console.log(a); } return inner; } var inn = outer(); inn(); //弹出333
推导过程:
我们之前已经学习过,inner()这个函数不能在outer外面调用,因为outer外面没有inner的定义:
function outer(){ var a = 888; function inner(){ console.log(a); } } //在全局调用inner但是全局没有inner的定义,所以报错 inner();
但是我们现在就想在全局作用域下,运行outer内部的inner,此时我们必须想一些奇奇怪怪的方法。
有一个简单可行的办法,就是让outer自己return掉inner:
function outer(){ var a = 33; function inner(){ console.log(a); } return inner; //outer返回了inner的引用 } var inn = outer(); //inn就是inner函数了 inn(); //执行inn,全局作用域下没有a的定义 //但是函数闭包,能够把定义函数的时候的作用域一起记忆住 //能够输出333
这就说明了,inner函数能够持久保存自己定义时的所处环境,并且即使自己在其他的环境被调用的时候,依然可以访问自己定义时所处环境的值。
一个函数可以把它自己内部的语句,和自己声明时所处的作用域一起封装成了一个密闭环境,我们称为“闭包” (Closures)。
每个函数都是闭包,每个函数天生都能够记忆自己定义时所处的作用域环境。但是,我们必须将这个函数,挪到别的作用域,才能更好的观察闭包。这样才能实验它有没有把作用域给“记住”。
因为我们总喜欢在函数定义的环境中运行函数。从来不会把函数往外挪。那为啥学习闭包,防止一些隐患,。
2.闭包的性质
每次重新引用函数的时候,闭包是全新的。
function outer(){ var count = 0; function inner(){ count++; console.log(count); } return inner; } var inn1 = outer(); var inn2 = outer(); inn1(); //1 inn1(); //2 inn1(); //3 inn1(); //4 inn2(); //1 inn2(); //2 inn1(); //5
无论它在何处被调用,它总是能访问它定义时所处作用域中的全部变量