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

2017-07-13  本文已影响0人  JacobMa1996

变量

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

提到前面这些概念,就是为了更好的理解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'

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

//动态属性
        var person = new Object();
        person.name = 'Matthew';
        console.log(person.name);//'Matthew'

        var name = 'Matthew';
        name.age = 27;
        console.log(name.age);//undefined
//变量赋值
        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'
//参数传递
        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作为局部变量在栈区开辟了内存空间,存放的是实参变量的地址;如此,才是引用传递;

作用域

//作用域
        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

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

//没有块级作用域
        for (var i = 0;i < 10;i++) {

        }

        console.log(i);//10

        if(true) {
            var color = 'blue';
        }

        console.log(color);//'blue'

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

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

上一篇下一篇

猜你喜欢

热点阅读