JavaScript函数

2020-11-20  本文已影响0人  云凡的云凡

一、函数的作用

耦合:各个代码块重复的代码太多了。

        if (3 > 0) {
            for (var i = 0; i < 10; i++) {
                console.log(i);
            }
        }
        if (2 > 0) {
            for (var i = 0; i < 10; i++) {
                console.log(i);
            }
        }
        if (1 > 0) {
            for (var i = 0; i < 10; i++) {
                console.log(i);
            }
        }

高内聚(开发的一个功能,称之为一个模块,它的代码相关性强,也就是它内部的代码紧密联系度比较强,紧密联系度比较强就会让这个模块的独立性强。希望一个模块独立的去完成一个任务,而这个任务完成的好坏是跟高内聚有关系的)。低耦合(希望把重复的代码提取出来,提取出来以后组成一个独立的模块去完成一个特定的功能)。
目的是让一个功能体(模块)有 强的功能性,高的独立性。
就是要用的时候直接调用。(模块的单一责任制)

重复性的代码太多就要解耦合,解耦合最好的方式就是函数。当然函数有很多种,也有很多种写法,有很多种解耦合的,但都是基于函数的。

        var x = 5

        if (x > 0) {
            test()
        }
        if (x > 5) {
            test()
        }
        if (x > 8) {
            test()
        }
        function test() {
            for (var i = 0; i < 10; i++) {
                console.log(i);
            }
            console.log(x + " window"); //5 window
        }

二、函数的声明

       // 最基本的函数写法--函数声明
        function test(参数) {
            //函数的执行语句
        }
        // 只有被调用才会执行
        test()
    function test() {
            // a 是局部变量,b 相对于全局变量
            var a = b = 1  //不正确
            console.log(a, b);  //1 1
        }
        test()
        console.log(b); //1
    // 表达式 字面量
        var test = function test() {
            var a=2, b = 1
            console.log(a, b);  //2 1
        }
        test()
     //会报错:为什么?声明了一个变量把test1赋值给test的时候,就把test赋予了一个函数的功能,所以调用test
//的时候能够正确的执行,而test1会自动忽略,test1这里写与不写对于test这个函数的执行是没有影响的。
//但是函数内部可以调用test1(递归)
       var test = function test1() {
            var a=2, b = 1
            console.log(a, b);  //2 1
        }
        console.log(test.name); //test1
        test1()  //Uncaught ReferenceError: test1 is not defined
      // 递归
        var test = function test1() {
            var a = 2, b = 1
            console.log(a, b);  //2 1
            test1()
        }
        console.log(test.name); //test1
        test()
  // 字面量
        var a={} //对象字面量、数组字面量、数字字面量...

三、函数的组成部分

// 组成部分
      function 函数名 参数(可选) 返回值(可选)  return

形参和实参可以不相等,
函数内部知道自己有哪些实参

    function test(a, b, c) {
            console.log(test.length);      //3         形参
            console.log(arguments);       //Arguments(2)  实参
            console.log(arguments[1]);    // 2    第二个实参
        }
        test(1, 2)
// 一个函数被调用时,累加它的实参值
        function test() {
            var a = 0
            for (var i = 0; i < arguments.length; i++) {
                a += arguments[i]
            }
            console.log(a);  //3
        }
        test(1, 2)
  //实参求乘积
        function test() {
            var a = 1
            for (var i = 0; i < arguments.length; i++) {
                a *= arguments[i]
            }
            console.log(a);  //2.3
        }
        test(1, 2.3)
 //函数里面可以更改实参的值
        function test(a, b) {
            a = 10
            console.log(a);//10
            console.log(a + b);//12
            // 实参多于形参,则会undefined
            console.log(c); //Uncaught ReferenceError: c is not defined
        }
        test(1, 2, 3)
      function test(a, b) {
            b = 10
            // 在实参里没有传入这个值,这个值就是undefined
            console.log(arguments[1]);  //undefined
        }
        test(1)

