1 函数定义
- 在JavaScript中,函数也是对象,是Function的实例。
- 我们可以将函数当作对象使用,包括当作函数的参数等。
typeof getSum; //"function"
- 函数有属性和方法。
- 函数内部的变量arguments是参数数组。
- 函数难点在于闭包、原型链等。
1.1 函数声明
function funName(parameters){
//...
}
1.2 函数表达式
1 函数表达式实际上是创建一个匿名函数,然后存储在变量中。
var getSum = function(a, b){
return a + b;
}
2 函数表达式可以包含名称,在递归时很有用
var f = function fact(x){
return x * fact(x-1);
}
1.3 Function构造函数
var sum = new Function("num1", "num2", ..., "return num1 + num2");
2 函数返回值
- 函数体中遇到return则停止执行。
- 若return后有值或表达式,则返回。
- 若return后无值或表达式,或函数中没有return,则返回undefined。
3 函数提升
- 函数声明会被移动到其作用域的顶端。
- 函数声明的调用可以在函数声明之前。
- 函数表达式无法提升。
4 函数内部对象
4.1 arguments
- arguments是一个包含函数实参列表的对象,长度可超过形参个数和。
- arguments的callee属性指向拥有该arguments对象的函数,便于递归调用。
- 严格模式下禁用callee属性。
4.2 this
- 默认情况下,this指向调用函数的对象。
- 若想改变默认情况this指向,则需使用call()或apple()方法。
- 在全局作用域下创建函数,非严格模式,函数调用上下文是全局对象,严格模式则是undefined,可用于确定是否严格模式。
var strict = (function(){return !this})();
this指针问题
1. 当函数作为方法调用,则this指向调用它的对象。
2. 当函数独立调用,this指向window(非严格模式)/undefined(严格模式)
3. 当this在对象属性的表达式中调用,若对象在全局声明,this指向全局对象。
4. 当this在对象属性的表达式中调用,若对象非全局声明,this指向window(非严格模式)/undefined(严格模式)
4.3 caller
函数对象属性caller指向调用当前函数的函数。
var outer = function(){
inner();
}
var inner = function(){
console.log(inner.caller); //function(){inner();}
}
- 控制台会输出caller指向的函数对象的源代码。
- 当在全局作用域中调用当前函数,caller属性值为null。
- 严格模式下就能用caller属性
5 作为命名空间的函数
- 因为函数包含作用域,所以将临时变量放在函数中,防止全局污染。
- 这些临时变量只会使用一次,所以将这些变量放在自执行函数中,内部返回目标对象。如果需要就用变量指向返回对象,如果不需要则自动垃圾回收。
- 自调用函数其实就是将匿名函数当作一个函数对象,然后调用。
(function(){
return 3 + 5;
})();
如果上面的代码看起来有点迷糊,那么拆分来看:
//首先我们将匿名函数赋值给变量,形成一个函数表达式:
var getSum = function(){
return 3 + 5;
}
//然后我们调用getSum方法:
getSum();
//还看不明白?没关系 我们把上面一行代码的setSum再换回匿名函数
(匿名函数)();
//到这里是否明白了呢?
6 函数的方法
6.1 toString()
函数的toString方法返回值为函数声明的代码字符串。
var fun = function(){
return 3 + 5;
}
console.log(fun.toString()); //function(){//...};
6.2 call()
- call方法用来替换方法内部的this指向。正常调用函数,函数内部this指向调用函数的对象;使用call方法可以指定函数内部this指向的对象。
- 传入的第一个参数即函数内部this指向的对象。
- 如果不传参数,默认指向全局对象。
funObj.call(thisObj, arg1, arg2,...)
6.3 apply()
- apply方法与call方法其实相同,不过只有两个参数,第二个参数是方法实际参数的数组集合。
funObj.apply(thisObj, arrayArgs);
6.4 巧用apply方法
当某方法需要传多个参数,但多个参数是存在数组中时,可以用此方法传参:
var fun = function(a,b,c,d){
return a + b + c + d;
}
var args = [1,2,3,4];
console.log(fun.apply(this,args));
6.5 bind()
语法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
6.5.1 用法
例子:
//a.js
var foo = {
bar: 1,
eventBind: function () {
var _this = this;
$('.someClass').on('click', function (event) {
/* Act on the event */
console.log(_this.bar); //1
});
}
}
上面例子中的eventBind中的click事件内无法直接访问foo对象内部的this,所以我们将this赋值给中间层_this,再从事件中调用_this。
简化一下:
//b.js
var foo = {
bar: 1,
eventBind: function () {
$('.someClass').on('click', function (event) {
/* Act on the event */
console.log(this.bar); //1
}.bind(this));
}
}
这里我们取消了中间临时变量_this,而直接在事件绑定中函数参数的末尾使用bind方法,将指向foo对象的this传给band方法,替换掉了事件内的this指向。
另外bind方法不能被覆盖,即
fun.bind(a).bind(b).bind(c)...
//只会绑定a,后面b、c的bind均不会生效。
6.5.2 与call/apply比较
与call/apply比较:
1. apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
2. apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
3. apply 、 call 、bind 三者都可以利用后续参数传参;
4. bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
根据上面区别我们就可以根据实际情况选择使用call/apply还是bind的。
这里有更详细的解释:http://www.cnblogs.com/coco1s/p/4833199.html
6.5.3 偏函数
使用bind()我们设定函数的预定义参数,然后调用的时候传入其他参数即可:
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
// Create a function with a preset leading argument
var leadingThirtysevenList = list.bind(undefined, 37);
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
6.5.4 配合 setTimeout
一般情况下setTimeout()的this指向window或global对象。当使用类的方法时需要this指向类实例,就可以使用bind()将this绑定到回调函数来管理实例。
function LateBloomer() {
this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
console.log('I am a beautiful flower with ' +this.petalCount + ' petals!');
};
var flower = new LateBloomer();
flower.bloom(); // 一秒钟后, 调用'declare'方法
6.5.5 绑定函数作为构造函数
绑定函数也适用于使用new操作符来构造目标函数的实例。当使用绑定函数来构造实例,注意:this会被忽略,但是传入的参数仍然可用。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return this.x + ',' + this.y;
};
var p = new Point(1, 2);
p.toString(); // '1,2'
var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);
// 实现中的例子不支持,
// 原生bind支持:
var YAxisPoint = Point.bind(null, 0/*x*/);
var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'
axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new Point(17, 42) instanceof YAxisPoint; // true
更多
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
7 函数的属性
7.1 length
- length属性表示函数希望接收的命名参数的个数。
7.2 prototype
- prototype属性无法枚举,不可使用for-in。
7.3 name
- name属性可获取函数名,不管是函数声明还是函数表达式,都可以使用fun.name获取函数的名字。
8 构造函数调用
如果函数或方法调用之前带有关键字new,就构成了构造函数调用。构造函数调用和普通函数调用以及方法调用,在实参处理、调用上下文和返回值方面都有所不同。
- 若构造函数无参数,则可直接省略函数后的()。
var o = new Object;
- 调用构造函数创建新对象,将此对象作为其调用上下文,所以在构造函数中使用this指向新对象。
- 构造函数隐式return新对象,若显式使用return返回一个对象,则返回这个对象;若return没有指定返回值,则返回this指向的新对象。
function Fun() { }
var obj = new Fun();
console.log(obj.__proto__ == fun.prototype); //true