js读书笔记(Function类型)

JavaScript高级程序设计(第3版)P110


 

Function 类型

    1.函数定义的三种方式:                                 

    1.函数声明定义函数:(具有声明提升的作用,建议使用)

1 function sum (num1, num2) {
2        return num1 + num2;
3 }

     2.函数表达式定义:

1 var sum = function(num1, num2){
2             return num1 + num2;
3           };//这里有分号,就跟一般变量声明一样 var a = "abv";

   3.使用函数的构造函数定义函数:(不推荐使用,但是对于理解函数也是对象很好理解)

1 var sum = new function("num1", "num2", "return num1 + num2"); //构造函数的最后一个参数被看做是函数体,前面的都是参数

 

 

    2.js中没有函数重载:                                         
    出现重名函数的话,后者覆盖前者.为什么js中函数没有重载,因为js中函数的参数可以不指定个数,参数是一个数组对象arguments,利用这个arguments数组就可以间接的实现重载,比如:

 1 !function sum(){
 2       if(arguments.length==2){
 3             console.log(arguments[0] + arguments[1]);
 4       }
 5       if(arguments.length==3){
 6             console.log(arguments[0] + arguments[1] + arguments[2]);
 7       }
 8 }(1,2);
 9  //上面这个函数就间接的实现了函数的重载,可以运算两个数的加法,也可以运算三个数的加法,当然也可以计算任意个数的加法
10  !function sum2(){
11       var result = 0;
12       for(var i = 0; i < arguments.length; i++){
13              result += arguments[i];
14       }
15       alert(result);
16  }(1,1,1,1,11,1);

    3.函数作为值:                                       
    在js中,函数也是对象,函数名就是变量名,这个变量名指向的是函数,也是指向函数的指针,但是有一点跟普通引用变量不同,普通引用变量访问对象本身的属性使用的是点操作符'.'或者方括号'[]',而函数名访问函数本身使用的是圆括号'()'(我不知道这里讲的对不对,这里讲的访问函数本身而不是函数的属性,其实我是有点糊涂的)

 1 function sum(num1, num2){
 2     return num1 + num2;
 3 };
 4 !function GetResult(sum, x, y){
 5     console.log(sum(x, y));
 6 }(sum, 1, 3);
 7             //学过C#的委托应该会觉得有点眼熟,C#中的委托是将函数名声明作为一种类型,然后就可以作为参数传递了,就可以在也就是将函数名作为了参数.
 8 
 9 当然也可以从一个函数中返回另一个函数,而且这也是一种极为有用的技术,
10             
11  eg:function createCompareFunction(propertyName){
12         return function(obj1, obj2){//这里拿到的是两个对象
13               var val1 = obj1[propertyName];//获取对象对应的属性
14               var val2 = obj2[propertyName];
15                   if(val1 < val2){
16                        return -1;
17                   }else if(val1 > val2){
18                        return 1;
19                   }else{
20                        return 0;
21                   }
22          };
23    }
24 
25    var person1 = {
26             name : 'ZhangSan',
27             age : 23
28         };
29     var person2 = {
30             name :'LiSi',
31             age : 24
32        };
33     var person3 = {
34             name : 'WangWu',
35             age : 25
36         };
37     var persons = [person1, person2, person3];
38     persons.sort(createCompareFunction('name'));//将name属性作为排序的标准
39     alert(persons[0].name);//弹出LiSi,说明已经按照name排序了
40      //上面再sort里面传递的是createCompareFunction('name'),是一个具体的函数,
41      //之前在讲数组的时候传递的是一个函数名,是不带有括号的,而这里带括号是因为
42      //在createCompareFunction()函数里面返回的是一个函数,也就是相当于还是传递了一个函数进去了,
43 
44       我们将上面的比较函数改的复杂一点,主要根据名字排序,次要根据年龄排序(都升序)
45             function createCompareFunction(properTyName1, properTyName2){
46                 return function (obj1, obj2){
47                     if(obj11 )
48                     var main1 = obj1[properTyName1];
49                     var minor1 = obj1[properTyName2];
50                     var main2 = obj2[properTyName1];
51                     var minor2 = obj2[properTyName2];
52                     if(main1 < main2){
53                         return -1;
54                     }else if(main1 > main2){
55                         return 1;
56                     }else if(minor1 < minor2){
57                         return -1;
58                     }else if (minor1 > minor2) {
59                         return 1;
60                     }else{
61                         return 0;
62                     }
63                 };
64             }
65 
66             var person1 = {
67                 name : 'Z',
68                 age : 23
69             };
70             var person2 = {
71                 name :'Z',
72                 age : 21
73             };
74             var person3 = {
75                 name : 'A',
76                 age : 23
77             };
78             var persons = [person1, person2, person3];
79             showArray(persons);
80             function showArray(array){
81                 for (var i = 0; i < array.length; i++) {
82                     console.log('Name: ' + array[i].name + '; Age: ' + array[i].age)
83                 };
84             }
85             persons.sort(createCompareFunction('name','age'));//将name属性作为排序的标准
86             showArray(persons);
87             //但是有一点没考虑到,js中的数组是可以存储任意类型的,我们无法保证persons数组里面全是person,如果存在非person类型,那么排序就得不到想要的结果.

     4.函数内部属性:                                         
    函数内部有两个特殊的对象:arguments和this.
      arguments是参数数组,this指代的是当前函数运行的环境对象,也就是作用域.arguments这个对象有一个 callee属性,该属性是一个指针,它指向拥有这个arguments对象的函数,这个参数就跟函数名是等价的,函数名也是一个指针,也是指向函数本身,只不过这个指针在函数内部
    看下面这个经典的求阶乘的递归:

 1                function factorial(num){
 2                     if(num <= 1){
 3                         return 1;
 4                     }else {
 5                         return num * factorial(num - 1);
 6                     }
 7                 }
 8                 var result = factorial(5);
 9                 console.log(result);//打印120
