手记

JavaScript定义函数

基本概念

JavaScript中的函数使用function关键字来声明,后跟一组参数以及函数体。函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。

例子:

function demo(n) {
    return n + 1;
}
demo(1);
//输出:2

没有重载

如果函数中定义了两个名字相同的函数,则该名字只属于后定义的函数。

例子:

function demo(n) {
    return n + 1;
}

function demo(n) {
    return n + 2;
}
demo(1);
//输出:3

函数声明与函数表达式

创建函数最常用的两个方法是函数表达式和函数声明,两者在语法形式上的区别只有一点:函数声明必须带有标示符(即函数名),而函数表达式可以省略。

例子:

函数表达式

let fn1 = function(n1, n2) {
    console.log(n1 + n2)
};

函数声明

function fn2(n1, n2) {
    console.log(n1 + n2)
}

如果函数是作为赋值表达式的一部分,那它就是一个函数表达式:

例子:

let num = function(n1, n2) {
    return n1 + n2;
}(1, 1); 

let obj = new function(name) {
    this.name = name;
}("Tom");

另外,立即调用函数也属于函数表达式,因为括号运算符里面只能包含表达式:

例子:

//两种立即调用函数方式
(function(n1, n2) {
    console.log(n1 + n2);
}(1, 1));  //输出:2

(function(n1, n2) {
    console.log(n1 + n2);
})(1, 1);  //输出:2

如果函数被包含在一个函数体内,或者位于程序的最外部的话,那它就是一个函数声明:

例子:

//程序最外部
function fn1() {
    console.log("ok");
};

//函数内部
(function() {
    function fn2() {
        console.log("ok");
    }
})();

*区分函数声明和表达式最简单的方法是看 function 关键字出现在声明中的位置:如果 function 是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。

命名函数表达式

函数表达式可以省略函数名,但也可以拥有函数名,如果拥有函数名,这个名字只在当前函数作用域内有效。

例子:

let fn = function num() {
    console.log(typeof(num)); //输出:function
}
fn();
console.log(typeof(num)); //输出:undefined
console.log(typeof(fn)); //输出:function

在 ES5 的严格模式下,出于安全方面的考虑,arguments.callee(函数内部可用,指向当前函数)属性被废弃,因此可以使用命名函数表达式来实现递归。

例子:

//arguments.callee 实现递归
let fn1 = function(num) {
    if (num <= 0) {
        return 0;
    } else {
        return num + arguments.callee(num - 1);
    }
}
fn1(5); //输出:15

//命名函数表达式实现递归
let fn2 = function numFn(num) {
    if (num <= 0) {
        return 0;
    } else {
        return num + numFn(num - 1);
    }
}
fn2(5); //输出:15

函数声明优先

JavaScript 代码在执行前都要进行编译,编译阶段中的一部分工作就是找到所有的声明( var 和 function 声明),提升到当前作用域的最上面。提升的只是声明本身,而赋值或其他运行逻辑会留在原地等待执行。

例子:

fn1();
fn2();

function fn1() {
    console.log(1);
}
var fn2 = function() {
    console.log(2);
}

由于声明提升的存在,上面例子中的代码在编译阶段会被编译成下面的形式去执行:

function fn1() {
    console.log(1);
}
//默认 fn2 赋值为 undefined
var fn2;

fn1(); //输出:1
fn2(); //输出:TypeError

fn2 = function() {
    console.log(2);
}

*函数 fn2 由于在执行前没有赋值,而导致抛出 TypeError 异常。

命名函数表达式也无法在赋值之前使用

例子:

foo();
var fn = function foo() {
    console.log(1);
}

编译为:

var fn;
foo(); //输出:ReferenceError
fn = function foo() {
    console.log(1);
}

函数声明和变量声明的优先级

函数声明和变量声明都会被提升,但如果标识符一样,函数声明的优先级高于变量声明。

例子1:

fn();

var fn = function() {
    console.log(2);
}

function fn() {
    console.log(1);
}

编译为:

function fn() {
    console.log(1);
}

//由于函数声明优先级高,因此这个变量声明会被忽略
var fn;

fn(); //输出:1

fn = function() {
    console.log(2);
}

例子2:

var fn = function() {
    console.log(2);
}

function fn() {
    console.log(1);
}

fn();

编译为:

function fn() {
    console.log(1);
}

//由于函数声明优先级高,因此这个变量声明会被忽略
var fn;

//但到了这里,函数声明的标识符 fn 被重新赋值
fn = function() {
    console.log(2);
}

fn();  //输出:2

ES6中的箭头函数

除了函数声明与函数表达式,ES6 语法中可以使用“箭头”(=>)定义函数。

例子:

普通函数

var demo = function(n1, n2) {
    console.log(n1 + n2);
}
demo(1, 1);
//输出:2

箭头函数

var demo = (n1, n2) => {
    console.log(n1 + n2);
}
demo(1, 1);
//输出:2

注意

1、如果箭头函数只有一个参数,可以省略圆括号;如果没有参数或有多个参数,就需要使用一个圆括号代表参数部分。

例子:

没有参数

var demo = () => {
    console.log(2);
}
demo();
//输出:2

一个参数

var demo = n => {
    console.log(n);
}
demo(2);
//输出:2

多个参数

var demo = (n1, n2) => {
    console.log(n1 + n2);
}
demo(1, 1);
//输出:2

2、如果箭头函数的代码只有一条语句,可以省略大括号,默认是返回该语句的计算值;如果多于一条语句,就要使用大括号将它们括起来。

例子:

一条语句

var demo = (n1, n2) => n1 + n2;
demo(1, 1);
//输出:2

多条语句

var demo = (n1, n2) => {
    if (typeof n1 == "number" && typeof n1 == "number") {
        return n1 + n2;
    }
}
//输出:2

使用箭头函数简化回调函数

箭头函数最常用于回调函数。

例子:

普通匿名回调函数

[1, 2, 3].map(function(v) {
    return v + 1;
});
//输出:[2, 3, 4]

箭头函数简化匿名回调函数

[1, 2, 3].map(v => v + 1);
//输出:[2, 3, 4]

如有错误,欢迎指出。

3人推荐
随时随地看视频
慕课网APP