美文网首页
重拾ECMAScript基础——变量、作用域

重拾ECMAScript基础——变量、作用域

作者: JacobMa1996 | 来源:发表于2017-07-13 17:12 被阅读0次

    变量

    之前看过一遍书,对ES中的参数传递,作用域链,无块级作用域,闭包等有了一定的了解,但是似懂非懂,今天把整个过程用代码演示出来,加深理解。

    • 变量名
      学过汇编和C我们知道,像C/C++这种编译成机器代码的语言,变量名是不需要存储在内存中的,在代码执行时都编译为(栈地址+偏移地址)的形式,变量名只是编程时提供给编译器识别的一个名字,比如int n = 5;在机器代码中是不存在n的,只对内存中5的地址产生类似于mov [0x00410FC0],5的操作;
    • 指针
      指针,int *p = &n;,p这个指针变量保存着n对应的内存空间的地址,即它的值为0x00410FC0,而p这个变量名,也不存在于内存和机器代码中;
    • 引用
      C++中引入引用的概念,除了需要初始化,且只能初始化一次,其实它和变量名没有区别,它就是一个别名;

    提到前面这些概念,就是为了更好的理解javascript中的变量机制,JS是弱类型动态语言(关于强、弱,静态、动态类型请参考 https://www.zhihu.com/question/19918532 ),是一种解释性语言,同时是一种脚本语言(编程性语言、解释性语言、脚本语言请参考 http://blog.csdn.net/mooncom/article/details/60955411 )。

    在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。——百度百科

    JS与C/C++是不同的,JS中保存基本类型的值和标识符在栈区,而引用类型的标识符和地址保存在栈区,值保存在堆内存中;

    存储.png

    通过变量名操作引用类型称为引用,这里的引用和C/C++中的引用不是同一个概念,因此催生出“指针”的概念,操作在栈区的地址,也可以叫做操作指针,或是堆内存地址。

    //C++中指针存放地址
        int n = 5;
        int *p = &n;
        int *q = p;
        cout<<p<<"\t"<<q<<endl;//0x6ffe2c 0x6ffe2c 
        cout<<*p<<"\t"<<*q<<endl;//5 5 
    
    //变量
            function sum1 () {
            }
            var sum2 = sum1;
            console.log(sum2.name);//'sum1'
            console.log(typeof sum1);//'function'
            sum1 = null;
            console.log(typeof sum1);//'null'
            console.log(sum2.name);//'sum1'
    

    保存在栈区或堆内存可以是基本类型的值,也可以是引用类型的值,基本类型的值与引用类型的值的区别:

    • 1.动态属性
      引用类型的值可以动态的改变其属性,因为它是对象;而基本类型的值不可以;
    //动态属性
            var person = new Object();
            person.name = 'Matthew';
            console.log(person.name);//'Matthew'
    
            var name = 'Matthew';
            name.age = 27;
            console.log(name.age);//undefined
    
    • 2.复制变量值
      基本类型的复制是把值复制到新变量分配的内存空间中,两个变量完全独立;
    //变量赋值
            var num1 = 5;
            var num2 = num1;
            num1 = 4;
            console.log(num1 + '\t' + num2);//4 5
    

    我们都知道变量是类似于引用的方式来映射的,《JS高程》中把它解释为指针的复制,实际上指的是传递一个引用,即传递栈区标识符对应的那个地址,和参数中传递基本类型和引用类型是一样的,都是按值传递,只不过前者是传递真值,后者是传递引用(地址)。

            var obj1 = new Object();
            var obj2 = obj1;
            obj1.name = 'Matthew';
            console.log(obj2.name);//'Matthew'
    
    • 3.传递参数
      上面也讲了,JS中参数传递都是按值传递,基本类型很好理解,参数是真值;
    //参数传递
            function add(num) {
                num++;
                return num;
            }
    
            var count = 1;
            var result = add(count);
            console.log(count);//1 没有变化
            console.log(result);//2
    

    而参数为引用类型时,传递的是一个引用,上面讲到,引用类型在栈区的标识符对应的地址,可以理解为一个指针,那么这里可以解释为传递的是这个指针的副本,即这个地址,所以也是值传递,我们看代码:

            function setName(obj) {
                obj.name = 'Matthew';
                obj = new Object();
                obj.name = 'Alex';
            }
    
            var person = new Object();
            setName(person);
            console.log(person.name);//'Matthew'
    

    如果是按引用传递,结果应该显示为'Alex';
    什么是引用传递?,在JS中是不存在引用传递的,在C++中,通过取地址符,取到变量地址,使得形参的地址与实参的相同,改变形参也就改变了实参,所以通常用来通过函数内部修改改变外部环境,看代码:

    void swap(int &a,int &b)
    {
         int temp;
         temp=a;
         a=b;
         b=temp;
         cout<<a<<""<<b<<"\n";
    }
    int main() 
    {
        int x=1;
        int y=2;
        swap(x,y);
        cout<<x<<""<<y<<"\n";//21 21
        return 0;
    } 
    

    形参a,b作为局部变量在栈区开辟了内存空间,存放的是实参变量的地址;如此,才是引用传递;

    作用域

    • 当程序开始执行时,解析器(编译器)会创建一个全局执行环境,又称为执行上下文,在web浏览器中,这个环境就是window对象,以后执行流每进入一个函数,函数的环境的都会被推入这个环境栈中,在函数执行完后,栈将其环境推弹出,把控制权交给之前的执行环境,ECMAScript的执行流正是由这个方便的机制控制的;
      每个执行环境都会保存一个变量对象和一个作用域链,变量对象就是上文提到的标识符和值或标识符和地址(基本类型或引用类型);作用域链的前端就是当前环境的变量对象,下一个对象就是下一个包含(外部)环境的变量对象,以此类推,直到全局环境的变量对象;
    //作用域
            var color = 'blue';
    
            function changeColor() {
                var anotherColor = 'red';
    
                function swapColors() {
                    var tempColor = anotherColor;
                    anotherColor = color;
                    color = tempColor;
                    //这里可以访问color,anotherColor和tempColor
                }
                //这里可以访问color和anotherColor,但不能访问tempColor
                swapColors();
            }
            //这里只能访问color
            changeColor();
    
    作用域链.png

    每个矩形代表一个执行环境,作用域链是以链表的形式连接的,只能向上访问,不能向下访问;

    • 没有块级作用域
      1.Javascript中没有块级作用域:
    //没有块级作用域
            for (var i = 0;i < 10;i++) {
    
            }
    
            console.log(i);//10
    
            if(true) {
                var color = 'blue';
            }
    
            console.log(color);//'blue'
    

    2.声明变量
    未使用var 关键字声明的是全局变量,及时在函数内部;
    3.查询标识符
    从作用域链前端向上逐级查询,直到全局环境;

    函数表达式和闭包放在下一篇吧

    相关文章

      网友评论

          本文标题:重拾ECMAScript基础——变量、作用域

          本文链接:https://www.haomeiwen.com/subject/ntkwhxtx.html