JS 学习笔记--8---Function类型
练习使用的浏览器IE11
JS 中Function类型实际上是一种对象,每一个函数实际上都是Function类型的一个实例,每一个函数都有一些默认的属性和方法。由于函数是对象,故函数名实际上也是一个指向函数对象的指针变量
1、声明一个函数
直接用关键字function来声明函数,JS是弱类型的语言,声明的时候参数个数不限,只是需要写参数名,不需要写参数的关键字var,不需要指定返回值以及类型,如果有返回值直接在函数后面一个return value 即可。
方法一:function func(num1,num2){return 1;}//num1,num2是参数名,返回值为1
方法二:利用变量来声明函数,var box=function(num1,num2){};相当于给window对象的自定义属性box注册方法
方法三:利用析构函数来声明,但是最好不要用这种方式来声明,因为这种方式浏览器会解析两次,效率不高,第一次是解析整个ECMAScript表达式,第二次是解析析构函数中传递的字符串列表,var box=new Function('num1','num2','return num1+num2');注意析构函数里面的参数以及语句是带有引号的。这种方式能够更好的理解 Function是一个对象的,函数名是一个指针的概念
2、调用函数
方法一中声明的函数直接写函数名就可以调用[fun(10,20)],
方法二和方法三声明的函数调用时用变量名后面加括号里面写上要传递的参数值[box(10,20)];
函数的返回值可以直接赋值给一个变量[var box=func(20,10);],也可以当做实参直接传递给另外的函数[alert(func(10,20));]
ECMAScript 中的函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数, 而且可以将一个函数作为另一个函数的结果返回
//将一个函数作为结果返回 其实就相当于执行这个函数,然后把这个函数的返回值给返回 function sum(num1,num2){ return num1+num2; } function func(num1){ return sum(num1,10); } alert(func(10));//20 //其实函数名,也就是一个对象的变量,执行的是函数的地址,相当于一个指针 //将函数名当做一个参数传递给另外一个函数, function sumFunc(num1,num2){ return num1+num2; } function func(sum,num1){ return sum(num1,10); } alert(func(sumFunc,10));//20
JS 中没有函数重载的说法,函数调用的时候,会以就近原则来调用
JS 中调用函数的时候允许调用者和被调用这的参数个数不一样,也不会报错,但是如果参数个数不够的话,在执行时因为有的参数没有赋值可能会报错,参数时从前向后对应传递接收的
3、函数的内部属性 arguments
arguments 是一个类数组,用来保存函数传递过来的参数,数组类型
在声明函数的时候,有的时候我们并不知道调用者到底要传递多少个参数过来,比如说,求一串数的累加和,如果调用者不是用数组传递过来而是用直接传递给函数的方式传递[func(10,20...)],那么在声明函数实体的时候就不知道要声明多少个参数,这时可以不写参数名,在函数内部用arguments来代替,arguments是一个类数组,保存的是拥有这个对象的函数的参数的值,用索引的方法既可以获取对应的参数的值,使用如下:
function func(){ var res=0; for(var i=0;i<arguments.length;i++){ res+=arguments[i]+" | "; } return res; } alert(func(20,30,40));
arguments 中还有一个很重要的属性就是callee,是一个指针变量,指向的是拥有这个arguments类数组的函数,其实也就是arguments所在的函数本身,在递归的时候最好用这个属性,因为如果函数名改变了,也不需要改变内部的实现,arguments.callee()始终代表的是这个函数本身
//用arguments.callee的好处是,当函数名改变了以后,不需要改动递归出的函数, function box(num){ if(num<=1){ return 1; }else{ return num*arguments.callee(num-1); //arguments.callee代表的是函数的本身,故和上面是同样的效果 } } alert(box(3));//6
4、函数的内部属性 this
this 这个属性代表的是它所在的对象本身,和C#中this代表实体对象本身是一样的,this 引用的是函数以执行操作的对象,或者说函数调用语句所处的那个作用域
window是JS中最大的一个对象,在window对象中声明一个变量其实就是给window这个对象声明一个属性,var box=1;相当于 window.box=1;也就等价于this.box=1;
当在全局作用域中调用函数时,this 对象引用的就是 window。
在显示的声明一个对象box,这个box中使用的this就代表的是box本身,this.color就是返回的box中属性color的值,而不是window对象中color的值
// this 代表的是坐在的对象本身,下面在对象box中的this,就代表对象box var color='红色'; // window对象的属性, var box={ color:'蓝色', run:function(){ alert(this.color);//蓝色 return this.color; } } alert(window.color);//红色 alert(this.color); //红色 此处this代表的是window对象,故this.color是代表的window的属性 alert(box.run()); //蓝色 window.co='hh'; //和上面的color是等价的,都属于window对象的属性 alert(this.co);
5、函数的属性 length
函数的属性length代表的是函数希望接收的参数的个数,是由声明函数时参数列表中的个数决定的
6、函数的属性 prototype
prototype 是保存所有实例方法的真正所在,也就是原型。
prototype 下面有两个方法 apply(),call(),这两个方法都是函数非继承而来的方法, 是每一个函数都具有的方法。这两个方法的用途都是在特定的作用域中调用函数,也就是说调用特定的对象下面调用函数,(当然这个对象不一定已经声明有这个方法),实际上也就是等价于设置函数内部this对象的值,有点类似于C#的反射中调用对象的方法。
func.apply():这个方法有两个参数,第一个是要执行这个方法的作用域,也就是传递一个对象过去,第二个参数是一个数组,这个数组中是存放的调用的函数func的实参,也就是要传递给func的值,当然第二个参数可以省略。func.apply(this, [num1, num2]);。如果是在一个函数中调用可以用arguments把这个函数的参数传递过去,前提是这个类数组中有参数。但是最好是保证这个类数组中存放的是你想要传递给func函数的实参的值。虽然个数不匹配也不会出错,但是业务上可能不对
func.call():这个方法和上面的apply()方法是一样的,不同的是参数,第一个同样是作用域,是一样的,其余的参数是直接传递给函数的,而不是传递一个数组过去。就是说传递的参数顺序和方式是按照正常调用函数的形式和方法。func.call(this, num1, num2);
这两个方法最主要的功能是是能够扩展函数赖以运行的作用域,也就是改变函数运行所在的作用域
使用这apply()和call()这两个方法来扩充作用域最大的好处是对象不需要与方法发生任何的耦合关系,也就是上面说的对象中可能不存在这个方法,但只要存在这个对象就OK。【耦合:关联的意思,修改和维护会发生连锁反应】。也就是说 box 对象和sayColor()方法之间不会有多余的关联操作,比如 box.sayColor = sayColor;[ box对象中无需显示写上这条语句,也可以通过sayColor.call(box,'red')或者apply来调用方法sayColor,并且调用时sayColor方法的作用域是在对象box内部 ].
//call()和apply()方法最主要的是用于扩展函数依赖的作用域 var color='red'; var box={ color:'green' } function sayColor(){ return this.color; } alert(sayColor()); //red 对象是window alert(sayColor.call(this)); //red 对象是window alert(sayColor.apply(this)); //red 对象是window alert(sayColor.call(window)); //red 对象是window alert(sayColor.apply(window)); //red 对象是window alert(sayColor.call(box)); //green 对象是box alert(sayColor.apply(box)); //green 对象是box
练习的代码:以IE10为主
1 /* 函数的声明 2 3 //声明一个函数,没有参数和返回值 4 function func(){ 5 alert('diao yong di shi hou zhi xin'); 6 } 7 func(); 8 9 //声明一个函数,带有两个参数,由于JS是弱类型的语言,故函数的参数时不需要使用var的 10 function func(num1,num2){ 11 alert(num1+num2); 12 } 13 func(10,20); 14 15 //声明一个函数,带有两个参数和返回值,JS中返回值直接用return即可,函数名前不需要标注带有返回值 16 function func(num1,num2){ 17 return num1+num2; 18 } 19 alert(func(10,20));//30 20 21 //可以把函数的返回值直接赋值给一个变量,然后直接操作变量 22 function func(num1,num2){ 23 return num1+num2; 24 } 25 var a=func(10,20); 26 alert(a); //11 27 28 //return还有一个功能就是,结束函数的执行 29 function func(num1,num2){ 30 if(num1>10){ 31 return 11; 32 } 33 return num1+num2; 34 } 35 alert(func(13,20)); //11 36 37 // JS 调用的时候传递的参数个数和函数原型中参数个数可以不一样 38 function func(num1,num2){ 39 return num1+num2; 40 } 41 42 alert(func(20,'ahc',9)); 43 44 //ECMAScript 函数不介意传递进来多少参数,也不会因为参数不统一而错误。实际上, 45 函数体内可以通过 arguments 对象来接收传递进来的参数,arguments 可以用数组来取值 46 function func(){ 47 var res=0; 48 for(var i=0;i<arguments.length;i++){ 49 res+=arguments[i]+" | "; 50 } 51 return res; 52 } 53 54 alert(func(20,30,40)); 55 56 // JS 中没有函数重载的说法,函数和变量等的调用都是就近原则 57 function func(num1,num2){ 58 return num1+num2; 59 } 60 function func(num1){ 61 return num1; 62 } 63 alert(20,30);//20 执行的第二个函数,就近原则 64 65 //将一个函数的返回结果值作为另外一个函数的参数进行传递 66 function sum(num1,num2){ 67 return num1+num2; 68 } 69 70 function func(num1){ 71 return num1+10; 72 } 73 alert(sum(func(10),10));//30 74 75 //用变量初始化的方式声明一个函数,然后用变量名加参数列表来调用这个函数 76 //这种声明方法,就是给变量名注册一个事件的形式 77 var box=function(num1,num2){ 78 return num1+num2; 79 } 80 alert(box(10,10));//20 81 82 //使用析构函数来声明一个函数,但是这种方法效率低,因为会解析两次 83 //第一次解析整个表达式(ECMAScript常规代码),第二次就是解析传入构造函数中的字符串 84 //用这种方式能够更好的理解函数是对象,函数名是一个指针的概念 85 var box=new Function('num1','num2','return num1+num2'); 86 alert(box(10,10));20 87 88 */ 89 90 /* 91 //将一个函数作为结果返回 其实就相当于执行这个函数,然后把这个函数的返回值给返回 92 function sum(num1,num2){ 93 return num1+num2; 94 } 95 function func(num1){ 96 return sum(num1,10); 97 } 98 alert(func(10));//20 99 100 //Function类型其实也是一个对象,既然是对象,那也就具有和其他对象一样的属性方法等, 101 //其实函数名,也就是一个对象的变量,执行的是函数的地址,相当于一个指针 102 //将函数名当做一个参数传递给另外一个函数, 103 function sumFunc(num1,num2){ 104 return num1+num2; 105 } 106 function func(sum,num1){ 107 return sum(num1,10); 108 } 109 alert(func(sumFunc,10));//20 110 111 */ 112 113 /* 函数的内部属性 arguments 和 this 114 //函数的递归 115 function box(num){ 116 if(num<=1){ 117 return 1; 118 }else{ 119 return num*box(num-1);//自己调用自己 120 } 121 } 122 alert(box(3));//6 123 124 //arguments 是一个类数组,用来保存函数传递过来的参数,数组类型 125 //arguments.callee 是一arguments的一个属性,是一个指针变量, 126 //指向的是拥有这个argumens属性的函数,也就是说代表了他所在的这个函数本身 127 //用arguments.callee的好处是,当函数名改变了以后,不需要改动递归出的函数, 128 function box(num){ 129 if(num<=1){ 130 return 1; 131 }else{ 132 return num*arguments.callee(num-1); 133 //arguments.callee代表的是函数的本身,故和上面是同样的效果 134 } 135 } 136 alert(box(3));//6 137 138 // this 对象代表的是对象的本身 139 // 和 C# 中的this差不多,C# 中的this代表的也是实例化的对象的本身 140 //默认情况下 this 带表的是window对象,window是JS中最大的一个对象 141 //从下面可以看出 在window对象下 this就等于window 142 alert(window); //object Window 143 alert(typeof window); //object 144 alert(this); //object Window 145 alert(typeof this); //onject 146 147 var color='hong se'; // color 是window的一个属性 148 alert(window.color); //hong se 输出window的属性 149 alert(this.color); //hong se 和上面结果相等,说明this在此处代表的是window对象的本身 150 151 // this 代表的是坐在的对象本身,下面在对象box中的this,就代表对象box 152 var color='红色'; // window对象的属性, 153 154 var box={ 155 color:'蓝色', 156 run:function(){ 157 alert(this.color);//蓝色 158 return this.color; 159 } 160 } 161 alert(window.color);//红色 162 alert(this.color); //红色 此处this代表的是window对象,故this.color是代表的window的属性 163 alert(box.run()); //蓝色 164 165 window.co='hh'; //和上面的color是等价的,都属于window对象的属性 166 alert(this.co); 167 168 // 下面是通过给对象添加方法来改变this的作用域 169 window.color='红色'; //等价于 var color='红色' 170 171 var box={ 172 color:'蓝色' 173 }; 174 175 function sayColor(){ 176 alert(this.color); 177 } 178 179 sayColor(); // 红色 180 box.sayColor=sayColor; //给对象添加一个方法, 181 //上面这句话改变了 sayColor 的作用域在box对象里面,故这个方法里面的this也就是指的box对象 182 box.sayColor(); // 蓝色 183 184 */ 185 186 /* 函数的属性和方法 length,prototype[原型,标准] 187 188 // 函数中的length属性返回的是函数的参数的个数 189 function box(nm1,num2){}; 190 alert(box.length); // 2 191 function func(num1,num2,num3,num4){}; 192 alert(func.length); // 4 193 // prototype 属性是保存实例方法的真正所在的位置,也就是保存的原型 194 //包含两个非继承而来的方法 apply()和call()方法,都是用在特定的作用域中调用, 195 //相当于改变函数的this,也就是说改变函数所处的对象,有点像C#中的反射调用对象方法 196 197 // box.apply()方法,两个参数,第一个参数是要操作的对象,第二个参数是方法的参数数组 198 function sum(num1,num2){ 199 return num1+num2; 200 } 201 202 function saySum1(num1,num2){ 203 //第一个参数时代表的是要调用的是哪一个对象的sum方法,后面是这个方法的参数列表,注意传递的是一个数组 204 return sum.apply(this,[num1,num2]);//this相当于window 205 } 206 function saySum2(num1,num2){ 207 return sum.apply(this,arguments); 208 //arguments 代表的是当前函数[saySum2]的参数列表,是一个类函数,直接传递给函数sum 209 } 210 alert(saySum1(10,20)); //30 211 alert(saySum2(10,20)); //30 212 213 //第一个参数的不同,函数的作用域是不一样的 214 function sum(num1,num2){ 215 return num1+num2; 216 } 217 218 function saySum1(num1,num2){ 219 return sum.apply(this,arguments);//是在window对象中执行的 220 } 221 function saySum2(num1,num2){ 222 return sum.apply(box,arguments);//是执行在box对象中的方法 223 } 224 var box ={ 225 num1:'acd', 226 num2:'ddd', 227 } 228 alert(saySum1(10,20)); //30 229 alert(saySum2(10,10)); //20 230 231 //方法 call()和上面的apply()方法是一样的,仅仅在于接收参数的方式不同 232 //call()第一个参数同样是传递作用域对象,其余的参数是直接传给函数的,而不是传递一个数组 233 234 function sum(num1,num2){ 235 return num1+num2; 236 } 237 238 function saySum1(num1,num2){ 239 return sum.call(this,num1,num2) 240 } 241 242 function saySum2(num1,num2){ 243 return sum.call(box,num1,num2); 244 } 245 var box={}; 246 247 alert(saySum1(10,20));//30 248 alert(saySum2(20,20));//40 249 250 //call()和apply()方法最主要的是用于扩展函数依赖的作用域 251 252 var color='red'; 253 var box={ 254 color:'green' 255 } 256 257 function sayColor(){ 258 return this.color; 259 } 260 261 alert(sayColor()); //red 对象是window 262 alert(sayColor.call(this)); //red 对象是window 263 alert(sayColor.apply(this)); //red 对象是window 264 alert(sayColor.call(window)); //red 对象是window 265 alert(sayColor.apply(window)); //red 对象是window 266 alert(sayColor.call(box)); //green 对象是box 267 alert(sayColor.apply(box)); //green 对象是box 268 */