1 函数的定义方式
方式1:函数声明
方式2:函数表达式
方式3:`new Function`
1.1 函数声明
function foo () {
}
1.2 函数表达式
var foo = function () {
}
1.3 函数声明与函数表达式的区别
函数声明必须有名字
函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
函数表达式类似于变量赋值
函数表达式可以没有名字,例如匿名函数
函数表达式没有变量提升,在执行阶段创建,必须在表达式执行之后才可以调用
下面是一个根据条件定义函数的例子:
if (true) {
function f () {
console.log(1)
}
} else {
function f () {
console.log(2)
}
}
以上代码执行结果在不同浏览器中结果不一致。
不过我们可以使用函数表达式解决上面的问题:
var f
if (true) {
f = function () {
console.log(1)
}
} else {
f = function () {
console.log(2)
}
}
2 函数的调用方式
普通函数
构造函数
对象方法
3 函数内 `this` 指向的不同场景
函数的调用方式决定了 `this` 指向的不同:
调用方式 | 非严格模式 | 备注 |
普通函数调用 | window | 严格模式下是 undefined |
构造函数调用 | 实例对象 | 原型方法中 this 也是实例对象 |
对象方法调用 | 该方法所属对象 | 紧挨着的对象 |
事件绑定方法 | 绑定事件对象 | |
定时器函数 | window |
这就是对函数内部 this 指向的基本整理,写代码写多了自然而然就熟悉了。
4 函数也是对象
所有函数都是 `Function` 的实例
5 call、apply、bind
那了解了函数 this 指向的不同场景之后,我们知道有些情况下我们为了使用某种特定环境的 this 引用,
这时候时候我们就需要采用一些特殊手段来处理了,例如我们经常在定时器外部备份 this 引用,然后在定时器函数内部使用外部 this 的引用。
然而实际上对于这种做法我们的 JavaScript 为我们专门提供了一些函数方法用来帮我们更优雅的处理函数内部 this 指向问题。
这就是接下来我们要学习的 call、apply、bind 三个函数方法。
5.1 call
`call()` 方法调用一个函数, 其具有一个指定的 `this` 值和分别地提供的参数(参数的列表)。
<p class="danger">
注意:该方法的作用和 `apply()` 方法类似,只有一个区别,就是 `call()` 方法接受的是若干个参数的列表,而 `apply()` 方法接受的是一个包含多个参数的数组。
</p>
语法:
fun.call(thisArg[, arg1[, arg2[, ...]]])
参数:
- `thisArg`
+ 在 fun 函数运行时指定的 this 值
+ 如果指定了 null 或者 undefined 则内部 this 指向 window
- `arg1, arg2, ...`
+ 指定的参数列表
5.2 apply
`apply()` 方法调用一个函数, 其具有一个指定的 `this` 值,以及作为一个数组(或类似数组的对象)提供的参数。
<p class="danger">
注意:该方法的作用和 `call()` 方法类似,只有一个区别,就是 `call()` 方法接受的是若干个参数的列表,而 `apply()` 方法接受的是一个包含多个参数的数组。
</p>
语法:
fun.apply(thisArg, [argsArray])
参数:
- `thisArg`
- `argsArray`
`apply()` 与 `call()` 非常相似,不同之处在于提供参数的方式。
`apply()` 使用参数数组而不是一组参数列表。例如:
fun.apply(this, ['eat', 'bananas'])
5.3 bind
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。
当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
语法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
参数:
- thisArg
+ 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
- arg1, arg2, ...
+ 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
返回值:
返回由指定的this值和初始化参数改造的原函数拷贝。
示例1:
this.x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 返回 81
var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域
// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81
示例2:
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'方法
5.4 小结
- call 和 apply 特性一样
+ 都是用来调用函数,而且是立即调用
+ 但是可以在调用函数的同时,通过第一个参数指定函数内部 `this` 的指向
+ call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
+ apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
+ 如果第一个参数指定了 `null` 或者 `undefined` 则内部 this 指向 window
- bind
+ 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
+ 它和 call、apply 最大的区别是:bind 不会调用
+ bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
* 1. 在 bind 的同时,以参数列表的形式进行传递
* 2. 在调用的时候,以参数列表的形式进行传递
* 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
* 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部
6 函数的其它成员
- arguments
+ 实参集合
- caller
+ 函数的调用者
- length
+ 形参的个数
- name
+ 函数的名称
function fn(x, y, z) {
console.log(fn.length) // => 形参的个数
console.log(arguments) // 伪数组实参参数集合
console.log(arguments.callee === fn) // 函数本身
console.log(fn.caller) // 函数的调用者
console.log(fn.name) // => 函数的名字
}
function f() {
fn(10, 20, 30)
}
f()