总结:在实参里面传了值的,可以在函数内部修改这个实参值,如果在实参里并没有传这个值,你给这个形参赋值是没有用的

    function test(a, b) {
            a = 3
            console.log(arguments[0]);
        }
        console.log(1, 2);
function test(a, b) {
            b = 3
            console.log(arguments[1]);  //undefined  在实参没有传入这个值,那么这个值就是undefined
            console.log(b === arguments[1]);  //false
        }
        test(1)

总结:在实参里面传了值的,可以在函数内部去修改这个实参的值,如果在实参没有传入这个值,你给这个形参赋值是没有用的。

function aaa(x, y) {
            x = 3
            console.log(arguments[0]); //3??? 不应该是undefined 吗,为什么我的是3
            // a 和 arguments[0]不是同一个变量产生的值
            // 因为a存在栈内存,arguments存在堆内存,
            // 实参和形参虽然不是同一个东西,但是它们在系统内部,在函数的基础上其实是一个映射关系
            console.log(x === arguments[0]);  //true
        }
        aaa(1, 2)

四、函数参数默认值

   // 参数默认值不设置就是undefined
        function test(a, b) {
            console.log(a, b);  //1 undefined
        }
        test(1)
     //a 用默认值,b 传入实参
        function test(a = 1, b) {
            console.log(a, b); //1 2
        }
        test(undefined, 2)  //用 undefined 占位

//一样的

        function test(a = undefined, b) {
            console.log(a, b); //1 2
        }
        test(1, 2) 

总结:谁不是undefined就找谁,,如果都是undefined,就是undefined,这也是映射关系,在形参赋值(a = 1,es6的写法。es5是不可以这样写的)一般浏览器是不兼容的。

   function test(a, b) {
            var a = arguments[0] || 1
            var b = arguments[1] || 2
            console.log(a + b);  //3
        }
        test()

//有实参时
        function test(a, b) {
            var a = arguments[0] || 1  //这种写法更好点
            var b = arguments[1] || 2
            console.log(a + b);  //10
        }
        test(4, 6)
        function test(a, b) {
            var a, b;
            if (typeof (arguments[0]) !== 'undefined') {
                a = arguments[0]
            } else {
                a = 1
            }
            // 如果 arguments[1]) 传了实参,那么就用传入的实参 6
            if (typeof (arguments[1]) !== 'undefined') {
                b = arguments[1]
            } else {
                b = 2
            }
            console.log(a, b);  //4 6
        }
        test(4, 6)

//---------------------------------------------------

function test(a, b) {
            var a, b;
            if (typeof (arguments[0]) !== 'undefined') {
                a = arguments[0]
            } else {
                a = 1
            }
            // 如果 arguments[1]) 没有传入实参,那么就用默认值 1
            if (typeof (arguments[1]) !== 'undefined') {
                b = arguments[1]
            } else {
                b = 2
            }
            console.log(a, b);  //1,2
        }
        test()

//用三目运算写
  function test(a, b) {
            var a = typeof (arguments[0]) !== 'undefined' ? a = arguments[0] : a = 1
            // 如果 arguments[1]) 没有传入实参,那么就用默认值 1
            var b = typeof (arguments[0]) !== 'undefined' ? b = arguments[0] : b = 2
            console.log(a, b);  //1,2
        }
        test()

五、函数返回值

image.png

         //   return 的问题
        function test() {
            console.log('我正在执行');
            console.log('我执行完了就结束这个函数'); 
       //确实是走到这就结束了,但js引擎会默认添加 return;
       //所以最后一条语句其实是 return;
        }
        test()

    //其实是这样的,每个函数最后一条语句都是 return;
 
       function test() {
            console.log('我正在执行');
            console.log('我执行完了就结束这个函数');
            return;
        }
        test()

return; 的用法,终止函数的执行

