js函数

1.函数简介

  -Function类型,即函数的类型

  -一个典型的JavaScript函数定义如下:

    function 函数名称(参数表) {

      函数执行部分;

    }

    注意函数的参数表直接写形参名,不用写var类型。

  -return语句,return返回函数的返回值并结束函数运行

  -函数也可以看做数据来进行传递

<script type="text/javascript" charset="UTF-8">
            
        
            function test(a, b) {
                
                return a + b;
            }
            alert(test(10, 20));
            
            // 函数也是一种数据类型
            alert(typeOf test);    // 返回值为function          // 函数可以传递
            function test1(func) {
                func();
            }
            
            function test2() {
                alert('aaa');
            }
            
            test1(test2);
            
            // 函数可以嵌套
            function test4() {
                function test5() {
                    alert('inner');
                }
                //只能在函数内部调用,外部不可调用
                test5();
            }
            
            test4();
        </script>

2.函数的三种定义方式

-三种定义方式
   - function语句式

   -函数直接量式

   -通过Function构造函数形式定义函数

-比较三种方式定义的区别

       
       
       
       
       
       
       

         function语句      function构造函数      函数直接量

  兼容     完全          js1.1以上         js1.2以上

  形式     句子          表达式          表达式

  名称     有名          匿名           匿名

  性质     静态          静态           静态

  解析时机   优先解析        顺序解析         顺序解析

  作用域    具有函数作用域       顶级函数(顶级作用域)     具有函数作用域

<script type="text/javascript" charset="utf-8" defer="defer" src="../commons/001.js">
            // 3种方式定义函数
            // 1 function语句函数式
            function test1() {
                // 只会被编译一次,放到内存里,静态,效率高
                alert('我是test1');
            }
            // 2.函数的直接量式   ECMAScript推荐使用
            // 只会被编译一次,放到内存里,静态,效率高
            var test2 = function() {
                alert('我是test2');
            };
            // 3.function 构造函数式  
            // 每次都会被编译,效率低,但不占用内存
            var test3 = new Function("a", "b", "return a+ b;");
            alert(test3(10,20));
            
            
            function test1() {
                alert('111');
            }
            test1();
            
            var test2 = function() {
                alert('222');
            };
            test2();
            // 执行结果为111 -》222 没问题
            
            // 但是如果将test1和test2分别放到函数体之前执行呢,
            test3();
            function test3() {
                alert('333');
            }
            test3();
            
            alert(test4); //返回undifind,表示变量声明了,但是没有赋值
            test4();
            var test4 = function() {
                alert('444');
            };
            // 执行结果为333 ,为什么444没有被弹出,因为只有function语句函数式才是优先解析,剩下是顺序解析
            
            
            // 作用域问题
            var k = 1;
            function t1() {
                var k = 2;
                function test() {return k;}    // 2
                var test = function(){return k;};// 2
                var test = new Function("return k;"); // 1  原因是具有顶级作用域,相当于在外面(全局)new 了一个函数,所以结果为1
                alert(test());
            }
            t1();
        </script>

3.函数的参数(arguments对象)
  -arguments是表示函数的实际参数(与形参无关)

  -callee函数(回调函数属性)

  -arguments对象的秘密属性 callee属性。这个属性比较奇怪,它能够返回arguments对象所属的函数的引用、这相当于

在自己的内部调用自己。用法:检测函数传递的参数正确与否。

<script type="text/javascript" charset="utf-8">
            // js中 函数的参数分为:形参和实参
            function test(a, b, c, d) {
                // 形参4个 实参2个,在js中 形参个数和实参个数没有关系
                // 如何求形参的个数
                test.length;
                // 函数的实际参数,内部就是用一个数组去接受的
                // arguments对象 可以访问函数的实际参数
                // arguments对象 只能在函数的内部访问和使用
                alert(arguments.length);
                alert(arguments[0]);
                alert(arguments[1]);
                
                if (test.length === arguments.length) {
                    return a + b;
                } else {
                    return '参数不正确';
                }
                
            }
            alert(test(10, 20));
            
            // arguments对象  用的最多的地方还是做递归操作
            // callee方法 指向的是函数本身
            arguments.callee.length;// 等价于test.length
            
            
            function fact(num) {
                if (num <= 1) {
                    return 1;
                } else {
                    // 这里建议使用arguments.callee,因为可以存在这样的代码,fact = null,这样会出现问题
                    return num * arguments.callee(num - 1);
                }
            }
            alert(fact(5));
        </script>

