函数声明与函数表达式
函数声明会自动提升(function declaration hoisting)
test();
function test() {console.log('for fun')} // for fun
test();
var test = function() {} // error
if(condition){
function sayHi(){}
} else {
function sayHi(){}
} // 无效语法,不同引擎会采取不同方式返回第一或第二个声明
var sayHi;
if (condition) {
sayHi = function (){}
} else {
sayHi = function (){}
} // 正常
函数参数
函数在调用时会获取arguments变量,和参数对应
两者并不访问相同的内存空间,但值会同步
agruments对象有一个callee属性,指向拥有该对象的函数
function a() {
console.log(arguments.callee == a);
}
a(); // true
所有的值类型都通过值类型传递不通过引用传递
function doo(num1, num2) {} // arguments[0] 和 num1 对应
函数属性
caller保存调用该函数的函数的引用,若是全局变量返回null
function a() {
b();
}
function b() {
console.log(arguments.callee.caller);
}
a(); // function a() { b(); }
b(); // null
length表示函数希望接受的命名参数
arguments见下
函数调用
函数在调用时会自动获取this和arguments两个特殊变量
函数调用都是通过值传递,即使引用类型也是通过值传递
// professional js for web developers p71
function setName(obj) {
obj.name = 'N'; // obj是一个内存中对象的引用,指向内存
obj = {}; // obj是值传递,因此不会造成原来的指针变化
obj.name = 'M';
}
var p = {};
setName(p);
p.name; // N
函数调用有四种方式,不同调用方法this指向不同
即使在不同环境中执行(传递的参数,如this值不同),指向的仍是同一个函数
function a(){}
var x = {
b: function(){}
}
function NewObj(){}
x.b() // 方法调用(The Method Invocation Pattern) this == x
a() // 函数调用(The Function Invacation Pattern) this == window(this指向全局变量)
var Obj = new NewObj() // 构造器调用(The Constructor Invocation Pattern) this == Obj
a.apply(obj, arguments) // apply调用(The Apply Invocation Pattern) this == obj
arguments并不是真正的数组,arguments有length属性,但没有任何数组的方法
函数返回值
匿名函数(lambda)
闭包
指有权访问另一个函数作用于衷的变量的函数
不合理的使用闭包会造成内存泄漏
function outter(property) {
return function() {
alert(property);
}
}
var def = outter('just for fun');
def();
def = null; // 解除对匿名函数的引用以释放内存
function domHandle(){
var ele = document.getElementById('a');
ele.onclick = function(){
alert(ele.id); // 循环引用造成无法销毁ele
}
}
function domHandle(){
var ele = document.getElementById('a');
var id = ele.id; // 消除循环引用
ele.onclick = function(){
alert(id);
}
ele = null; // 闭包保存的是外部函数所有的对象,所以要将ele设为null解除对DOM对象的引用
}
闭包通过作用域链获取数据,因此保存的是整个变量对象而非特定值
function create() {
var result = [];
for (var i = 0; i < 10; i++) {
result[i] = function() {
return i; // 在闭包调用的时候保存的i并非每次的值,而是最后的值10
}
}
return result;
}
闭包的调用和声明时间不一致,因此在闭包中this值可能会发生改变
块级作用域
创建并立即调用一个函数,块级作用域中的变量会被从内存中立即销毁
(function(){
// 块级作用域
})();
私有变量和特权方法
在函数中定义私有变量,通过闭包返回特权方法访问私有变量
//1
function MyObject(){
var privateVariable = 10; // 私有变量
function privateFunction(){} // 私有方法
this.publicMethod = function(){ // 特权方法
privateVariable ++;
return privateFunction();
}
}
var obj = new MyObject();
obj.publicMethdo(); // 通过特权方法访问私有变量和方法
//2
(function(){
var privateVariable = 10; // 私有变量
function privateFunction(){} // 私有方法
MyObject = function(){ // 构造函数
};
MyObject.prototype.publicMethod = function(){ // 特权方法
privateVariable ++;
return privateFunction();
}
})();
var obj = new MyObject();
obj.publicMethdo(); // 通过特权方法访问私有变量和方法
//3
(function() {
var pub_pri_var = 0;
function pub_pri_fun () {
return pub_pri_var;
}
MyObj = function (name) {
this.set = function (val) {
name = val;
}
this.get = function () {
return name;
}
}
MyObj.prototype.pub_pub_fun = function () {
pub_pri_var ++;
return pub_pri_fun();
};
})();
var a = new MyObj('a\'s name');
var b = new MyObj('b\'s name');
- 第一种方法,不同的实例有自己的变量及方法
- 第二种方法,所有实例享有同样的私有变量和私有方法
- 第三种方法,所有实例有共同的方法和变量,也有私有的方法和变量
- 使用私有变量会多增加一层作用域链,因此造成查找速度减慢
模块(Module)
利用闭包和私有变量构造模块
// <js: the good parts> p41
var serial_maker = function () { // 一个序列号生成函数
var prefix = '';
var seq = 0;
return {
set_prefix: function(p) {
prefix = p;
},
gensym: function() {
var result = prefix + seq;
seq ++;
return result;
}
}
}
var seqer = serial_maker();
seqer.set_prefix('Q');
var unique = seqer.gensym(); // Q1
通常可和单例共用,为单例创建私有变量和特权方法
var singleton = function () {
var privateVariable;
function privateFunction() {}
var obj = {}
obj.publicProperty;
obj.publicMethod = function () {}
return obj;
} ();
级联
让方法返回this
var MyString = function (fir, sec) {
this.first = fir;
this.second = sec;
}
MyString.prototype.setF = function (val) {
this.first = val;
return this;
};
MyString.prototype.setS = function (val) {
this.second = val;
return this;
};
MyString.prototype.toString = function () {
return this.first + ' ' + this.second;
};
var obj = new MyString('a', 'b');
obj.toString(); // 'a b'
obj.setF('aa').setS('bb').toString(); // 'aa bb'
柯里化(Currying)