image.png
    function test() {
            console.log('我正在执行');
            // return; 放到哪里,return;之后的语句就不会执行了
            return;
            console.log('我执行完了就结束这个函数');
        }
        test()
     function test(name) {
            if (!name) {
                return;
            }
            console.log(name);  //li
        }
        test('li')

不仅仅可以终止函数执行,还可以返回一个变量,返回一个变量还是字符串,数字,null,undefined,function等等都可以

  function test(name) {
            if (!name) {
                return '您没有填写姓名!';
            }
            return name
        }
        console.log(test('li'));  //li

 function test(name) {
            if (!name) {
                return '您没有填写姓名!';
            }
            return name
        }
        console.log(test());  //您没有填写姓名!
 function test(name) {
            if (!name) {
                console.log('您没有填写姓名!');  //您没有填写姓名!
            }
            console.log(name);  //undefined
        }
        test(); 


   // 在里面 return;
 function test(name) {
            if (!name) {
                console.log('您没有填写姓名!');  //您没有填写姓名!
                return;
            }
            console.log(name);  
        }
        test(); 

    //用三目运算
     function test(name) {
            return name = !name ? '您没有填写姓名!' : name
        }
        console.log(test());  //您没有填写姓名!

  //用或运算
  function test(name) {
            return name = '您没有填写姓名!' || name
        }
        console.log(test());  //您没有填写姓名!

六、全局变量与函数

 b = 2
        function test() {
            var a = 1
            console.log(b); //2
        }
        test()
        console.log(a); //Uncaught ReferenceError: a is not defined
// 外面的可以访问里面的,里面的不能访问外面的 ,[[scope] 域的问题
        a = 1  //全局变量
        function test() {
            var b = 2  //局部变量
            console.log(a);//1
            function test2() {
                var c = 3;//局部变量
                console.log(b);//2
            }
            test2()
            console.log(c); //c is not defined
        }
        test()

对于外面的变量,里面的取值和赋值都可以

 <script>
        a = 1
        function test() {
            var b = 2
            a = 4
            function test2() {
                var c = 3;
            }
            test2()
            console.log(a); //4
        }
        test()
        console.log(a); //4

    </script>
  <script>
        a = 1
        function test() {
            var b = 2
            a = 4
            function test2() {
                var c = 3;
                a=5
            }
            test2()
            console.log(a); //5
        }
        test()
        console.log(a); //5

    </script>
        a = 1
        function test() {
            var b = 2
            a = 4
            function test2() {
                var c = 3;
                a=5
                console.log(b);  //2
            }
            test2()
            console.log(a); //5
        }
        test()
        console.log(a); //5

function 有自己独立的作用域,也就是声明了的变量它的可访问范围

        function test() {
            var a = 1
            console.log(b); //b is not defined
        }
        function test2() {
            var b = 2
            console.log(a);
        }
        test()
        test2()

 function test() {
            var a = 1
            console.log(a); //1
        }
        function test2() {
            var b = 2
            test()
            function test3() {
                c = '我是test3'
                console.log(c);  
            }
        }
        test2()
        test3()   //test3 is not defined

函数就是一个固定的功能或者程序段被封装的过程,实现一个固定的功能或者程序,在这个封装体中需要一个入口和一个出口,入口就是参数,出口就是返回。

 function test(str1, str2, str3) {
            console.log(str1);
            console.log(str2);
            console.log(str3);
        }
        test('元宵节', '端午节', '劳动节')
  function test(str1, str2, str3) {
            return str1
            return str2
            return str3
        }
        console.log(test('元宵节', '端午节', '劳动节'));