4.this对象
  -this对象是在运行时基于函数的执行环境绑定的。在全局函数中,this等于window,而当函数被作为某个对象

的方法调用时,this等于那个对象。

  -也就是说this关键字总是指代调用者。

  

<script type="text/javascript" charset="utf-8">
            // this : this对象是指在运行时期,基于执行环境所绑定的。
            // 总是指向调用者
            var k = 10;
            function test() {
                this.k = 20;
            }            
            alert(test.k); // 返回undefind,因为函数只是编译了,并没有执行
            test(); // 对于test来讲,执行环境是全局作用域  等价于window.test();
            alert(test.k);// 还是返回undefined,因为this指的是window
            alert(window.k); // 20 等价于alert(k);
        </script>

 5.call和apply方法

  - 每一个函数都包含两个非继承而来的方法:call和apply。

    这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。

  -call,apply的用途之一就是传递参数,但事实上,他们真正强大的地方是能够扩充函数赖以运行的作用域。

  -使用call()和apply()来扩充作用域的最大好处就是对象不需要与方法有任何耦合关系。

  -call方法简单的实现。

<script type="text/javascript" charset="utf-8">
            // call和apply 简单的用法,绑定一些函数,用于传递参数 调用
            function sum(x, y) {
                return x + y;
            }
            
            function call1(num1, num2) {
                // 将sum绑定到this。也就是call1上。这样就可以使用sum方法了。
                return sum.call(this, num1, num2);
            }
            
            function apply1() {
                // apply1和call类似,只不过后面传递的参数 是一个数组。
                return sum.apply(this, [num1, num2]);
            }
            
            
            // 主要用途  扩充作用域
            window.color = 'red';
            var obj = {color :'blue'};
            
            function showColor(){
                alert(this.color);
            }
            
            showColor.call(this);        // 结果为red
            showColor.call(obj);    // 结果为blue  函数showColor的作用域从 全局变为了obj
            
            // call方法的简单模拟和实现
            function test1(a, b) {
                return a + b;
            }
            
            // 名字大写,默认为一个对象 ->自定义的对象
            function Obj(x, y) {
                return x * y;
            }
            var obj1 = new Obj(10, 20);
            alert(test1.call(obj1, 10, 20));
            // 相当于 自己定义了一个临时的方法,用来接收test1
            obj.method = test1();
            // 使用完之后删掉
            delete obj1.method;
        </script>

6.执行环境和作用域链概念
    -执行环境(execution context)是js中最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。

  每一个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们的代码无法访问这个对象,

  但是解析器在处理数据的时候会在后台执行它。

    -全局执行环境是最外围的一个执行环境。根据ECMAScript实现所在的宿主环境不同,表示执行环境的对象也不一样。

    -每一个函数都有自己的执行环境。当执行流进一个函数时,函数的环境就会被推入一个函数栈中、而在函数执行之后,栈将其弹出,

  把控制权返还给之间的执行环境。当代码在一个环境中执行时,会创建变量对象的一个作用域链(Scope chain)。作用域链的用途,是保证

  对执行环境有权访问的所有变量和函数的有序访问。

