【JavaScript系列】你应掌握的JavaScript之函数(一)

在编程语言中,无论是面向过程的C,兼备面过程和对象的c++,还是面向对象的编程语言,如java,.net,php等,函数均扮演着重要的角色。当然,在面向对象编程语言JavaScript中(严格来说,JS属于弱面向对象编程语言),函数(function)更扮演着极其重要的角色和占有极其重要的地位。在本篇文章中,不论述什么是JS,JS解决什么问题等之类问题,而是重点阐述JS中的函数(function)。 一 什么是javascript函数 1.函数定义 关于函数的定义,我们先从两个角度来思考:数学角度和编程语言角度。 (1)数学角度:在数学领域,关于“函数”二字,再熟悉不过,如三角函数,反三角函数,幂函数,对数函数,指数函数,微积分函数等; (2)编程角度:在编程领域,大家最熟悉且最先接触的应该是"Main函数"了,除此外,如日期函数(Date),数学函数(Math)等,当然除了内置函数外,还包括用户自定义函数; 综合1,2点,我们不难发现,函数的定义应该是这样的: 函数是解决某类问题的集合,是某类问题的高度抽象,它具有一定的通用性和复用性。 2.js中两种经典函数定义 在Javascript中,存在两种经典的函数定义方式:函数声明式和函数表达式 (1)函数声明式 复制代码 1 //定义两个数相加函数 2 function AddNum(num1, num2) { 3 return num1 + num2; 4 } 复制代码 (2)函数表达式 复制代码 1 //定义两个数相加函数 2 var AddFun=function AddNum(num1, num2) { 3 return num1 + num2; 4 } 复制代码 注意,在用函数表达式定义时,一般采用匿名函数定义,即如下: 复制代码 1 //定义两个数相加函数 2 var AddFun=function (num1, num2) { 3 return num1 + num2; 4 } 复制代码 Question:为什么函数表达式用匿名函数,而函数声明式不用匿名? 答:因为函数表达式调用时,使用的是函数表达式名,不需要函数名,因此函数名可以匿名,而函数声明式调用时,使用函数名调用,因此不用匿名函数; 声明式调用1: 复制代码 1 //定义两个数相加函数 2 function AddNum(num1, num2) { 3 return num1 + num2; 4 } 5 6 7 console.log(Add(10,20));//30 复制代码 声明式自调用: 自调用只存在函数声明式中,也叫立即调用,不能在在函数表达式中调用。 复制代码 1 (function AddNum(num1, num2) { 2 console.log(num1 + num2); 3 })(10,20);//30 复制代码 声明式调用3:错误调用方式 请大家想想,这种调用方式为什么会错? 复制代码 1 function (num1, num2) { 2 return num1 + num2; 3 } 4 5 console.log((10,20)); 复制代码 调用结果: 表达式调用1:推荐写法 复制代码 1 //定义两个数相加函数 2 var AddFun=function (num1, num2) { 3 return num1 + num2; 4 } 5 6 console.log(AddFun(10,20));//30 复制代码 表达式调用2:不推荐写法 复制代码 1 //定义两个数相加函数 2 var AddFun=function AddNum(num1, num2) { 3 return num1 + num2; 4 } 5 6 console.log(AddFun(10,20));//30 复制代码 表达式调用3:错误调用方式 请大家想想,这种调用方式为什么会错? 复制代码 1 //定义两个数相加函数 2 var AddFun=function AddNum(num1, num2) { 3 return num1 + num2; 4 } 5 6 console.log(AddNum(10,20));//30 复制代码 调用结果: 3.变量 在JavaScript编程语言中,变量的定义是通过var关键字来定义的(若变量不通过var定义,则为全局变量,但不推荐这么做),与其他编程语言一样,变量也分为两大类,即局部变量和全局变量。 (1)局部变量:作用域为其所在的函数; (2)全局变量:作用域为整个过程; (3)变量作用域:JS中的变量作用域是通过this指针,从当前的作用域开始,从当前作用域由内向外查找,直到找到位置,这里分为几个逻辑: a.从当前作用域由内向外查找,若找到,就停止查找,否则,继续查找,直到查到window全局作用域为止; b.当内部作用域变量名与外部作用域变量名相同时,内部作用域的覆盖外部作用域。 我们来看一个例子: 复制代码 1 var dateTime='2018-09-16'; 2 function GetUserInfo(){ 3 var age=120; 4 var name="Alan_beijing"; 5 function Say(){ 6 var name="老王"; 7 var address="shanghai"; 8 console.log(address+"-"+name+"-"+age+"-"+dateTime);//shanghai-老王-2018-06-05 9 } 10 return Say(); 11 } 12 13 14 GetUserInfo();//shanghai-老王-120-2018-09-16 复制代码 来分析一下变量及其作用域: 如上图,有4个作用域,当函数执行如下语句时,发生如下过程: 1 console.log(address+"-"+name+"-"+age+"-"+dateTime); a.js当前this环境作用域为4作用域; b.this指针寻找变量:addresss,name,age,dateTime,从当前作用域向外作用域逐层寻找,知道寻找到变量值为止,若寻找到最外层作用域任然没找到,则该变量返回undefined; c.当内外层变量相同时,内层变量覆盖外层变量,如4作用域的name覆盖3作用域的name; 4.函数声明式定义存在的问题 在js中,存在声明提前问题,看看如下例子。 复制代码 1 var globleName="Alan_beijing"; 2 function Say(){ 3 console.log(localName); // undefined,不报错,是因为变量声明提前 4 var localName="Alan"; 5 console.log(localName);// Alan 6 } 复制代码 看过如上代码,你可能会问,函数执行到console.log(localName); 时,应该报错,因为localName未定义。 如果在后端语言,如java,.net中,可能会报错,但是在js中,却不会,不报错的原因是:在js中存在声明提前。 如上代码相当于如下代码: 复制代码 1 var globleName="Alan_beijing"; 2 function Say(){ 3 var localName; 4 console.log(localName); 5 localName="Alan"; 6 console.log(localName); 7 } 复制代码 二 函数几大关键点 1.匿名函数 匿名函数,顾名思义,就是没名字的的函数,我们来看看如下两个例子: 函数表达式 复制代码 1 //定义两个数相加函数 2 var AddFun=function (num1, num2) { 3 return num1 + num2; 4 } 复制代码 立即执行函数 复制代码 (function AddNum(num1, num2) { console.log(num1 + num2); })(10,20);//30 复制代码 从如上,不难看出,匿名函数主要用域函数表达式和立即执行函数。 2.闭包 闭包的根源在于变量的作用域问题。 我们先来考虑这样一个问题,假设在面向对象编程语言中,某个方法的变量被定义为私有变量,其他函数要获取访问该变量,.net怎么处理? 方法一:构造函数 方法二:单例模式 同样地,在js中,同样存在外部函数调用内部函数变量问题,js运用的技术就叫做闭包。 所谓闭包,就是将不可访问的变量作为函数返回值的形式返回来,从而实现函数外部访问函数内部变量目的。 复制代码 1 //闭包 2 function GetName() { 3 var name = "Alan_beijing"; 4 var age = function () { 5 var age = 30; 6 return age; 7 } 8 return name + age; 9 } 复制代码 3.js多态问题(重载问题) 在面向对象编程语言,如.net中,实现多态的方式大致有如下: a.接口 b.抽象类 c.虚方法 d.方法重载 然而,在js中,没有面向对象之说(OO),那么js是如何实现多态的呢?根据方法实际传递的参数来决定。 复制代码 1 //重载 2 function SetUserInfo(userName, age, address, tel, sex) { 3 console.log(arguments.length);//4 4 } 5 6 SetUserInfo('Alan_beijing',44,'china-shanghai','xxxx'); 复制代码 从如上可以看出,传递多少个参数,就接收多个参数,如果在现象对象编程语言中实现该功能,至少需要写一堆代码,这也是体现js强大之一。 4.递归 来看看一个递归阶乘函数 复制代码 1 //递归 2 function factorial(num) { 3 if (num < 1) { 4 return 1; 5 } else { 6 return num * arguments.callee(num-1); 7 } 8 } 复制代码 如果是.net,我们一般会这样写 复制代码 1 //递归 2 function factorial(num) { 3 if (num < 1) { 4 return 1; 5 } else { 6 return num * factorial(num-1); 7 } 8 } 复制代码 然而,这样写,却会存在异常情况 复制代码 1 var factorial1 = factorial; 2 factorial = null;//将factorial变量设置为null 3 console.log(factorial1(4));//出错 复制代码 5.原型和原型链 面向对象编程语言的显著特征之一是面向对象,然而,在js中,没有对象,那么js是如何面向对象的功能的呢(封装,继承,多态)?当然是通过原型和原型链来实现的。 大家都比较怕原型和原型链,其实很简单,它的功能相当于面向对象的继承,主要解决继承和复用。 介于篇幅有限,余下的内容,将在下篇文章阐述..... 三 参考文献 【01】JavaScript 高级程序设计(第三版) (美)Nicholas C.Zakas 著 李松峰 曹力 译 【02】JavaScript 权威指南 (第6版) David Flanagan 著 https://www.cnblogs.com/wangjiming/p/9656942.html
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信