10 
11                 //上面的递归调用使得求阶乘这个功能和这个函数名绑定在一起了,
12                 //我们可以利用callee这个内部指针,进行解耦,将功能和函数名解耦
13                 function factorial(num){
14                     if(num <= 1){
15                         return 1;
16                     }else {
17                         return num * arguments.callee(num - 1);
18                     }
19                 }
20                 var otherFun = factorial;
21                 factorial = function(){//这里重新定义了factorial函数
22                     return 0;
23                 }
24                 var result = otherFun(5);
25                 var result2 = factorial(5);
26                 console.log(result);//打印120
27                 console.log(result2);//打印0
28             关于this,this引用的是函数据以执行的环境对象,或者也可说是this的值
29             (当在全局环境下使用this时,this对象的引用就是windows对象)
30             下面例子:
31                 window.color = 'red';
32                 var obj = {
33                     color : 'blue'
34                 };
35                 function sayColor(){
36                     console.log(this.color);
37                 }
38                 sayColor();
39                 obj.sayColor = sayColor;//标记
40                 obj.sayColor();
41 
42                 上面的代码标记的地方,是将函数对象赋值给了o对象的sayColor属性
43                 上面代码等价于下面:
44                 window.color = 'red';
45                 function sayColor(){
46                     console.log(this.color);
47                 }
48                 var obj = {
49                     color : 'blue',
50                     objSayColor : sayColor//将函数名赋值给对象o的一个属性,这里是为了避免重名,才改成了objSayColor
51                 };
52                 sayColor();
53                 obj.objSayColor();//调用obj的的属性objSayColor指向的函数
54 
55                 我为什么不像下面这样做呢?
56                 window.color = 'red';
57                 var obj = {
58                     color : 'blue',
59                     sayColor : function /*sayColor*/(){//这里的sayColor可以有也可以没有
60                                     console.log(this.color);
61                                }
62                 };
63                 function sayColor(){
64                     console.log(this.color);
65                 }
66                 sayColor();
67                 obj.sayColor();
68 
69                 那是因为 obj.sayColor = sayColor;这句话是将sayColor变量指向的函数赋值给了obj的sayColor属性
70                 最终sayColor();
71                    obj.sayColor();这两行代码调用的是同一个函数,而不是两个相同的函数,也就是说,内存中只存在一个sayColor()函数,
72                     而向上面这样做的话,就制造出两个功能相同的sayColor()函数了,这样不是等价的.所以我没有这样做.
73                 //我的这句话也是对115页作者强调标记的理解.
74             函数还有一个特殊的对象属性:caller,这个属性保存着调用当前函数的函数的引用,也就是主调函数的引用,可以说caller和主调函数的函数名指向的是同一个函数对象,既然是函数的引用,那么在全局作用域中调用该函数时,没有主调函数,那么caller的值就是null了.
75             eg:
76                 function outer(){
77                     inner();
78                 }
79                 function inner(){
80                     alert(inner.caller);
81                 }
82                 outer();
83 
84             当函数在严格模式下运行时,访问arguments.callee会导致错误,
85             严格模式下还有一个限制:不能为caller属性赋值,否则会导致错误,也就是说,caller属性是只读的,不可写.                    

     5.函数的属性和方法:                                                
    之前说过函数也是对象,既然是对象,那么就有属性和方法【其中方法当然有继承下来的 toString(),valueOf()方法】
      每个函数都包含两个属性:length和prototype.其中length属性表示函数希望接收的命名参数的个数,