<script type="text/javascript" charset="utf-8">
            
            // 1.执行环境,window对象(最上层的执行环境)
            
            var color1 = 'blue';
            // 每个函数都有一个执行环境,(variable obj)
            function changeColor() {        
                var color2 = 'red';
                // color1 是一级作用域  color2是二级作用域,color3是三级作用域            
                function swapColor() {         // 这个函数也产生了一个执行环境 (variable obj)
                    var color3 = color2;
                    color2 = color1;
                    color1 = color3;
                    // 这里可以访问color1,color2,color3
                }
                // 这里可以访问color1,color2,但是不能访问color3
                swapColor();
            }
            // 这里只能访问color1
            changgeColor();        //第一个作用环境 window
            
            // 环境变量 可以一层一层的向上进行追溯,可以访问它的上层环境 (变量和函数),基于这种机制,全局变量尽量少用。节约效率。
        </script>

7.垃圾收集和块级作用域
  -js是一门具有自动垃圾收集机制的编程语言。开发人员不必关心内存分配和回收问题。

    -离开作用域的值被自动标记为可以回收,因此将在垃圾收集期间被删除。标记清楚是目前主流的垃圾收集算法。这种算法的思想

  是想给当前不使用的值加上标记,然后回收其内存。

  -js里面没有块级作用域的概念,和C,java等高级语言不同。所以在使用if和for的时候要注意。

  -js模拟块级作用域。

<script type="text/javascript" charset="utf-8">
            
            
            // 垃圾收集 方法1 标记法
            function test() {
                var a = 10;    //被使用
                var b = 20; // 被使用
                
            }
            test();    //执行完毕之后,a,b又被标记了一次 , 标记成了没有被使用状态。
            
            // 方法2 引用计数法
            function test1() {
                var a = 10;    // aCount = 1
                var b = 20; // bCount = 1
                var c;
                c = a;    //aCount = 2
                a = 20; //aCount = 1;
                // 当aCount = 0的时候,就会被回收。                
            }
            
            // 块级作用域的概念
            function test2 () {
                for (var i = 1; i <= 5; i++) {
                    alert(i);
                }
                alert(i); //在java中,肯定会报错,但是在js中  返回值为6.所以在使用时 需要注意这一点。原因是js没有块级作用域这一说法。
                // js默认当函数 执行完之后  这个变量才没有。
            }
            
            
            // 模拟块级作用域
            function test3() {
                // 写一个匿名函数,在加上小括号,表示让函数立即执行。
                (function () {
                    for (var i = 1; i <= 5; i++) {
                    alert(i);
                }
                })();
            }
            
            // 函数直接执行 参考下面的代码
            (function() {alert('i am comming');})();
        </script>

8.闭包(Closure)
  -闭包与函数之间有着紧密的关系,它是函数的代码在执行过程中的一个动态环境,是一个运行期的,动态的概念。

  -所谓闭包,是指词法表示包括不必计算的变量的函数。也就是说,该函数能够使用函数外显示定义的变量。

  -在程序语言中,所谓闭包。是指语法域位于某个特定的区域。具有持续参照(读写)位于该区域内自身范围之外的执行域上的非

持久型变量值能力的段落。这些外部执行域的非持久性变量神奇保留他们在闭包最初定义(或创建)时的值。

<script type="text/javascript" charset="utf-8">
            
            
            var name = 'xiao A';
            var obj = {
                name : 'xiao B',
                getName : function() {
                    return function(){
                        return this.name;
                    };
                }
            };
            alert(obj.getName()());        // 结果为xiao A
            
            
            var obj1 = {
                name : 'xiao B',
                getName : function() {
                    return function(){
                        var o = this;
                        return o.name;
                    };
                }
            };
            alert(obj1.getName()());    // 结果为xiao B
            
            // 闭包:一个函数可以访问另外一个函数作用域中的变量。
            // 封闭性,类似于java中的private  起一个保护变量的作用。
            
            // 1级作用域
            function f(x) {
                // 2级作用域
                var temp = x;
                // 3级作用域
                return function(x){
                    temp += x;
                    alert(temp);
                };
            }
            var a = f(50);        // 匿名的函数结构体
            a(5);    // 55
            a(10);    // 65
            a(20);  // 85
            // 直到temo找不到了之后,才回收。
        </script>

 

posted @ 2017-06-24 23:16  noooooob  阅读(218)  评论(0编辑  收藏  举报