zoukankan      html  css  js  c++  java
  • Javascript二(函数详解)

    一.函数

               Javascript是一门基于对象的脚本语言,代码复用的单位是函数,但它的函数比结构化程序设计语言的函数功能更丰富。JavaScript语言中的函数是“一等公民”,它可以独立存在;而且JavaScript的函数完全可以作为一个类来使用(而且它还是该类唯一的构造器);与此同时,函数本身也是一个对象,函数本身是function

      实例。

       函数的最大作用是提供代码复用,将需要重复使用的代码块定义成函数,提供更好的代码复用。函数可以有返回值,可以没有返回值

    1.定义函数的三种方式

       a)定义命名函数,

            语法格式如下:

    function functionName(param1,param1,...){
    
    staments;
    }

      b)定义匿名函数

          语法格式如下:

    function(parameter list){
    staments
    };

         与命名函数的区别是没有函数名,函数后面有个分号。

         当通过这种语法格式定义了函数之后,实际上就定义了一个函数对象(即function实例),接下来可以将这个对象赋给另外一个变量。例如下面代码:

    <script type='text/javascript'>
    var f = function(name)
    {
      document.writeln('匿名函数<br/>');
     document.writeln('你好'+name);
    }
    f('yukey');
    </script>
    

       使用匿名函数提供更好的可读性。

         c)使用function类匿名函数

          JavaScript提供了一个function类,该类也可以用于定义函数,Function类的构造器的参数个数可以不受限制,function可以接受一系列的字符串参数,其中

        最后一个字符串参数是函数的执行体,执行体的各语句以分号(;)隔开,而前面的各字符串参数则是函数的参数,看下面定义函数的方式:

    <script type='text/javascript'>
    var f = new Function('name',"document.writeln('Function定义的函数<br/>');"
              +"document.writeln('你好'+name);");
    
      f('yukey');
    </script>
    

    2.局部函数

    局部函数在函数里定义,看下面代码

     <script type="text/javascript">
           function outer(){
               function inner1(){
                   document.write("局部函数11111<br/>");
               }
    
               function inner2(){
                   document.write("局部函数22222<br/>");
               }
               inner1();
               inner2();
               document.write("结束测试局部函数<br/>");
           }
           outer();
           document.write("调用outer之后...");
       </script>

          在外部函数里调用局部函数并不能让局部函数获得执行的机会。只有当外部函数被调用时,外部函数里调用的局部函数才获得执行的机会。

    3.数,方法,对象,变量和类   函数是JavaScript的“一等公民”,函数是JavaScript变成里非常重要的一个概念,当使用JavaScript定义了一个函数之后,实际上可以得到如下四项。

          函数:就像Java的方法一样,这个函数可以被调。

          对象:定义一个函数时,系统也会创建一个对象,该对象时Function类的实例

          方法:定义一个函数时,该函数通常会附加给某个对象,作为该对象的方法

          变量:在定义一个函数的同时,也会得到一个变量

          :在定义函数的同时,也得到一个与函数同名的类

       定义函数之后,有如下两种方式调用函数

          直接调用函数:直接调用函数总是返回该函数体内最后一条return语句的返回值;如果该函数体内不包含return语句,则直接调用函数没有返回值。

          使用new关键字直接调用函数:通过这种方式调用总有返回值,返回值就是一个Javascript对象。

          

    <script type="text/javascript">
            var test = function(name)
            {
                return "你好,"+name;
            }
            var rval = test('Sherman');
            var obj = new test("Sherman");
            alert( rval+"
    "+obj);
        </script>

        可以看出,第一种方式直接调用函数,返回的是return语句返回值,第二种使用new关键字调用给函数,也就是将函数当成类来使用,得到的是一个对象。

       下面程序定义了一个person函数,也就定义了一个person类,该person函数也会作为Person类唯一的一个构造器,定义了person函数时希望为该函数定义

        了一个方法。

          

    <script type="text/javascript">
            function Person(name ,age){
                this.name = name;
                this.age = age;
                this.info=function(){
                    document.writeln("我的名字是"+this.name+'<br/>');
                    document.writeln("我的年纪是"+this.age+'<br/>');
                }
            }
           var p = new Person('Sherman',24);
            p.info();
        </script>

      被this关键字修饰的的变量不再是局部变量,它是该函数的实例属性。

      JavaScript定义的函数可以“附加”到某个对象上,作为该对象的方法。实际上如果没有明确指定将函数“附加”到哪个对象上,该函数默认“附加”到window

    对象上,作为window对象的方法。

     例如如下代码:

    <script type="text/javascript">
            function hello(name)
            {
                document.write(name+",您好<br/>")
            }
            window.hello("孙大圣");
             p = {
            //定义一个函数,该函数属于p对象
                walk:function(){
                    for(let i = 0 ; i < 2  ; i++){
                        document.write("慢慢地走...<br/>");
    
                    }
                }
            };
            p.walk();
        </script>

    4.函数的实例属性和类属性

      由于JavaScript函数不仅仅是一个函数,而且是一个类,该函数还是此类唯一的构造器,只要在调用函数时使用new关键字,就可返回一个object,这个object

    不是函数的返回值,而是函数本身产生的对象。因此JavaScript中定义的变脸不仅有局部变量,还有实例属性和类属性两种。根据函数中声明变量的方式,

    中的变量有三种

      a)局部变量:在函数中以var声明的变量

      b)实例属性:在函数中以this前缀修饰的变量

      c)类属性:在函数中以函数名前缀修饰的变量

        局量只能在函数里访问的变量。实例属性和类属性是面向对象的概念:实例属性是属于单个对象的,因此必须通过对象来访问,类属性是属于整个类本身

    (也就是函数)的,因此必须通过类来访问。

      同一个类只占用一块内存,因此每个类属性只占用一块内存;同一个类每创建一个对象,系统将为该对象的实例属性分配一块内存。

     <script type="text/javascript">
            function Person(national,age){
                this.age = age;
                Person.national = national;
                var bb = 0;
            }
            var p1 = new Person('中国',29);
            with(document){
                writeln("创建第一个Person对象");
                writeln("p1的age属性为:"+p1.age+"<br/>");
                writeln("p1的national属性为:"+p1.national+"<br/>");
                writeln("通过Person访问静态national属性为:"+Person.national+"<br/>");
                writeln("p1的bb属性为"+p1.bb+"<br/><hr/>");
            }
    
             var p2 = new Person('美国',32);
            with(document){
                writeln("创建两个Person对象中后<br/>");
                writeln("p1的age属性为:"+p1.age+"<br/>");
                writeln("p1的national属性为:"+p1.national+"<br/>");
    
                writeln("p2的age属性为:"+p2.age+"<br/>");
                writeln("p2的national属性为:"+p2.national+"<br/>");
                writeln("通过Person访问静态national属性为:"+Person.national+"<br/>");
                
            }
        </script>

    浏览器输出:          

    创建第一个Person对象 p1的age属性为:29
    p1的national属性为:undefined
    通过Person访问静态national属性为:中国
    p1的bb属性为undefined


    创建两个Person对象中后
    p1的age属性为:29
    p1的national属性为:undefined
    p2的age属性为:32
    p2的national属性为:undefined
    通过Person访问静态national属性为:美国

       值得指出的是,JavaSript和java不一样,它是一种动态语言,它允许随时为对象增加属性和方法,当直接为对象的某个属性赋值时,即可视为给对象增加属性

    5.调用函数的3种方式

     5.1 直接调用函数

         如下代码:

    //调用window对象的alert方法
    window.alert();
    //调用p对象的walk方法
    p.walk();

      当程序使用window对象调用方法时,window调用者可以省略

        5.2 以call方式调用函数

    直接调用函数的方式简单易用,但这种调用方式不够灵活,有时候调用函数时需要动态的传入一个函数引用,此时为了动态地调用函数,就需要call方法。call调用函数

    语法格式为

    函数引用.call(调用者,参数1,参数2,...)

     下面通过call方法调用each函数:

     

    <script type="text/javascript">
            var each = function(array,fn){
                for(var index in array){
                    fn.call(null,index,array[index]);
                }
            }
            each([4,20,3],function(index,ele){
                document.writeln("第"+index+"个元素是:"+ele+"<br/>");
            });
        </script>

    浏览器输出:

    第0个元素是:4
    第1个元素是:20
    第2个元素是:3

    5.3 以apply()方法调用函数

    apply()方法和call()方法比较类似,都可以动态的调用函数,他们的区别是:

    a)通过call()方法调用函数时,必须在括号中列出每个参数

     b)通过apply()动态地调用函数时,需要以数组形式一次性传入所有调用函数

    以下代码示范了call()和apply()的关系

    <script type="text/javascript">
            var myfun = function(a,b){
                alert('a的值是'+a+'
    b的值是'+b);
            }
            //以call()方式动态的调用函数
            myfun.call(window,5,20);
            //以apply()方式动态的调用函数
            myfun.apply(window,[3,12]);
            var example = function(num1,num2){
                //直接用arguments代表调用example函数时传入的所有函数
                myfun.apply(this,arguments);
            }
            example(20,40);
        </script>

    由此可见,apply()和call()对应关系如下:

    函数引用.call(调用者,参数1,参数2,...); = 函数引用.apply(调用者,[参数1,参数2,...]);

    6.函数独立性

    虽然定义函数时可以将函数定义成某个类的方法,或定义成某个对象的方法。但JavaScript的函数是“一等公民”,他永远是独立的,函数永远不会从属于其他类,对象。

    下面代码示范了函数的独立性:

    <script type="text/javascript">
            function Person(name){
                this.name = name;
                this.info = function (){
                    alert("我的name是:"+this.name);
                }
            }
    
            var p = new Person("Sherman");
            //调用p对象的info方法
            p.info();
    
            var name = "测试名称";
           //以window对象作为调用者来调用p对象的info方法
            p.info.call(window);
        </script>

     

    当使用匿名内嵌函数定义某个类的方法是时,该内嵌函数一样是独立存在的,该函数也不是作为该类实例的附庸存在,这些内嵌函数也可以被分离出来独立使用,成为另一个对象的函数。如下代码再次证明函数的独立性:

    <script type="text/javascript">
            function Dog(name,age,bark)
            {
                this.name = name;
                this.age = age;
                this.bark = bark;
                //使用内嵌函数为Dog实例定义方法
                this.info = function(){
                    return this.name+"的年龄为:"+this.age+",它的叫声为:"+this.bark;
                }
            }
    
            var dog = new Dog("旺财",3,"汪汪,汪汪...");
            function Cat(name,age){
                this.name = name;
                this.age = age;
            }
         //将dog实例的info方法分离出来,在通过call方法调用info方法
        //此时cat为调用者
            var cat = new Cat("Kitty",2)
            alert(dog.info.call(cat));
        </script>

    7.函数提升

    JavaScript允许先调用函数,然后再在后面定义函数,这就是典型的函数提升:JavaScript会将全局函数提升到根元素<script.../>元素的顶部定义

    例如如下代码:

     <script type="text/javascript">
            console.log(add(2,5));
            function add(a,b){
                console.log("执行add函数")
                return a+b;
            }
        </script>

    和下面代码效果是一样的:

     <script type="text/javascript">
            function add(a,b){
                console.log("执行add函数")
                return a+b;
            }
            console.log(add(2,5));
        </script>

    效果如图:

     如果使用程序先定义匿名函数,然后将匿名函数赋值给变量,在这种方式下依然会发生函数提升,但此时只提升被赋值的变量,函数定义本省不提升。例如

     <script type="text/javascript">
            console.log(add(2,5));
    
            var add = function(){
                console.log("执行add函数");
                return a+b;
            }
        </script>

    效果如图:

    局部函数会被提升到所在函数的顶部,如

     <script type="text/javascript">
            function test() {
                function add(a, b) {
                    console.log("执行add函数")
                    return a + b;
                }
    
                console.log(add(2, 5));
            }
            test();
        </script>

    JavaScript编程时应尽量避免变量名和函数名同名。否则会发生覆盖的情形:分两种情况

     a)定义变量时只用var定义变量,不分配初始值,此时函数的优先值更高,函数会覆盖变量。

     b)定义变量时为变量值指定了初始值,此时变量的优先值更高,变量会覆盖函数

    测试代码如下:

    <script type="text/javascript">
            function a(){}
            var a;
            console.log(a);
            var b;
            function b(){}
            console.log(b);
            var c = 1;
            function c(){};
            console.log(c);
    
            function d(){}
            var d = 1;
            console.log(d);
        </script>

    效果如下:

    8.箭头函数

         箭头函数相当于其他语言的Lambda表达式或闭包语法,箭头函数是普通函数的简化写法。语法格式如下:

    (param1,param2,param3,...) => {staments}

    相当于定义了如下函数:

    function(param1,param2,param3,...){}

    如果箭头函数的执行体只有一条return语句,则允许省略函数执行体的花括号和return关键字。

    如果箭头函数的形参只有一个参数,则允许省略形参列表的圆括号。

    如果箭头函数没有形参,则圆括号不可以省略。

    (param1,param2,param3,...) => expression
    //等同于(param1,param2,param3,...) =>{return expression}
    singleParam => {staments}
    //等同于(singleParam) => {staments}

    下面代码示范了箭头函数代替传统函数:

    <script type="text/javascript">
            var arr = ["yuekey","fkit","leegang","sczit"];
            var newArr1 = arr.map(function(ele){
                return ele.length;
            });
            var newArr2 = arr.map((ele) =>{return ele.length});
            var newArr3 = arr.map(ele => ele.length);
            console.log(newArr3);
            arr.forEach(function(ele){
                console.log(ele);
            });
            arr.forEach((ele) => {console.log(ele);})
            arr.forEach(ele => console.log(ele));
    
        </script>

      与普通函数不同的是,箭头函数并不拥有自己的this关键字,对于普通函数而言,如果程序通过new调用函数创建对象,那么该函数中的this代表所创建的对象;

    如果直接调用普通函数,那么该函数的this代表全局对象(window)。例如,如下代码示范了this关键字的功能。

    <script type="text/javascript">
            function Person(){
                this.age = 0;//Person()作为构造器使用时,this代表构造器创建的对象
                setInterval(function growUp(){
                    console.log (this=== window);//对于普通函数来说,this代表全局对象window,总是返回true
                    this.age++;
                },1000);
            }
            var p = new Person();
            setInterval(function(){
                console.log(p.age);//此处访问p对象的age,总是0
            },1000);
        </script>

    箭头函数中的this总是代表包含箭头函数的上下文,例如:

    <script type="text/javascript">
            function Person(){
                this.age = 0;
                setInterval(() => 
                {
                    console.log(this === window);
                    this.age++;//this总是代表包含箭头函数的上下文
                    },1000);
            }
    
            var p = new Person();
            setInterval(() => console.log(p.age),1000);//此处访问的是p对象的age,总是不断加1
    
        </script>

     如果在全局范围内定义箭头函数,那么箭头函数的上下文就是window本身,this代表全局对象window对象。

    <script type="text/javascript">
    var f = () => {return this;};
    console.log(f() === window);//输出true
    
    </script>

     箭头函数并不绑定arguments,因此不能在箭头函数中通过arguments来访问调用箭头函数的参数,箭头函数的arguments总是引用当前上下文的arguments。例如

    <script type="text/javascript">
        var arguments = "Sherman";
        var arr = () => arguments;
        console.log(arr());
    
        function foo(){
          var f = (i) => 'Hello,'+arguments[1];
          return f(2);
        }
        console.log(foo("Sherman","Leegang"));//箭头函数中的arguments引用当前上下文的arguments,此时代表调用foo函数的参数
      </script>

    9.函数的参数处理

    大部分时候,函数都需要接受参数传递。与Java完全类似,JavaScript的参数传递也全部采用值传递方式。

    9.1基本类型和复合类型的参数传递

    对于基本类型参数,JavaScript采用值传递方式,当通过实参调用函数时,传入函数里的并不是实参本身,而是实参的副本,因此在函数中修改参数值并不会对实参

    有任何影响:

     <script type="text/javascript">
        function change(arg1) {
          arg1 = 10;
          document.writeln("函数执行中arg1的值为:" + arg1 + "<br/>");
        }
        var x = 5;
           document.writeln("函数调用前x的值为"+x+"<br/>");
           change(x);
           document.writeln("函数调用之后的x值为"+x+"<br/>");
    
      </script>

    对于复合类型的参数,实际上采用的依然是值传递方式,只是很容易混淆。看如下程序

    <script type="text/javascript">
        function changeAge(person)
        {
          person.age = 10;
          document.writeln("函数执行中age的值为:" + person.age + "<br/>");
          person = null;
        }
        var person = {age:5};
        document.writeln("函数调用之前age的值为"+person.age+"<br/>");
        changeAge(person);
        document.writeln("函数调用之后的age值为"+person.age+"<br/>");
        document.writeln("person对象为"+person);
      </script>

    9.2空参数

     在JavaScript中,在函数声明时包含了参数,但调用时没有传入实参,这种情况是允许的,JavaScript会自动将参数值设置为undefined值,对于JavaScript来说,函数名就是函数的唯一标识。

    如果先后定义两个同名,形参列表不同的函数,这不是函数重载,这种情况后面定义的函数会覆盖前面的函数。

    9.3参数类型

    JavaScript是弱类型语言,参数列表无需声明参数类型。

    “鸭子类型”的理论认为,弱类型语言的函数需要接收参数时,则应先判断参数类型,判断参数是否包含了需要访问的属性、方法。当条件都满足时 ,程序才会真正

    执行。看如下代码

    <script type="text/javascript">
        function changeAge(person) {
          if (typeof person == 'object' && typeof person.age == 'number') {
            document.writeln("函数调用之前age的值为" + person.age + "<br/>");
            person.age = 10;
            document.writeln("函数执行中age的值为:" + person.age + "<br/>");
          }
          else {
            document.writeln("参数类型不符合" + typeof person + "<br/>")
          }
        }
          changeAge();
          changeAge("Sherman");
          changeAge(true);
    
          p = {abc:34};//json格式创建第一个对象
          changeAge(p);
    
          person = {age:25};//json格式创建第二个对象
          changeAge(person);
      </script>

  • 相关阅读:
    丁夏畦同志去世
    [裴礼文数学分析中的典型问题与方法习题参考解答]4.5.7
    [数分提高]2014-2015-2第10教学周第2次课 (2015-05-07)
    [数分提高]2014-2015-2第10教学周第1次课 (2015-05-04)
    [数分提高]2014-2015-2第9教学周第2次课 (2015-04-30)
    [数分提高]2014-2015-2第9教学周第1次课 (2015-04-28)
    [数分提高]2014-2015-2第8教学周第2次课 (2015-04-23)
    2014年江西省青年科学家名单
    2014年“江西青年五四奖章”名单
    [数学杂志]AML
  • 原文地址:https://www.cnblogs.com/yumiaoxia/p/8893505.html
Copyright © 2011-2022 走看看