1             function sum(num1, num2){
2                 return sum1 + num2;
3             }
4             alert(sum.length);//弹出2
5             sum.length = 5;
6             alert(sum.length);//弹出2,说明length属性不可写,可读.

      prototype属性是一个比较重要的属性了,对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在, 换句话说,诸如toString()和valueOf()等方法实际上都是保存在prototype名下,只不过是通过各自对象实例访问罢了,在创建自定义引用类型以及实现继承时,prototype属性是极其重要的(在第六章将详细介绍,下一篇博客我就写第六章的内容), 在ECMAScript5中,prototype属性是不可枚举的,因此使用for-in都无法发现。
    每个函数都包含两个非继承而来的方法:apply()和call()方法,这两个方法的用途都是在特定的作用域调用该函数, 实际上等于设置函数体内的this对象的值.
      apply()函数接受两个参数:第一个是在其中运行函数的作用域,第二个参数是数组(或者是arguments对象),
      call()函数接收的参数:第一个同apply一样,是该函数运行的作用域,其余的参数都是都是直接传递给函数(就是将参数数组的各个项都列举出来)
    这两个函数的作用一样,调用时就看参数是否确定,也就是说如果传递给函数的是一个数组,那么apply()更适合,反之call()可能更合适,当然,你也可以将确定个数的参数放在数组里,然后使用apply()函数. 事实            上,传递参数并非apply()和call()真正的用武之地;他们真正强大的地方是它们能够扩大函数赖以运行的作用域, 也就是说, 我传什么作用域进去, 函数就在那个作用域运行.

 1           eg:
 2                     window.color = 'red';
 3                     var o = {
 4                         color : 'blue'
 5                     };
 6                     function sayColor(){
 7                         alert(this.color);
 8                     }
 9                     sayColor();//弹出red,函数内部this指向的是全局作用域
10 
11                     sayColor.call(this);//弹出red,函数的执行环境是全局作用域
12                     sayColor.call(window);//弹出red,函数的执行环境是全局作用域
13                     sayColor.call(o);//弹出blue,函数内部this指向的是引用类型o
14 
15                     使用call()和apply()来扩充作用域最大的好处,就是对象不需要域方法有任何耦合关系.
16                     上面的例子是先将sayColor()函数放到了对象o中作为了一个方法,然后通过o来调用这个方法,
17                     而ECMAScript还定义了一个方法,bind(),这个方法会创建一个函数的实例,并作为返回值返回,这个实例的中this的值
18                     会被绑定到传给bind()方法的值,这个实例也是一个函数,只不过里面的this值指定的是传递的那个参数(这个参数会是一个对象)
19                     eg:
20                         window.color = 'red';
21                         var o = {
22                             color : 'blue'
23                         };
24                         function sayColor(){
25                             alert(this.color);
26                         }
27                         var objectSayColor = sayColor.bind(o);//得到一个函数的实例,这个实例也是一个函数,只不过这个函数体内的this指向的是对象o
28                         objectSayColor();//弹出blue
29                         //如果我们直接敲入objectSayColor会打印出sayColor函数的定义语句,和敲入objectSayColor的结果一样,但是前者打印结果中多了一个感叹号,
                 其内容是Function was resolved from bound function,表明这个是来源于函数,但是与函数不同。


 

posted @ 2017-02-27 19:01  码上猿梦  阅读(178)  评论(0编辑  收藏  举报