浅谈JavaScript作用域

浅谈JavaScript作用域


作用域的问题是任何一门语言必须弄清的问题之一,作为一个初学者,我希望通过这篇博客对我所学的知识做一个总结,有不对或者不清楚的地方还请不吝赐教

词法作用域和动态作用域

  • 词法作用域
    也可以理解为静态作用域,也就是当你打出代码的时候就决定了作用域;

    无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定

作用域链和遮蔽效应

var a = 1,b = 2,c = 3;
function f1(){
	var b = 20, c = 30;
	function f2(){
	var c = 300;
	console.log(a,b,c);
	}
	f2();
}
f1();//1,20,300

最外面的是全局作用域,然后是f1的函数作用域,最里面是f2的函数作用域,他们是嵌套的关系;对于最里层的f2,它的作用域链就是 f2内–>f1内–>全局(Window);

通过这种技术可以使得同名变量在不同作用域中值不同。

  • 动态作用域
    javascript使用的是词法作用域,它最重要的特征是它的定义过程发生在代码的书写阶段

    那为什么要介绍动态作用域呢?实际上动态作用域是javascript另一个重要机制this的表亲。作用域混乱多数是因为词法作用域和this机制相混淆,傻傻分不清楚

    动态作用域并不关心函数和作用域是如何声明以及在任何处声明的,只关心它们从何处调用。换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套

var a = 2;
function foo() {
    console.log( a );
}
function bar() {
    var a = 3;
    foo();
}
bar();

【1】如果处于词法作用域,也就是现在的javascript环境。变量a首先在foo()函数中查找,没有找到。于是顺着作用域链到全局作用域中查找,找到并赋值为2。所以控制台输出2

【2】如果处于动态作用域,同样地,变量a首先在foo()中查找,没有找到。这里会顺着调用栈在调用foo()函数的地方,也就是bar()函数中查找,找到并赋值为3。所以控制台输出3

两种作用域的区别,简而言之,词法作用域是在定义时确定的,而动态作用域是在运行时确定的

声明提升

  • 变量提升
a = 2;
console.log(a);//2
var a ;

console.log(b);//undefine
var b = 2;

var a = 2;
这个代码片段实际上包括两个操作:var a 和 a = 2

第一个定义声明是在编译阶段由编译器进行的。第二个赋值操作会被留在原地等待引擎在执行阶段执行
每个作用域都会进行提升操作

  • 函数提升
foo();
function foo(){
    console.log(1);//1
}

foo1();
var foo1 = function(){
    console.log(1);//TypeError: foo1 is not a function
}

函数声明会提升,但函数表达式却不会提升

上述代码提升后会变为

function foo(){
    console.log(1);//1
}
foo();

var foo1;
foo1();
foo1 = function(){
    console.log(1);//TypeError: foo1 is not a function
}

这样就容易理解了,函数声明提升了,而字面量形式的函数表达式会使用变量提升,当执行foo1();时,foo1 是一个未赋值的变量,默认赋值为undefine; 自然不能拿当做函数来执行;

  • 函数覆盖

函数声明和变量声明都会被提升。但是,函数声明会覆盖变量声明;但是,如果变量存在赋值操作,则最终的值为变量的值

变量的重复声明是无用的,但函数的重复声明会覆盖前面的声明(无论是变量还是函数声明)

var a;
function a(){};
console.log(a);//'function a(){}'
a = 1;
console.log(a);//1

【1】变量的重复声明无用

var a = 1;
var a;
console.log(a);//1

【2】由于函数声明提升优先于变量声明提升,所以变量的声明无作用

var a;
function a(){
    console.log(1);
}
a();//1

【3】后面的函数声明会覆盖前面的函数声明

a();//2
function a(){
    console.log(1);
}
function a(){
    console.log(2);
}

所以,应该避免在同一作用域中重复声明

块级作用域

随着ES6的推广,块作用域也将用得越来越广泛。

  • 变量污染
var i = 99;
for (var i= 0; i<10; i++) {
     console.log(i);
}
console.log(i);//10

本来只想在循环内使用的变量污染了全局环境;

ES6改变了现状,引入了新的let关键字,提供了除var以外的另一种变量声明方式。let关键字可以将变量绑定到所在的任意作用域中(通常是{…}内部),实现块作用域;

var i = 99;
for (let i= 0; i<10; i++) {
     console.log(i);
}
console.log(i);//99
var a = [];
for(let i = 0; i < 5; i++){
    a[i] = function(){
        return i;
    }
}
console.log(a[0]());//0

以及setTimeout同样可以通过在循环中使用let来防止最后输出相同;

let 不可以变量提升,不可以多次声明

除了let以外,ES6还引入了const,同样可以用来创建块作用域变量,但其值是固定的(常量)。之后任何试图修改值的操作都会引起错误;

const声明的常量,也与let一样不可重复声明

  • try 中的变量也都为块级变量
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值