函数的定义方式
- 函数声明
function foo () { }
- 函数表达式
var foo = function () { }
- new Function(很少使用)
var fn = new Function('var name = "张三";console.log(name)'); //函数也是对象,所有函数都是 `Function` 的实例
函数声明与函数表达式的区别
- 函数声明必须有名字
- 函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
- 函数表达式类似于变量赋值
- 函数表达式可以没有名字,例如匿名函数
- 函数表达式没有变量提升,在执行阶段创建,必须在表达式执行之后才可以调用
函数的调用方式
- 普通函数
- 构造函数
- 对象方法
函数内this指向的不同场景
调用方式 | this指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
call、apply、bind
call
call()
方法调用一个函数,其具有一个指定的this
值和分别地提供的参数(参数的列表)- 注意:该方法的作用和
apply()
方法类似,只有一个区别,就是call()
方法接受的是若干个参数的列表,而apply()
方法接受的是一个包含多个参数的数组 - 语法
fun.call(thisArg[, arg1[, arg2[, ...]]])
- 参数
-
thisArg
- 在 fun 函数运行时指定的 this 值
- 如果指定了 null 或者 undefined 则内部 this 指向 window
-
arg1, arg2, ...
- 指定的参数列表
-
apply
apply()
方法调用一个函数, 其具有一个指定的this
值,以及作为一个数组(或类似数组的对象)提供的参数。- 注意:该方法的作用和
call()
方法类似,只有一个区别,就是call()
方法接受的是若干个参数的列表,而apply()
方法接受的是一个包含多个参数的数组。 - 语法:
fun.apply(thisArg, [argsArray]) 例如:fun.apply(this, ['eat', 'bananas'])
- 参数
- thisArg
- argsArray
bind
- bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。
- 当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。
- 一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
- 语法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
- 参数
- thisArg
- 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
- arg1, arg2, …
- 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
- thisArg
- 返回值:返回由指定的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'方法
小结
-
call 和 apply 特性一样
- 都是用来调用函数,而且是立即调用
- 但是可以在调用函数的同时,通过第一个参数指定函数内部
this
的指向 - call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
- apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
- 如果第一个参数指定了
null
或者undefined
则内部 this 指向 window
-
bind
- 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
- 它和 call、apply 最大的区别是:bind 不会调用
- bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
- 1.在 bind 的同时,以参数列表的形式进行传递
- 2.在调用的时候,以参数列表的形式进行传递
- 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
- 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部
函数的其他成员
- 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 eat (callback) { setTimeout(function () { console.log('吃完饭') callback() }, 1000) } eat(function () { console.log('去唱歌') })
- 作为返回值
function genFun (type) { return function (obj) { return Object.prototype.toString.call(obj) === type } } var isArray = genFun('[object Array]') var isObject = genFun('[object Object]') console.log(isArray([])) // => true console.log(isArray({})) // => true
函数闭包
什么是闭包
- 闭包就是能够读取其他函数内部变量的函数,
- 由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成 “定义在一个函数内部的函数”。
- 所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的用途
- 可以在函数外部读取函数内部成员
- 让函数内成员始终存活在内存中
- 闭包特点:延展了函数的作用域范围
闭包案例
function fn() {
var n = 10;
return function () {
return n;
}
}
var f = fn();
console.log(f());