作用域的类型特点
特点:全局作用域,局部作用域,块级作用域
共同特点:如果作用域之间存在包含关系,也就是子可以拿父,父不可以拿子.
1.在不使用var的情况下改变的是全局变量
var a = 10;
function fn(){
a = 20; //注意这里并不是var = a;
console.log(a); //20
}
fn();
console.log(a); //20
2.使用var进行重新声明,声明的是局部变量,和全局变量没有关系.
var a = 10;
function fn(){
var a = 20; //注意这里使用var重新声明
console.log(a); //20
}
fn();
console.log(a); //10
3.当a作为实参传入到函数中.
var a = 10;
function fn(a){ //形参就是函数的内部,局部
//等同于 var a;
a = 20;
console.log(a); //20
}
fn(a); //注意:这里a作为实参传递进来
console.log(a); //10
其实这里理解形参a也是进行声明的函数内部的局部变量,对全局的变量不会产生影响.
4.作用域是单独的,这里是拿不到每一个 i 的
//假设arr的长度是10
for(var i = 0;i < arr.length;i++){
arr[i].onclick = function(){
console.log(i); //10
}
}
这里涉及到Javascript的异步,因为for循环自动执行,而点击事件没有立即执行,点击事件会停顿,但是for循环没有等点击事件,还在执行,当点击的时候,for循环已经执行结束了,所有这时候只能拿到for循环结束时候的 i 值.
问题?如果想要拿到每个 i ,也就是说必须把每个 i 都要存下来,所以要有一个独立作用
域把每个 i 存下来(现在拿不到).
方法一
//假设arr的长度是10
//这里把 var 改为 let
for(let i = 0;i < arr.length;i++){
arr[i].onclick = function(){
console.log(i);
}
}
因为当我们写成var的时候for循环是没有块级作用域的,而let是有块级作用域的,这里修改为let可以相当于for循环里面的内容执行了10遍,每一个都是独立的作用域.
方法二
for(var i = 0;i < arr.length;i++){
(function(){
arr[i].onclick = function(){
console.log(i);
}
})(i)
}
在使用var的时候,可以使用匿名函数,通过把每个 i 传进去也可以拿到每个 i,相当于我们手动的创建独自的作用域,来存储每个 i
5.前面通过块级作用域是为了可以拿到每个 i,但是有时候也需要把前面的数据覆盖
function fn(){
var a = 10;
a++;
console.log(a);
}
fn();//11
fn();//11
这里我们每次执行函数.都是重新执行函数fn,a都会重新被声明赋值.
问题?当不允许声明全局的变量a,函数执行两次的结果要求为11,12
function fn(){
var a = 10;
return function(){
a++;
console.log(a);
}
}
var f = fn();
f(); //11
f(); //12
这里通过 fn 函数执行返回一个函数 f,这时候的 a 相当函数 f 就是一个全局的变量,同时 a 也不会被重新声明,不会被覆盖.
6 .函数存在两个作用域
定义作用域:在哪个位置定义的
执行作用域:函数在哪执行的
在函数执行时,可以拿到定义作用域中存在值变量
7.函数变量的读取读取顺序
当前作用域(参数) ->父级作用域->......全局作用域->抛出错误
var a = 10;
function fn(a){
console.log(a); //hello 参数也是在当前作用域中
}
fn("hello");
var a = 10;
function fn(a){
a = 20;
console.log(a); //20 当前作用域
}
fn("hello");
var a = 10;
function fn(a){
console.log(a); //10 全局作用域
}
fn(a);
以上是对作用域的一些理解,可能还不是特别具体,在闭包,以及在严格模式(“use strict”)下的作用域的情况稍后再整理出来.