一、函数
函数实际上是对象,每个函数都是Function类型的实例,而Function也有属性和方法,因为函数是对象,所以函数名就是指向函数对象的指针。
函数名就是指向函数的指针,,所以他们跟其他包含对象指针的变量具有相同的行为,这意味着一个函数可以有多个名称
1、函数声明方式
function sum(){}
2、函数表达式
let sum = function(){}
3、使用Function构造函数,最后一个参数被当成函数体
不推荐使用这种语法来定义函数
let sum = new Function("num1","num2","return num1+num2");
4、箭头函数
=>使用胖箭头语法定义函数表达式。
任何可以使用函数表达式的地方,都可以使用箭头函数。
如果有一个参数,那也可以不用括号,多个参数或者没有参数的情况下,才需要使用括号。
箭头函数不能使用arguments、super、和new.target,也不能用作构造函数,箭头函数没有prototype属性。
只能通过定义的命名参数访问。
没有重载。
重复定义,第二个定义覆盖第一个定义。
5、递归
递归函数通常的形式是一个函数通过名称调用自己,如下面的例子所示:
function factorial(num){
if(num<1){
return 1;
}else{
return num*factorial(num-1)
}
}
这是经典的递归阶乘函数。但是如果把这个函数复制给其他变量,就会出问题。
let anotherFactorial = factorial;
factorial =- null;
console.log(anotherFactorial(4));//报错
这里把factorial函数赋值在了另一个变量anotherFactorial中,然后factorial被设置为null,于是只保留了一个对原始函数的引用,而在调用anotherFactorial时,要递归调用factoria(),但因为它已经不是函数了,所以会出错。 可以使用arguments.callee就是一个指向正在执行的函数的指针,因为可以在函数内部递归调用。
function factorial(num){
if(num<1){
return 1;
}else{
return arguments.callee(num-1)
}
}
不过在严格模式下运行的代码是不能访问arguments.callee的,因为访问会出错。 可以使用命名函数表达式达到目的
const factorial = (function f(num){
if(num <= 1){
return 1;
}else{
return num*f(num-1);
}
})
这里创建了一个命名函数表达式f(),然后将它赋值给变量factorial。即使把函数赋值给另一个变量,函数表达式的名称f也不变,因此递归调用不会有问题,这个模式在严格模式和非严格模式下都可以使用。
二、闭包
闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。
调用一个函数时,会为这个函数调用创建一个执行上下文,并创建一个作用域链,然后用arguments和其他命名参数来初始化这个函数的活动对象,外部函数的活动对象是内部函数作用域链上的第二个对象。这个作用域链一直向外串起了所有包含函数的活动对象,知道全局执行上下文才终止。
闭包中this对象
在闭包中使用this会让代码变复杂,如果内部函数没有使用箭头函数定义,则this对象会在运行时绑定到执行函数的上下文,如果在全局函数中调用,则this在不严格模式下等于window,在严格模式下等于undefined。如果作为某个对象的方法调用,则this等于这个对象。匿名函数在这种情况下不会绑定到某个对象,这就意味着this会指向window,除非在严格模式下this是undefined。
window.identity = "The Window";
let object = {
identity:"My Object",
getIdentityFunc(){
return function(){
return this.identity;
}
}
}
console.log(object.getIdentityFunc()());
如果把this保存到闭包可以访问的另一个变量中,则是行得通。
window.identity = "The Window";
let object = {
identity:"My Object",
getIdentityFunc(){
let that = this;
return function(){
return that.identity;
}
}
}
console.log(object.getIdentityFunc()());
三、立即调用后的匿名函数
又被称作立即调用的函数表达式。它类似于函数声明,但由于被包含在括号中,所以会被解释为函数表达式。
(function(){
//块级作用域
})()
let count = 10;
(function(){
for(var i=0;i<count;i++){
console.log(i);
}
})();