在上一篇文章中,详细讲解了javascript是如何表达信息的,JavaScript如何处理信息.处理信息是任何语言的核心特性,掌握了这些特性那你就掌握了这门语言了.

我讲分成几个步骤去讲解,我画了个脑图如下:
我会从基本的函数的定义、函数的调用、函数的参数以及返回值,来讲解JavaScript的基本函数概念.
JavaScript 函数学习脑图

函数是神马

看如下代码,相信大家已经见的司空见惯了,函数的声明以及调用. 任何编程语言都存着函数,函数可以帮助我们复用代码,提高可读性,统一修改和维护.

       console.log(add(1, 2));
        /**
        * 函数可以复用代码
        */
        function add(num1, num2) {
            return num1 + num2;
        }
        add(2, 2);

我们需要认清JavaScript的函数的本质,在JavaScript中函数就是对象,在JavaScript中任何对象都可以添加属性和方法,那么JavaScript的函数同样可以添加属性和方法.

来看如下代码, 给add函数添加属性和方法,其实和给Object添加属性和方法一样的方式. 很简单就不用多说了.

 //JavaScript 中函数就是对象
       function add(num1, num2) {
            return num1 + num2;
        }
        //给函数添加属性和方法
        add.sex = '男';
        add.setSex = function (sex) {
            this.sex = sex;
        }
        console.log(add.sex);//男
        console.log(add.setSex('女'));//undefined 由于函数什么都没有返回,所以默认的都是undefined
        console.log(add.sex);//女
        console.log(add(1, 2));//3

将函数作为数据值来使用,直接看如下代码,函数可以直接赋给一个变量,text()就可以直接调用函数,如果直接text调用则会返回函数的本体即function(){return "atex"},函数的执行必须加(),如果不加返回的就是函数的本体. 同时函数可以在数组中和对象中声明,函数本质就是个对象,对象能干的事情它都能干.

      var text = function () {
            return "atex";
        }
        text();
        console.log(text());//atex
        console.log(text);//function(){return "atex"}
        //数组中可以 对象、函数
        [{}, function () { }, { family: {}, setName: function (name) { } }]
        //函数可以作为参数使用
        //函数名加()表示函数被执行,不加参数表示将函数的本体传入

函数可以作为返回值,代码如下:

       //函数作为返回值
        function fn() {
            return function () {
                console.log(1);
            }
        }
        var newFn = fn();
        //调用函数返回值的函数
        newFn();//1
        fn()();//1

注意,在JavaScript中没有块级作用域,如果在if/else中声明函数,不能达到按需定义函数的作用. 为什么呢,我们直接看代码示例:还记得我们在上一篇文章中讲解的预解析的问题,在JavaScript中函数的声明在预解析的时候就已经加载完毕了,所以在if/else中按需定义函数并不能起到作用.

//在预解析的时候就已经对声明函数加载了 并不能达到if/else按需 定义函数的作用
        if (true) {
            function add() {

            }
        } else {
            function sub() {

            }
        }

那么,如何能在if/else中按需加载函数呢? 我们可以这样做,将函数赋值给一个变量,var 变量在预解析的时候为undefined,然后在解析时就可以达到按需定义函数的目的,但是并不推荐这种方式.

//可以使用下面的方式,使if/else起到作用,因为 add sub 是变量,在预解析时就变为了undefined,但是并不推荐使用这种方式
        if (true) {
            var add = function () {

            }
        } else {
            var sub = function () {

            }
        }

匿名函数这个一个重要的概念,JavaScript在面向对象的编程中:继承、封装都会用到匿名函数. 我会在下一篇文章中,详细的讲解匿名函数,代码如下:

 //匿名函数头部需要存在合法的值 才可以自执行
        //匿名函数定义直接执行
        var s = function () {
            console.log('');
        }();

        //匿名函数的自执行 
        (function () {

        })();
        (function () {

        }());
        //匿名函数也可以这样用
        !+function () {
            console.log('1');
        }();
        //也可以这样用
        console.log(function () {
            return 1;
        }());

函数的调用:递归调用、链式调用、方法调用