七、函数递归: 第一步 找到规律, 第二步 找到出口

  // n的阶乘
        // var num = 1
        // for (var i = 1; i <= 5; i++) {
        //     num = num * i
        // }
        // console.log(num);
        
        // n 的阶乘 -> 不能用for循环 ,用递归
        // n! = n * (n-1)
        function fact(n) {
            if (n === 1) {
                return 1
            }
            return n * fact(n - 1)
        }
        console.log(fact(5));  //120
       function fb(n) {
//第二步 找到出口
            if (n <= 0) {
                return 0
            }
            if (n <= 2) {
                return 1
            }
// 第一步 找到规律,
            return fb(n - 1) + fb(n - 2)
        }
        console.log(fb(6)); //8
        console.log(fb(3)); //2

八、立即执行函数(IIFE-immediately-incoked function expression)和它的小秘密

自动执行,执行完成以后立即释放
写不写函数名是一样的,执行完成会立即销毁

// 两种写法
        (function () {

        })()
            // 一样的
            (function () {

            }())  //w3c建议

仍然可以传参数

 (function (a, b) {   //传形参
            console.log(a + b);  //9
        }(4, 5))  //传实参

返回值的问题:立即执行函数也是有返回值的,把返回值交给一个变量

  var num = (function (a, b) {   //传形参
            return a + b; 
        }(4, 5))  //传实参
        console.log(num);   //9

// 括号括起来的就是表达式
        (function () { }())
        (function test() { })
        (function () { })()
  //一定是表达式才能被执行符号执行
        (function test1() {
            console.log('test1');//test1
        })()

        var test2 = function () {
            console.log('test2'); //test2
        }()

        // function test3() {
        //     console.log('test3');
        // }()

**函数声明变成表达式的方法 ! || + - && **

  + function test() {
            console.log(1);
        }()
        console.log(test);  //test is not defined

  0 || function test() {
            console.log(1);  //1
        }()

九、函数的call、apply方法

       function test() {
            console.log(1);  //1
        }
        // 系统隐式给test添加了.call()
        test.call()

        function Test(id, name) {
            this.id = id
            this.name = name
        }
        var newTest = {
            phone: '123456'
        }
        console.log(newTest);//{phone: "123456"}

        Test.call(newTest, '001', 'mary')  //把 Test 的 this 改变了,指向了 newTest
        // call apply只有一个区别,作用都是更改 this 指向
        // Test.apply(newTest, ['001', 'mary'])  
        console.log(newTest);//{phone: "123456", id: "001", name: "mary"}
        var test = new Test('002', 'jack')
        console.log(test);//{id: "002", name: "jack"}

call、apply好处:1.补充不完整的方法 2.分组来写,再去调用构造函数的属性和方法 3.分类来写,再汇总

   function Compute() {
            this.plus = function (a, b) {
                console.log(a + b);   //3
            }
            this.minus = function (a, b) {
                console.log(a - b);
            }
        }
        function FullCompute() {
            Compute.call(this)
            this.mul = function (a, b) {
                console.log(a * b);
            }
            this.div = function (a, b) {
                console.log(a / b);
            }
        }
        var compute = new FullCompute()
        compute.plus(1, 2)
        

十、callee 与 caller

callee
  function test(a, b, c) {
            console.log(arguments.callee);// 返回正在被执行的函数对象
            console.log(arguments.callee.length);//返回 实参列表所对应的函数
            console.log(arguments.callee.length === test.length);  //true
            console.log(test.length); //3 打印形参的长度
            console.log(arguments.length); //3 打印实参的长度
        }
        test(1, 2, 3)//3
 function sum(n) {
            if (n <= 1) {
                return 1
            }
            return n + sum(n - 1)
        }
        var res = sum(5)
        console.log(res);//15


        // 匿名函数用arguments.callee获取函数
        
        var sum = (function (n) {
            if (n <= 1) {
                return 1
            }
            return n + arguments.callee(n - 1)
        })(5)
        console.log(sum);//15

caller
 //'use strict'  //caller 严格模式下会报错
        test1()
        function test1() {
            test2()
        }
        function test2() {
            console.log(test2.caller);//打印调用当前函数的函数引用,所以打印的是 test1
        }
上一篇下一篇

猜你喜欢

热点阅读