zoukankan      html  css  js  c++  java
  • JS简记-作用域1

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

    第一行代码var a=1,会被引擎看成两句话:var a和a=1。

    首先,会由编译器询问当前作用域内(这里就是全局作用域)是否存在已经声明的变量a,如果存在则忽略var a,否则在当前作用域内声明变量a(这时,作用域内就有了变量a。测试时可以发现,即便两行代码倒置顺序,并不会抛ReferenceError,而是打印undefined,说明引擎首先会提前通过编译器执行代码中所有的声明操作)。

    其次,编译器声明变量a(即在作用域内生成了变量a)后,就会编译a=1语句,编译后的语句由执行引擎执行。

    最后,在执行引擎执行时,也就是在执行被编译后的a=1时,首先会进行LHS(左查找,目的是找到变量所在的容器,顺着作用域链查找,如果最终在全局作用域中仍未找到,则会默认在全局作用域内声明该变量,但如果是“use strict”,则与RHS未找到一样,抛ReferenceError),然后在赋值1。

    var o = {};
    o.b = 1;
    console.log(o.b);

    引擎在执行o.b=1时(假设已被编译器编译),首先会LHS查找变量o,然后使用对象访问规则访问o的b属性,这里就是对b赋值1。

    eval和with

    js中变量的作用域通常情况下,在书写代码时就已经确定了,但也有在运行时动态修改作用域的语法。

    eval函数可以传入一个字符串,该字符串在运行时被解析为相应语句来执行。

    function foo(str){
        eval(str);//改变a的作用域
        console.log(a);//2
    }
    var a = 1;
    foo("var a = 2;");

    with可以将一个对象视作为一个作用域,在with块内,可以直接引用对象中的属性,而无需再强调对象本身。在with块中仍然会使用LHS和RHS查找原则,由于o2没有a属性,所以with(o2)块中使用LHS对a赋值时,最终赋在了全局作用域上。

    var o1 = {
        a: 1
    };
    var o2 = {};
    
    with(o1){
        a = 2;
    }
    with(o2){
        a = 2;
    }
    console.log(o1.a);//2
    console.log(o2.a);//undefined

    上面我们见到了两种作用域:函数和with块

    我们通常会将一些执行逻辑放在函数内,但在js中函数还有一个重要的用途,那就是封装变量与函数,将变量与函数放在一个函数内可以很好将其私有化,可以避免将所有变量和函数暴露在全局作用域中。

    我们可以这样做:

    function doSomething(a) {
        b = a + doSomethingElse( a * 2 );
        console.log( b * 3 );
    }
    function doSomethingElse(a) {
        return a - 1;
    }
    var b;
    doSomething( 2 ); // 15

    但更好的做法是:

    function doSomething(a) {
        function doSomethingElse(a) {
            return a - 1;
        }
        var b;
        b = a + doSomethingElse( a * 2 );
        console.log( b * 3 );
    }
    doSomething( 2 ); // 15    

    由于将函数和变量封装起来,使得外部更加“干净”,但这样写需要先声明函数,再进行函数调用,很罗嗦,好在js提供了相应的解决策略。

    function foo(a) { 
        //...
    }
    foo(a); 

    相当于:

    (function foo(a) { 
        //...
    })(a);
    //or
    (function foo(a) { 
        //...
    }(a)); 

    这种模式叫做IIFE,即立即执行函数表达式。

    catch作用域:

    try{
        var a = 1;
        console.log(b);//ReferenceError
    }catch(e){
        var b = 2;
        console.log(e);
    }
    console.log(a);//1
    console.log(b);//ReferenceError

    let(es6)可以将变量绑定到任意的作用域中,通常是{...}中,且其声明不会被提升,使用let可以将变量限定在局部逻辑中,更加利于垃圾收集,下面的代码没有使用let声明someReallyBigData,而且someReallyBigData只会被process临时使用,而由于click形成了一个覆盖整个作用域的闭包,所以js引擎可能在process执行完后,还会一直保留someReallyBigData。

    function process(data) {
        // 在这里做点有趣的事情
    }
    var someReallyBigData = { .. };
    process( someReallyBigData );
    var btn = document.getElementById( "my_button" );
    btn.addEventListener( "click", function click(evt) {
        console.log("button clicked");
    }, /*capturingPhase=*/false );

    为了提高垃圾收集效率,可以将someReallyBigData使用let声明,并放在代码块中:

    function process(data) {
        // 在这里做点有趣的事情
    }
        // 在这个块中定义的内容可以销毁了!
    {
        let someReallyBigData = { .. };
        process( someReallyBigData );
    }
    var btn = document.getElementById( "my_button" );
    btn.addEventListener( "click", function click(evt){
        console.log("button clicked");
    }, /*capturingPhase=*/false );

    let在循环的使用中也是非常重要的:

    for (let i=0; i<10; i++) {
        console.log( i );
    }
    console.log( i ); // ReferenceError

    上面这段代码相当于下面代码,每次迭代时进行重新绑定(这对于闭包是非常重要的)

    {
        let j;
        for (j=0; j<10; j++) {
            let i = j; // 每个迭代重新绑定!
            console.log( i );
        }
    }    

     const(es6)与let类似,不同的是其值不可变:

    {
        const a = 1;//必须在声明时初始化
        var b = 2;
        console.log(a);//1
        try{
            a = 2;
        }catch(e){
            console.log(e);//TypeError
        }
        
    }
    console.log(b);//2
    console.log(a);//ReferenceError
  • 相关阅读:
    Jessica's Reading Problem POJ
    FatMouse and Cheese HDU
    How many ways HDU
    Humble Numbers HDU
    Doing Homework again
    Stacks of Flapjacks UVA
    Party Games UVA
    24. 两两交换链表中的节点
    面试题 03.04. 化栈为队
    999. 可以被一步捕获的棋子数
  • 原文地址:https://www.cnblogs.com/holoyong/p/8972312.html
Copyright © 2011-2022 走看看