/递归调用
        //计算阶乘
        function fn1(num) {
            if (num <= 1) return 1;
            return num * fn1(num - 1);
        }

        console.log(fn1(5));

        //方法的调用
        var option = {
            add: function (num1, num2) {
                return num1 + num2;
            },
            sub: function (num1, num2) {
                return num1 - num2;
            }
        }
        option.add(1, 2);
        option['add'](1, 2);
        var key = 'add';
        option[key](2, 2);

        //链式调用
        var option = {
            add: function (num1, num2) {
                console.log(num1 + num2);
                return this;
            },
            sub: function (num1, num2) {
                return this;
            }
        }
        option.add(1, 2).sub(3, 2);

同时还有构造函数的调用、间接调用. 关注一下call和apply这两个关键字,每个函数都存在这两个关键字,非常重要,后期详细讲解.

//构造函数的调用

        function Person(){

        }
        //是不是通过new 来调用构造函数
        var obj = new Person();

        //间接调用
        // function add(){

        // }
        // //每个函数下都有两个方法
        // add.call
        // add.apply

        var name = 'xm';
        var person = {};
        person.name = 'xh';

        person.getName = function(){
            return this.name;
        }

        console.log(person.getName());//xh 
        console.log(person.getName.call(window));//xh call 改变this的指向 this不指向了person 而是指向window


        function adds(num1,num2){
            return num1 + num2;
        }
        var data = [1,2];
        console.log(adds(1,2));
        console.log(adds.call(window,1,2));//add 中并没有用到this window 只是改变this的值
        console.log(adds.apply(window,data));//apply 和call的区别 就是传递参数是个数组

可以通过函数,给对象动态的设置属性.

 //动态设置属性
        var person = {
            setPerson: function (property, value) {
                person[property] = value
            }
        };

        person.setPerson('name', 'xm');
        person.setPerson('age', 18);
        person.setPerson('sex', 'male');

看下图,这是对函数定义的一个总结:

关于函数调用的一个总结:

函数的参数

关于函数的参数也是很简单的,我们需要明白函数的参数本质: 形参 = 实参

函数的参数的个数

   function add(num1, num2) {
            return num1 + num2;
        }
        add(1, 2);
        add(1);//num1 = 1 ; num2 = undefined
        //可选参数
        function pow(base, pow) {
            pow = pow || 2;
            return Math.pow(base, pow);
        }
        console.log(3);
        console.log(3,2);

可以通过arguments,来获取参数,arguments也是一个非常重要的关键字,arguments 是类似数组的对象.如下:

var arguments =  {
            '0':1,
            '1':2,
            'length':2
        }

通过arguments来获取参数,注意的是add.length是形参的个数,arguments.length是实参的个数.

        function add(){
            if(arguments.length === 0) return;
            var nums = 0;
            for (let index = 0; index < arguments.length; index++) {
                const element = arguments[index];
                nums += element;
            }
            return nums;
        }
        console.log(add(1,2,3,4,5));

注意 arguments 要谨慎使用,如果改变了arguments中的某个参数的值,那么这个参数也就被修改了,如果函数fn中,嵌套函数fn2,fn中的arguments发生改变,但是并不影响fn2中的arguments,每个函数都有一个独特的arguments.

       function fn(name){
            arguments[0] = '';
            console.log(name);//'' name值也发生变化了
            function fn2(){//每个函数都有一个独特的arguments
                console.log(arguments);//fn2 的自己的arguments
            }
        }

扩展 arguments.callee 返回当前函数的本体.

  //arguments.callee 函数的本体 可以用到在递归调用中
        //场景如下:
        //如果函数jiecheng 内部过于复杂,用到了递归,某天改掉了函数名,还要将每个递归调用的函数名都要改掉
        //用arguments.callee 就是当前函数的本体,这样就不用在手动去改掉函数名了.
        // "use strict"//严格模式下 不允许使用arguments.callee 如何修改呢?
        function jiecheng(num){
            if(num <= 1) return 1;
            return num * arguments.callee(num - 1);
        }

        console.log(jiecheng(5));

        严格模式下 可以这样使用
       var jiecheng =  function fn(num){
            if(num <= 1) return 1;
            return num * fn(num - 1);
        }
        console.log(jiecheng(5));

老规矩,通过脑图来总结一下函数的参数

本篇文章,只是讲解了JavaScript的基本知识,函数的深入和运用会在下一篇的文章中\<JavaScript面向对象>深入讲解函数.