JavaScript中的作用域和作用域链(一)

96
李宪骁
2019.04.15 07:13* 字数 964
大纲

作用域

1.作用域的概念:

变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。

2.全局作用域与函数作用域

  • 全局作用域

生命周期将存在于整个程序之内。能被程序中任何函数或者方法访问。在 JavaScript 内默认是可以被修改的。
一般来说,是指程序最外层定义的函数或者变量

var a = 1//全局变量,可以给任何函数使用
function f1(){
  console.log(a)//函数内无变量声明,只能从全局中找
}
function f2(){
  console.log(++a)//使用了全局变量
}
f1()//1
f2()//2
  • 函数作用域

函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域

function f1(){
  var a = 1
}
console.log(a);//undefined,函数内定义的变量无法被函数外使用

通过return返回函数内部变量,返回我们想要的值

function number(value) {
  var testnumber = 1;
  
  return testnumber + value;
}
console.log(number(1)); //输出2

通过 闭包 访问函数内部变量

闭包

立刻执行函数

它能够自动执行 (function() { //... })() 里面包裹的内容,能够很好地消除全局变量的影响;很多库都用它分离全局作用域,形成一个单独的函数作用域

<script type="text/javascript">

    (function() {

      var testValue = 123;

      var testFunc = function () { console.log('just test'); };

    })();

    console.log(window.testValue);      // undefined
    
    console.log(window.testFunc);       // undefined
    
</script>

image.png

作用域链

1.作用域链的概念:

当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)来保证对执行环境有权访问的变量和函数的有序访问。作用域第一个对象始终是当前执行代码所在环境的变量对象(VO)。

image.png

2.不同情况下的作用域链

思路:
1. 函数在执行的过程中,先从自己内部找变量
2. 如果找不到,再从创建当前函数所在的作用域去找, 以此往上
3. 注意找的是变量的当前的状态

  • 一个函数,包裹一个函数
    函数外有全局变量var x = 10,外面一层函数拥有var x = 30 ,里面一层函数没有变量声明的同时求值console.log(x)
双层函数,内部求值
  • 一个函数,包裹两个函数

    image.png

  • 一个函数,与另一个内部包裹着函数的函数,并行

    image.png

  • 三重函数嵌套,其中第二层函数有与没有return的表现

    上为无return,下为有return

补充知识点:

表达式的定义

表达式(expression),指一个为了得到返回值的计算式

立即执行函数表达式

立即执行函数表达式有多种写法:(function(){})();, 或(function(){}()); ,或!function(){}();,或void function(){}();

image.png

将函数表达式定义为其他变量,再在其他变量后面加上小括号,同样可以立即执行函数表达式
image.png

变量声明和函数声明

  • 变量声明

变量声明:变量的声明只有一种方式,那就是用var关键字声明,直接赋值不是一种声明方式。这仅仅是在全局对象上创建了新的属性(而不是变量)。

区别:

(1)因为它只是一种赋值,所以不会声明提前

alert(a); // undefined
alert(b); // error "b" is not defined
b = 10;
var a = 20;

(2)直接赋值形式是在执行阶段创建

alert(a); // undefined, 这个大家都知道
b = 10;
alert(b); // 10, 代码执行阶段创建
var a = 20;
alert(a); // 20, 代码执行阶段修改

(3)变量不能删除(delete),属性可以删除

a = 10;
alert(window.a); // 10
 
alert(delete a); // true
 
alert(window.a); // undefined
 
var b = 20;
alert(window.b); // 20
 
alert(delete b); // false
 
alert(window.b); // 仍然为 20,因为变量是不能够删除的。

但是,这里有一个意外情况,就是在“eval”的上下文中,变量是可以删除的:

eval('var a = 10;');
alert(window.a); // 10
 
alert(delete a); // true
 
alert(window.a); // undefined
有些debug工具也是可以删除的,因为它们使用了 eval()方法来执行代码的。
  • 函数声明

函数声明:函数的声明有三种方式

(1)function name( ){ }直接创建方式

function add(a,b){
    return a+b;
}

add(5,4);

(2)new Funtion构建函数创建

var add=new Function("a", "b", "return a+b;");

add(4,5);

(3)给变量赋值匿名函数方法创建

var add = function(a,b){
    return a+b;
}

add(4,5);

后面两种方法,在声明前访问时,返回的都是一个undefined的变量。当然,在声明后访问它们都是一个function的函数。

注意:如果变量名和函数名声明时相同,函数优先声明。

alert(x); // function

var x = 10;
alert(x); // 10
 
x = 20;

function x() {};
 
alert(x); // 20

参考链接:
作用域链
作用域
W3school

饥人谷李宪骁
Web note ad 1