我在上一篇【javascript基础】基本概念中介绍了javascript的一些基本概念,多谢你们的阅读和意见,本身写的东西能够被你们阅读,真心高兴,刚开始发布的时候我一直盯着阅读人数,虽然知道你们可能就是点开一下而已,可是仍是给我一些继续写下去的信心。那今天写一些关于javascript函数的一些知识,帮助你们熟悉或者复习一些函数的基本知识。javascript
PS:最近jQuery源码交流群( 239147101)加了很多热新人,但愿你们仍是以学习为主,尽可能少灌水,给你们一个好的提高本身的环境。html
函数在任何一种编程语言中都是一个很重要的结构或者组成部分,编程中的复杂结构和功能都会有函数的参与。javascript中的函数是一个对象,函数对象时Function类型的实例,因为Function类型是一个引用类型,那么函数能够拥有本身的方法和属性,同时也由于函数是一个对象,那么函数名是一个指向函数对象的指针,能够被赋值。下面详细介绍函数的各个部分。java
函数的建立有三种方式,分别为使用Function的构造函数、函数声明、函数表达式,下面分别介绍这三种方法。面试
这种方式是直接new出来一个Function 实例,经过使用Function的构造函数进行建立函数。Function构造函数能够接收任意多个参数,可是最后一个参数会被认为是函数体,前面的因此参数被当作被建立出来的函数的参数。编程
var test = new Function("a","b","return a + b");//参数a和b,函数体return a + b console.log(test(1,2));//3
咱们能够看出比较的麻烦,而且《javascript高级程序设计》也不推荐咱们使用这种方式,主要是由于浏览器要解析常规的javascript代码以外,还要解析传入的参数字符串,这个相似eval()的解释,影响性能。数组
这种方式是建立的常见方式之一,具体请看浏览器
var test = function(a,b){ return a + b; }
console.log(test(1,2));
上面的代码就是建立一个函数,使用test()进行调用。其实,上面的代码是先建立了一个匿名的函数,以后把这个匿名的函数赋值给test变量。每一个函数有一个name属性,这个属性不是ECMA标准的一部分,可是许多地方可使用它。咱们能够给上面的函数起一个名字,具体下面代码app
//函数的名字newName var test = function newName(a,b){ return a + b; } console.log(test.name);//newName //匿名函数 var nTest = function (a,b){ return a + b; } console.log(nTest.name);//""
这个属性在后面详细解释吧。编程语言
这种方式和C语言中的很相似,这种是最多见的一种建立函数的方法。是经过关键字function直接声明,请看函数
function test(a,b){ return a + b; } console.log(test(1,2));//3 console.log(test.name);//test
以上介绍了三个建立函数的方式,如今介绍三种的区别,确切的说是后两种的区别,由于Function不推荐使用,性能是一大缘由。区别就是使用函数声明这种方式会使函数的声明提早,相似前面咱们提到的变量申明的提早。也就是说,使用函数申明方式,咱们能够将函数的声明放在调用函数代码的后面,由于解析器会在代码执行以前将函数的声明提高,提到源代码树的顶部,而函数表达式方式则会报错,具体请看
//调用函数 console.log(test(1,2));//3 //建立函数(函数申明方式) function test(a,b){ return a + b; } //上面的函数相等于 //建立函数(函数申明方式) //function test(a,b){ // return a + b; //} //console.log(test(1,2));//3 //调用函数 console.log(ntest(1,2));//TypeError: undefined is not a function //建立函数(函数表达式方式) var ntest = function (a,b){ return a + b; }
javascript语言不像java那些语言有函数重载这一律念,其实函数名就是一个指针,指向一个Function实例的地址,固然只能指向一个函数,固然没有重载的概念了,只有覆盖,后面定义的函数覆盖前面定义的函数,具体请看
function test(a,b){ return a + b; } //下面的函数覆盖上面的 function test(a,b){ return a + b + 100; } console.log(test(0,0));//100
也就是说若是一个同名的函数表达式和函数申明的函数在一块儿,不管位置是怎么样的,最后的函数就会是用函数表达式建立的函数,由于函数申明会提高到顶部嘛,看看下面的代码
var test = function (a,b){ return a + b -100; } function test(a,b){//会被下面的函数覆盖 return a + b; } function test(a,b){//会被函数表达式覆盖 return a + b + 100; } console.log(test(0,0));//-100
函数的内部有两个重要的对象:arguments和this。
arguments是一个相似组对象,包含因此传入函数的全部参数, 写程序或者面试中常问的就是如何将arguments转化完成一个数组,请看
Array.prototype.slice.call(arguments); Array.prototype.slice.call(arguments,0); Array.prototype.slice.call(arguments,0,arguments.length); Array.apply(this, arguments); //没用过 Array.apply(null, arguments); //没用过
arguments有一个length属性,表示函数传入参数的个数,还有一个callee属性,这是一个指针,指向拥有这个arguments的函数,这个主要是在函数内部调用本身时使用,也就是递归时使用。看个例子就明白了
function test(count){ console.log("参数:"+arguments[0]+"个数:"+arguments.length); if(count <= 0){ console.log("递归"+count+" 结束了"); }else{ console.log("递归"+count); arguments.callee(--count);//调用本身 } } test(3); /* 参数:3个数:1 递归3 参数:2个数:1 递归2 参数:1个数:1 递归1 参数:0个数:1 递归0 结束了 */
javascript中的this和java中的this差很少,this引用的是函数的执行环境,就是this在不一样的执行环境中引用的是不一样的对象,执行环境这里尚未说到,之后会详细介绍,这里的this也是简单的介绍一下,我之后会整理一些面试题,帮助你们理解。看例子吧,
//全局变量 var color = "red";//至关于window.color = "red" //定义一个对象 var obj = {color : "blue"}; function pop(color){ alert(this.color); } pop();//至关于window.pop();输入"red" //obj对象增长一个方法,将pop赋值给它 obj.pop = pop; obj.pop(); //输出"blue"
解释一下,this这个对象是在函数执行时才绑定的,能够说是一个动态的。pop函数是定义在window下的一个函数,也就是全局做用域的一个函数,当直接执行pop函数时,就是在全局做用域下调用pop时。this引用的是window,this.color就是window.color,输出red。当咱们把pop这个函数赋值给obj这个对象而且调用pop的时候,this引用的就是obj这个对象,this.color 就是obj.color,输出blue。
这里说一下函数的属性和方法,包括length,name,prototype,apply,call这几个。
这个属性比较简单,就是表示定义函数时定义的参数的个数,要和arguments.length区分开,arguments.length表示实际输入的参数个数,看例子
function test(a,b){ console.log("输入的参数个数:"+arguments.length); console.log("定义的参数个数:"+test.length); } test();//0,2 test(1);//1,2 test(1,2)//2,2 test(1,2,3)//3,2 //函数的内部咱们能够经过arguments[i],取得输入的参数,假如定义一个参数,输入两个参数,那怎么取得第二个参数呢 function testA(c){ console.log("输入的参数个数:"+arguments.length); console.log("定义的参数个数:"+test.length); console.log("第二个参数:"+arguments[1]); } testA(1,100);2,2,100 //这里能够遍历取得全部的参数,不讲了
这个属性在前面提到了一点,这个就是函数的名字,咱们在建立函数的时候说了这个属性,这个属性不是标准属性,可是不少地方就使用这个属性,主要也是在递归调用上使用。name属性是只读属性,不能修改它的值。直接看例子
//修改name属性 function test(){ console.log(test.name); //修改name属性 test.name = "newName"; console.log(test.name); } test();//test,test //函数内部使用name属性,递归调用 function testD(count){ console.log("参数:"+arguments[0]+"个数:"+arguments.length); if(count <= 0){ console.log("递归"+count+" 结束了"); }else{ console.log("递归"+count); testD(--count);//调用本身 } } testD(3); /* 参数:3个数:1 递归3 参数:2个数:1 递归2 参数:1个数:1 递归1 参数:0个数:1 递归0 结束了 */
name属性的使用和arguments.callee()的效果是同样的,只不过arguments.callee()更方便些,当函数名字更改时程序不用更改。
函数的prototype属性是一个很重要的属性,特别是在自定义引用类型和实现继承时。咱们如今这简单的介绍一下它,由于这个属性足以单独写一篇文章。咱们能够认为prototype是一个模板,在new 一个对象时候会参照这个模板,将模板里的属性和方法复制给对象,固然你不定义这个模板,这个模板不存在方法和属性。简单例子
function People(name){ this.name = name; } //prototype中的属性 People.prototype.home = "jilin"; var hainan = new People("hainan"); console.log(hainan.home);//jilin
先简单介绍到这,后面单独详细说。
这两个方法做用是同样的,就是改变this做用域的值,在特定的做用域中调用本身,也就是设置this的指向,不一样点在于参数接收方式不一样。apply方法须要两个参数,第一个是指定的做用域,就是要把this指向的对象,第二个是参数数组,函数调用须要的参数,这个参数数组也能够是arguments这个伪数组。call的第一个参数也是给this绑定的值,其余的参数个数不定,其余的参数就是函数调用须要的参数,和apply不一样,你要一个一个的都列举出来。看例子
function sum(a,b){ return a + b; } //arguments参数 function callSum1(a,b){ return sum.apply(this,arguments); } //数组参数 function callSum2(a,b){ return sum.apply(this,[a,b]); } //列举全部参数 function callSum3(a,b){ return sum.call(this,a,b); } console.log(callSum1(1,2)); console.log(callSum2(1,2)); console.log(callSum3(1,2));
上面是传递参数的例子,再看看改变this指向的例子
//全局变量 var color = "red";//至关于window.color = "red" //定义一个对象 var obj = {color : "blue"}; function pop(color){ alert(this.color); } pop();//至关于window.pop();输入"red" pop.call(this);//red pop.call(obj);//blue
解释一下,pop.call(this)这句代码改变了this的指向,由于是在全局中调用的函数,this指向window,输出window.color。pop.call(obj)这句代码将this指向了obj,因此输出obj.color。
把函数这部分的基础和你们说了一下,本身讲代码敲了一遍实验了一下,有些东西看着容易懂,写起来仍是挺困难的,但愿你们也要多写写吧,我一直觉得做文就很差,这是难为你们了。要放假了,有点想家了,想吃家里的酸菜了。