JavaScript之函数
JavaScript之函数
函数的定义方式
- 函数声明
- 函数表达式
- new Function
函数声明
function foo() {
}
函数表达式
var foo = function() {
}
两者之间的区别
- 函数声明必须要有名字
- 函数声明会函数提升, 在预解析就已经创建,声明前后都可以调用
- 函数表达式类似于变量赋值
- 函数表达式可以没有名字,匿名函数
- 函数表达式没有变量提升, 在执行阶段创建, 必须要在表达式执行之后才可以进行调用
demo
if (1){
function f() {
console.log(1);
}
}else {
function f() {
console.log(2);
}
}
以上代码执行结果在不同浏览器中结果不一致。为了解决则使用函数表达式
var f;
if (1){
f = function() {
console.log(1);
}
}else{
f = function() {
console.log(2);
}
}
函数的调用方式
- 普通函数
- 构造函数
- 对象方法
函数中this
指向不同场景
函数的调用方式决定了this
指向的不同:
调用方式 | 非严格模式 | 备注 |
---|---|---|
普通函数调用 | window | 严格模式下是 undefined |
构造函数调用 | 实例对象 | 原型方法中 this 也是实例对象 |
对象方法调用 | 该方法所属对象 | 紧挨着的对象 |
事件绑定方法 | 绑定事件对象 | |
定时器函数 | window |
函数也是对象
- 所有函数都是
Function
的实例
call、apply、bind
call
call() 方法调用一个函数,其中具有一个指定的this
值和分别提供的参数(参数的列表)
注意: 该方法的作用和apply()
方法类似, 只有一个区别, 就是call()方法接受的是 若干个参数的列表,而
apply()`方法接受一个或者多个参数的数组。
语法:
fun.call(thisArg[1, arg1[1, arg2[1,2]]])
参数:
- thisAry
- 在fun函数运行时指定的this值
- 如果制定了null或者undefined, 则内部this指向window
- arg1, arg2
- 指定的参数列表
apply
apply()
方法调用一个函数, 具体制定了一个this
值,以及作为一个数组(或类似于数组的对象)提供的参数。
注意: 该方法的作用和call()方法类似,只有一个区别,call()方法接受的是若干个参数的列表,而apply()
方法接受的 是一个包含多个参数的数组。
语法:
fun.apply(thisArg, [argsArray])
参数:
- thisArg
- argsArray
apply()与call() 非常相似,不同之处在于提供参数的方式。
apply()使用参数数组而不是一组参数列表。
fun.apply(this, ['eat', 'bananas'])
bind
bind()函数会创建一个新函数(称为绑定函数), 新函数和与被调函数(绑定函数的目标函数)具有相同的函数体(在es5中内置的call属性)。 当目标函数被调用的时 this 值绑定 bind() 的第一个参数, 该参数不能被重写。 绑定函数被调用,bind()也接受预设的参数提供给原函数。
一个绑定函数可以使用 new 操作符创建对象: 这种行为就像是吧原函数当做为解析器。 提供的 this 值忽略,同时调用时参数被提供给模拟函数。
语法:
fun.bind(thisArg[1, agr1[1]])
参数:
- thisArg
- 当绑定函数被调用时, 该参数会作为原函数运行是的 this 指向。当使用 new 操作符调用绑定函数时,该参数无效。
- arg1, arg2
- 当绑定函数被调用的时, 这些参数将置于实参之前传递被绑定的方法。
返回值:
返回有指定的 this值 和初始化参数改造的原函数的拷贝。
demo01
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和model里面的属性 x 迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回81
小结
- call和apply特性一样
- 都是用来调用函数,而且是立即调用
- 但是可以在调用函数的同时, 通过第一个参数指定函数内部的 this 的指向
- call调用时, 参数一参数列表进行传递,以逗号分隔的方式依次传递即可
- apply调用时, 参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
- 如果第一个参数指向了null,或者undefined则内部 this 指向余 window
- bind
- 可以用来指定内部 this 的指向, 然后生成一个改变 this 指向的新的函数
- 区别: bind不会调用
- bind支持传递参数,两个位置传递
- 在bind的同时,以参数列表的形式进行传递
- 在调用的时候,以参数列表的形式进行传递
- 那到底以谁 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 f() {
fn(10, 20, 30)
}
f()
高阶函数
- 函数可以作为参数
- 函数可以作为返回值
- setTimeout延时函数,里面是毫秒单位
作为参数
function eat(run) {
setTimeout(function() {
console.log("在吃饭");
run();
}, 3000)
}
eat(function() {
console.log("跑步");
})
作为返回值
function genFun() {
return function() {
var name = "胡珺";
return name;
}
}
var isArray = genFun();
console.log(isArray());
作用域,作用域链, 预解析
- 全局作用域
- 函数作用域
- 没有块级作用域
var a = 10;
function fn() {
var b = 20;
function fn1() {
var c = 30;
console.log(a+b+c);
}
function fn2() {
var d = 40;
console.log(c+d);
}
fn1();
fn2();
}
fn()
内层作用域可以访问外层作用域,反之不行
闭包
闭包就是能够读取其他函数内部变量的函数, 由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量, 因此可以把闭包简单理解成 “定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的用途:
- 可以在函数外读取函数内部成员
- 让函数内成员始终存活于内存中
demo1
var array = [11,22,33];
for (var i = 0; i < array.length; i++){
array[i] = function() {
console.log(i);
}
}
array[1]();
demo2
console.log(111);
for(var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i)
}, 0)
}
console.log(222)
递归函数
- 自己调用自己
计算阶乘的递归函数
function factorial (num) {
if (num <= 1) {
return 1
} else {
return num * factorial(num - 1)
}
}
factorial(4);