《Javascript高级程序设计》学习笔记一
这是我在学习Js红皮书的学习记录
本篇笔记主要记录了函数、变量以及作用域方面的知识
函数
声明
1 | function functionName(arg0, arg1, ..., argN){ |
不必指定返回值,任何函数在任何时候都可以通过return语句后跟要返回的值来实现返回值。当没有返回值时,将默认返回undefined
。
参数
ECMAScript所有参数传递传递的都是值。当传入基本类型的值时,被传递的值会被复制给一个局部变量;当传入引用类型的值(对象)时,会赋值这个值在内存中的地址,因此局部变量的变化会反映在函数外部。
ECMAScript函数不介意传递进来的参数个数,也不介意传进来的参数类型是什么,也就是说,例如定义的函数声明了接收两个参数,但在调用时可以传入0到n个任意参数。
之所以会允许上述情况发生,是因为ECMAScript中的参数在内部是用一个数组来表示的,函数接收到的永远是这个数组,也就不关心数组中的内容到底是什么了。而我们在函数中,可以通过arguments
对象来访问这个数组,从而获得传递给函数的每一个参数(但arguments
对象只是与数组类似,并不是Array的实例)。我们可以通过下标访问元素,使用length
属性确定一共传进了多少个参数。
1 | function sayHi(){ |
因此,如上代码所示,我们可以不显式的使用命名参数,直接通过arguments
获取,这是ECMAScript函数的一个重要特点:即命名参数只提供便利,但不是必需的。
还有一点,arguments
对象可以和命名参数一起使用,同时,它的值永远与对应的命名参数的值保持同步。如以下代码,每次执行都会重写第二个参数,将其值修改为10,不管传入的是多少。(注意:这不意味着这两个值会访问相同的内存空间,它们的内存空间是独立的,但值会同步)
1 | function doAdd(num1, num2){ |
没有传入值的命名参数将被赋予undefined
值。
重载
ECMAScript函数没有重载的概念,因为ECMAScript函数没有签名,其参数是由包含零个或多个值的数组表示的。
但我们可以通过arguments
对象对于传入的参数个数进行判断,或者在函数中判断参数类型来模仿重载。
变量
变量分为两种不同的数据类型的值:基本类型值和引用类型值。
基本类型值指的是五种基本数据类型,在内存中占据固定大小的空间,因此保存在栈内存中,都是按值访问的,可以操作保存在变量中的实际的值,复制时会创建副本。
引用类型的值是保存在堆内存中的对象,不允许直接访问,实际操作的是对象的引用,因此,引用类型的值是按引用访问的,复制的是指针,指向同一个对象。
对于引用类型的值,可以为其动态添加、改变、删除属性和方法。
引用类型类似于C\C++的指针。但需要注意函数的参数都是按值传递的。
作用域
执行环境定义了变量或函数有权访问的其他数据,每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。当某个执行环境的所有代码执行完毕后,环境被销毁,保存在其中的所有变量和函数也随之销毁。
全局执行环境是最外围的一个执行环境,在Web浏览器中,是window对象。
每个函数都有自己的执行环境,当执行流进入一个函数时,其环境就会推入一个环境栈中,执行后再出栈。当代码在执行中,会创建一个作用域链(scope chain),作用是保证对于执行环境有权访问的所有变量和函数的有序访问。作用域链前端是当前代码执行环境的变量对象,末端是全局执行环境。简单来说,就是查找变量定义时一级一级的往上找。
延长作用域的两种方式(会在作用域链的前端临时加一个变量对象):
- try-catch语句的catch块:创建一个新的变量对象,其中包含的是被抛出的错误对象的声明
- with语句:将指定对象添加到作用域链
没有块级作用域
在例如C、C++等语言中,由花括号封闭的代码有自己的作用域,在里面声明的变量执行完后就被销毁了,但是Javascript并不会。如下,即使for循环结束后,创建的变量i
依然存在。
1 | for(var i = 0; i < 10; i++){ |
使用var
声明的变量会自动添加到最接近的环境中,函数内部就是函数的局部作用域。如果初始化变量时没有使用var
,会自动加入到全局环境中。
1 | function add(num1, num2){ |
建议:初始化变量之前一定要先声明。在严格模式下,初始化未经声明的变量会导致错误。
垃圾收集机制
这里顺带提一下Javascript具有自动垃圾收集机制,执行环境会管理代码执行过程中使用的内存,找出不再继续使用的变量,然后释放其占用的内存。垃圾收集器会在固定时间间隔执行这一操作。
垃圾收集器识别无用变量的策略:
- 标记清除:mark-and-sweep:给变量加上标记,例如进入环境,离开环境;(目前流行的方式)
- 引用计数:reference counting:顾名思义。但会导致循环引用的问题。解决:解除引用,即手动置为null。
由于Web浏览器可用内存通常较少,所以确保占用最少的内存可以让页面获得更好的性能。而优化内存占用的最佳方式,就是执行时只保存必要的数据,一旦数据不再有用,将其值置为null来释放引用(dereferencing)。