转载自 https://harttle.land/2016/02/03/js-function-invocation.html
函数(Function)是JavaScript的基本模块单元,JavaScript的代码重用, 信息隐藏,对象组合等都可以借助函数来实现。
调用模式
我们知道在函数里可见的名称包括:函数体内声明的变量、函数参数、来自外部的闭包变量。 此外还有两个:this和arguments。
this 在面向对象程序设计中非常重要,而它的值在JavaScript中取决于调用模式。 JavaScript中的函数有4种调用模式:方法调用、函数调用、构造函数调用、apply调用。
arguments 是一个类数组变量(array like),拥有length属性并可以取下标, 它存着所有参数构成的有序数组。 在JavaScript中,函数调用与函数签名不一致(个数不正确、类型不正确定) 时不会产生运行时错。少了的参数会被置为undefined,多了的参数会被忽略。
JavaScript中的函数有4种调用模式:
- 方法调用(Method Invocation Pattern)
- 函数调用(Function Invocation Pattern)
- 构造函数调用(Constructor Invocation Pattern)
- apply调用(Apply Invocation Pattern)
方法调用
在面向对象程序设计中,当函数(Function)作为对象属性时被称为方法(Method)。 方法被调用时this会被绑定到对应的对象。在JavaScript中有两种语法可以完成方法调用: a.func()和a['func']()。
var obj = {
val: 0,
count: function(){
this.val ++;
console.log(this.val);
}
};
obj.count(); // 1
obj.count(); // 2
值得注意的是,this到obj的绑定属于极晚绑定(very late binding), 绑定发生在调用的那一刻。这使得JavaScript函数在被重用时有极大的灵活性。
函数调用
当函数不是对象属性时,它就会被当做函数来调用,比如add(2,3)。 此时this绑定到了全局对象global。
其实this绑定到global是JavaScript的一个设计错误(可以说是最严重的错误), 它导致了对象方法不能直接调用内部函数来做一些辅助工作, 因为内不函数里的this的绑定到了global。 所以如果要重新设计语言,方法调用的this应该绑定到上一级函数的this。
然而共有方法总是需要调用内部辅助函数,于是产生了这样一个非常普遍的解决方案:
man.live= function(){
var self = this;
function eat(){
self.happy++;
}
function sleep(){
self.happy--;
}
eat() && sleep();
}
有些场景下用Function.prototype.bind会更加方便:
man.live= function(){
function eat(apple, rice, ...){
this.happy++;
}
eat.bind(this)();
...
}
构造函数调用
JavaScript中,那些用来new对象的函数称为构造函数。
JavaScript采用原型继承方式。这意味着一个对象可以从另一个对象直接继承属性, JavaScript是class free的~ 但JavaScript为了迎合主流的基于类声明的继承方式, 同时也给出了构造函数机制:使用new关键字,便会创建一个对象, 根据prototype属性创建原型链,并以该对象为this执行指定的(构造)函数。
function Man(name, age){
this.sex = 'man';
this.name = name;
this.age = age;
}
Man.prototype.funcAAA = function(apple, rice, ...){}
var man = new Man();
man.funcAAA ();
当构造函数有很多参数时,它们的顺序很难记住,所以通常使用对象进行传参:
var man = new Man({
name: 'bob',
age: 18
});
给参数起名字以达到顺序无关的做法在Python中也存在,但JavaScript的对象传参还将带来另一个好处: JSON兼容。因为JavaScript常常需要数据库(例如MongoDB)或网络(application/json)传来的JSON数据,这一点使得对象构造非常方便。
apply调用
JavaScript函数是一种特殊的对象,而对象可以有属性和方法。 其中的apply方法提供了一个更加特殊的调用方式。 它接受两个参数:第一个是该函数要绑定的this,第二个是参数数组。
var args = [apple1, apple2];
var animal = new Animal();
Man.prototype.eat.apply(animal, args);
Apply使得一个方法可以用不同的对象来调用,比如animal也可以用Man的方式来eat。