函數
聲明方法有3
function 命令
function print(s) { console.log(s); }
函数表达式
var print = function(s) { console.log(s); };
如果採用函數表達式聲明,給function 取的名字只能在函數內使用
var print = function x(){ console.log(typeof x); }; x // ReferenceError: x is not defined print() // function
Function 构造函数
var add = new Function( 'x', 'y', 'return x + y' ); // 等同于 function add(x, y) { return x + y; }
可以传递任意数量的参数给Function构造函数,只有最后一个参数会被当做函数体,如果只有一个参数,该参数就是函数体。
函數作用域
函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。
var x = function () {
console.log(a);
};
function y(f) {
var a = 2;
f();
}
y(x)
// ReferenceError: a is not defined
x的作用域在外層,訪問不到y中的a
省略參數
多傳或少傳參數,JavaScript都不會報錯,少傳的會帶入undefined
function f(a, b) {
return a;
}
f(1, 2, 3) // 1
f(1) // 1
f() // undefined
f.length // 2 -> f預期接收的參數
但是,没有办法只省略靠前的参数,而保留靠后的参数。如果一定要省略靠前的参数,只有显式传入undefined。
function f(a, b) {
return a;
}
f( , 1) // SyntaxError: Unexpected token ,(…)
f(undefined, 1) // undefined
傳參
var obj = [1, 2, 3];
function f(o) {
o = [2, 3, 4]; // 重新對o賦值,把裡頭的o指向的地址更改了,不影響外面obj實際的值
}
f(obj);
obj // [1, 2, 3]
argument
由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。
arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
一般可以用argument改變參數的值,但是可以開啟嚴格模式切開聯動關係
var f = function(a, b) {
arguments[0] = 3;
arguments[1] = 2;
return a + b;
}
f(1, 1) // 5
var f = function(a, b) {
'use strict'; // 开启严格模式
arguments[0] = 3;
arguments[1] = 2;
return a + b;
}
f(1, 1) // 2
argument vs array
虽然arguments很像数组,但它是一个对象。数组专有的方法(比如slice和forEach),不能在arguments对象上直接使用
callee
arguments对象带有一个callee属性,返回它所对应的原函数。
var f = function () {
console.log(arguments.callee === f);
}
f() // true
可以通过arguments.callee,达到调用函数自身的目的。这个属性在严格模式里面是禁用的,因此不建议使用。
eval
eval命令接受一个字符串作为参数,并将这个字符串当作语句执行。
eval没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。
var a = 1;
eval('a = 2');
a // 2
閉包
可以讀取其他函數內的變量的函數
根據鏈式作用域(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
- 可以读取外层函数内部的变量
- 这些变量始终保持在内存中
closure w/ 鏈式作用域