第三章 函数

3.1 什么是函数

一般来说,函数声明通常由以下几部分组成:

function子句

函数名称

函数所需参数

函数体

return子句。如果某个函数没有显示的返回值,默认它的返回值为undefined。

注意:一个函数只能有一个返回值,如果需要同时返回多个值,可以考虑将其放进一个数组里,以数组元素的形式返回。

function sum(a,b){
var c=a+b;
return c;
}
//忘了传递相关的参数值,javascript引擎就会自动将其设定为undefined
>>>sum(1)
NaN
//传递参数过多,多余的部分也只会被默默地忽略掉
>>>sum(1,2,3,4,5)
3
  • 每个函数内部都有一个内建的arguments数组(第四章,实际是一个类似数组的对象),它能返回函数所接收的所有参数。

      >>>function args(){return arguments;}
    >>>args();
    []
    >>>args(1,2,3,4,true,'ninja');
    [1,2,3,4,true,'ninja']

    通过arguments数组,可以进一步完善sum()函数功能,使之能对任意数量的参数执行求和运算。

      function sumOnSteroids(){
    var i,res=0;
    var number_of_params=arguments.length;
    for(i=0;i<number_of_params;i++){
    res+=arguments[i];
    }
    return res;
    }

3.2预定义函数

javascript引擎中有一组可供随时调用的内建函数。包括:

  • parseInt()

      >>>parseInt('123')
    123
    >>>parseInt('abc123')
    NaN
    >>>parseInt('1abc23')
    1
    >>>parseInt('123abc')
    123

    该函数还有个可选的第二参数:radix,负责设定函数所期望的数字类型(十进制、十六进制、二进制等)。

      >>>parseInt('FF',10)
    NaN
    >>>parseInt('FF',16)
    255
    >>>parseInt('0377',10)
    377
    >>>parseInt('0377',8)
    255

    如调用时没指定第二参数,默认为十进制,但有两种情况例外:首参数字符串是0x开头(十六进制),以0开头(八进制)

      >>>parseInt('377')
    377
    >>>parseInt('0377')
    255
    >>>parseInt('0x377')
    887
  • parseFloat()

      >>>parseFloat('123')
    123
    >>>parseFloat('1.23')
    1.23
    >>>parseFloat('1.23abc.00')
    1.23
    >>>parseFloat('a.bc1.23')
    NaN
    >>>parseFloat('123e-2')
    1.23
    >>>parseFloat('123e2')
    12300
  • isNaN()

    确定某个输入值是否是一个可以参与算术运算的数字。因而该函数可以用来检测parseInt()和parseFloat()的调用成功与否。

      >>>isNaN(NaN)
    true
    >>>isNaN(123)
    false
    >>>isNaN(parseInt('abc123'))
    true

    该函数也会始终试图将其所接收的输入转换为数字

      >>>isNaN('1.23')
    false
    >>>isNaN('a1.23')
    true

    NaN自己不存在等值的概念,也就是NaN===NaN返回的是false。(事实上,可以将NaN理解为一个集合,同属于一个集合的值未必是等值的)

  • isFinite()

    用来检查输入是否是一个既非infinity也非NaN的数字。

      >>>isFinite(Infinity)
    false
    >>>isFinite(-Infinity)
    false
    >>>isFinite(12)
    true
    >>>isFinite(1e308)
    true
    >>>isFinite(1e309)
    false
  • URI的编码与反编码

    URL(Uniform Resource Locator,统一资源定位符)

    URI(Uniform Resource Identifier,统一资源标识符)

    encodeURI(),反转函数decodeURI()

    encodeURIComponent(),反转函数decodeURIComponent()

  • eval()

    将其输入字符串当作javascript代码来执行

      >>>eval('var ii=2')
    >>>ii
    2

    eval is evil

    性能方面:是一种由函数执行的“动态”代码,显然要比直接执行脚本慢得多。

    安全性方面:不确定性大。

  • alert():不是javascript核心的一部分(即没有包括在ecma标准中)而是由宿主环境——浏览器所提供的,是一个用于显示文本的消息对话框。

    使用该函数会阻塞当前浏览器线程,在alert()执行窗口关闭之前,当前所有的代码都会暂停执行。

3.3 变量的作用域

  1. 在javascript中,我们不能为变量定义特定的块作用域,但可以定义其所属的函数域。也就是说,如果变量是在某个函数中定义的,那么它在函数以外的地方是不可见的。

    如果该变量是定义在if或者for这样的代码块中的,它在代码块之外是可见的。

    此外,在javascript中,全局变量是指声明在所有函数之外的变量。局部变量则指在某个函数中定义的变量。其中函数内的代码可以像访问自己局部变量那样访问全局变量,反之则不行。

  2. 下例中,请注意两点:函数f()可以访问变量global;在函数f()以外,变量local是不存在的。

    	var global=1;
    function f(){
    var local=2;
    global++;
    return global;
    }
    >>>f();
    2
    >>>f();
    3
    >>>local
    local is not defined

    ps:如果我们声明一个变量时没有使用var语句,该变量就会被默认为全局变量

    	function f(){local=2;}
    >>>f()
    >>>local
    2

    首先,在函数f()中定义了一个变量local。在该函数被调用前,local是不存在的。该变量在函数首次被调用时被创建,并赋予全局作用域,这使我们可以在函数外部访问它。

  3. 最佳实践

    • 尽量将全局变量的数量降到最低。(可能多人在同一脚本不同函数中使用相同全局变量,导致不可预测的结果和难以察觉的bug)

    • 总是使用var语句来声明变量。

关于本地和全局作用域的另一重要问题

var a=123;
function f(){
alert(a);
var a=1;
alert(a);
}
f();

错误答案:alert()第一次显示123(全局变量a的值),第二次1(局部变量a)

正确答案:“undefined”(函数域始终优先于全局域,所以局部变量a覆盖掉所有与它同名的全局变量,尽管在alert()第一次被调用时a还没有被正式定义(即该值为undefined),但该变量本身已经存在于本地空间了)

3.4 函数也是数据

函数是一种数据类型。也就是说,下面两种函数定义在本质上是相同的。

function f(){return 1;}
var f=function(){return 1;}

其中,第二种定义方式通常被叫做函数标识记法(function literal notation)。

对函数变量调用typeof,操作符返回字符串将会是“function”.

两个重要特性:

它们所包含的是代码;

它们是可执行的(或者说是可调用的)

下例与函数定义方式无关,演示的是如何像变量那样使用函数。

>>>var sum=function(a,b){return a+b;}
>>>var add=sum;
>>>delete sum
true
>>>typeof sum
"undefined"
>>>typeof add
"function"
>>>add(1,2);
3

命名规则:与一般变量相同,函数名不能以数字开头,可以由任意的字母、数字和下划线组合而成。

3.4.1 匿名函数

两种优雅的用法:

  • 我们可以将匿名函数作为参数传递给其他函数,这样,接收方函数就能利用我们传递的函数来完成某些事情(回调函数)
  • 我们可以定义某个匿名函数来执行某些一次性任务(自调函数)

3.4.2 回调函数

例1:

function invoke_and_add(a,b){
return a()+b();
}
function one(){return 1;}
function two(){return 2;}
>>>invoke_and_add(one,two);
3

事实上,可直接用匿名函数来代替one()和two(),作为目标函数的参数

invoke_and_add(function(){return 1;},function(){return 2;})

当我们将函数a传递给函数b,并由b来执行a时,a就成了一个回调函数(callback functions)。如果这是a还是一个无名函数,我们就称它为匿名回调函数。

回调函数的优势:

  • 它可以让我们在不做命名的情况下传递函数(这意味着可以节省全局变量);
  • 我们可以将一个函数调用操作委托给另一个函数(这意味着可以节省一些代码编写工作);
  • 有助于提升性能。

3.4.3 回调示例

定义两个函数:

//通过循环对接受参数乘以2
function multiplyByTwo(a,b,c){
var i,ar=[];
for(i=0;i<3;i++){
ar[i]=arguments[i]*2;
}
return ar;
}
//只接受一个值,加一返回
function addOne(a){
return a+1;
}

实现这三个元素在两个函数间传递

>>>var myarr=[];
>>>myarr=mulytiplyByTwo(10,20,30);
>>>for(var i=0;i<3;i++){myarr[i]=addOne(myarr[i]);}
>>>myarr
[21,41,61]

改善:对mulytiplyByTwo()函数做改动,使其接受一个回调函数,并在每次迭代操作中调用它。

function mulytiplyByTwo(a,b,c,callback){
var i,ar=[];
for(i=0;i<3;i++){
ar[i]=callback(arguments[i]*2);
}
return ar;
} >>>myarr=mulytiplyByTwo(1,2,3,addOne);
[3,5,7]

也可以用匿名函数代替addOne(),节省额外的全局变量。

>>>myarr=mulytiplyByTwo(1,2,3,function(a){return a+1});
[3,5,7]

使用匿名函数也更易于随时根据需求调整代码。例如:

>>>myarr=mulytiplyByTwo(1,2,3,function(a){return a+2});
[4,6,8]

3.4.4 自调函数

这种函数可以在定义后自行调用。只需将匿名函数的定义放进一对括号中,然后外面再紧跟一对括号即可。其中,第二对括号起到的是“立即调用”的作用,同时它也是我们向匿名函数传递参数的地方。

(
function(name){
alert('Hello'+name+'!');
}
)('dude')

好处是不会产生任何全局变量。缺点是无法重复执行(除非放在某个循环或其他函数中)。

匿名函数最适合于执行一些一次性的或初始化的任务。

3.4.5 内部(私有)函数

function a(param){
function b(theinput){
return theinput*2;
};
return 'The result is'+b(param);
};

也可以改用函数标识记法:

var a=function(){
var b=function(theinput){
return theinput*2;
};
return 'The result is'+b(param);
};

调用全局函数a()时,本地函数b()也会在内部被调用,b()在a()以外不可见,b()也被称之为私有函数。

>>>a(2);
"The result is 4"
>>>b(2);
b is not defined

使用私有函数的好处主要有以下几点:

  • 有助于确保全局名字空间的纯净性(意味命名冲突的机会很小)
  • 私有性(可以选择只将一些必要函数暴露给“外部世界”,并保留属于自己的函数,不为应用程序其他部分所用)

3.4.6 返回函数的函数

function a(){
alert('A!');
return function(){
alert('B!');
};
}

在这个例子中,函数a()会在执行它的工作(说“A!”)之后返回另一个函数b()。而b()又会去执行另外一些事情(说"B!")。我们只需将该返回值赋值给某个变量,然后就可以像使用一般函数那样调用它了。

>>>var newFunc=a();
>>>newFunc();

上面第一行执行的是alert('A!'),第二行执行的是alert('B!');

如果想让返回的函数立即执行,也可以不用将它赋值给变量,直接在该调用函数后面再加一对括号即可,效果是一样的。

>>>a()();

3.4.7能重写自己的函数

  • 从外部重定义函数(将函数返回值赋值给函数本身)

    上例中,也可以通过a()的返回值来重写a()函数自己:

      >>>a=a();//当前该句执行alert('A!'),但若再次调用a(),它就会执行alert('B!')了。

    这对于要执行某些一次性初始化工作的函数来说会非常有用。这样一来,该函数可以在第一次被调用后重写自己,从而避免了每次调用时重复一些不必要的操作。

  • 函数从内部重写自己

      function a(){
    alert('A!');
    a=function(){
    alert('B!');
    };
    }

    这样一来,当我们第一次调用该函数时:

    • alert('A!')将会被执行(可以视之为一次性的准备操作);
    • 全局变量a将会被重定义,并被赋予新的函数。

      而如果该函数再被调用的话,将执行的就将是alert('B!')了。
  • 组合型应用示例:

      var a=function(){
    function someSetup(){
    var setup='done';
    }
    function actualWork(){
    alert('Worky-worky');
    }
    someSetup();
    return actualWork;
    }();

    在这个例子中:

    • 使用了私有函数——someSetup()和actualWork()。
    • 也使用了自调函数——函数a()得定义后面有一对括号,因此它会执行自行调用。
    • 函数第一次被调用时,会调用someSetup(),并返回函数变量actualWork的引用。返回值中是不带括号的,因此该结果仅仅是一个函数引用,并不会产生函数调用。
    • 由于这里的执行语句是以var a=...开头的,因而该自调函数所返回的值会重新赋值给a。
  • 浏览器特性探测技术

    这项技术对于某些浏览器相关的操作会相当有用。因为在不同浏览器中,实现相同任务的方法可能不同,浏览器的特性不可能因为函数调用而发生任何改变。因此最好选择就是让函数根据其当前所在的浏览器来重定义自己。这就是所谓的“浏览器特性探测”技术。(后面章节展示示例)。

javascript面向对象编程笔记(函数)的更多相关文章

  1. javascript面向对象编程笔记(基本数据类型,数组,循环及条件表达式)

    javascript面向对象编程指南 最近在看这本书,以下是我的笔记,仅供参考. 第二章 基本数据类型.数组.循环及条件表达式 2.1 变量 区分大小写 2.3 基本数据类型 数字:包括浮点数与整数 ...

  2. javascript面向对象编程笔记(函数之闭包)

    3 函数 3.5 闭包(closures) 3.5.1 作用域链 与很多程序设计语言不同,javascript不存在大括号级的作用域,但它有函数作用域,即在函数内定义的变量在函数外是不可见的.但如果该 ...

  3. javascript面向对象编程笔记

    对象:一切事物皆是对象.对象是一个整体,对外提供一些操作.比如说一个收音机是一个对象,我们不需要知道它的内部结构是什么,只需要会使用外部的按钮就可以使用收音机. 面向对象:面向对象语言的标志是他们都有 ...

  4. 《JavaScript面向对象编程指南(第2版)》读书笔记(一)

    目录 一.对象 1.1 获取属性值的方式 1.2 获取动态生成的属性的值 二.数组 2.1 检测是否为数组 2.2 增加数组长度导致未赋值的位置为undefined 2.3 用闭包实现简易迭代器 三. ...

  5. 《JavaScript面向对象编程指南》读书笔记②

    概述 <JavaScript面向对象编程指南>读书笔记① 这里只记录一下我看JavaScript面向对象编程指南记录下的一些东西.那些简单的知识我没有记录,我只记录几个容易遗漏的或者精彩的 ...

  6. JavaScript面向对象编程学习笔记

    1  Javascript 面向对象编程 所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量.对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例 ...

  7. 《JavaScript面向对象编程指南(第2版)》读书笔记(二)

    <JavaScript面向对象编程指南(第2版)>读书笔记(一) <JavaScript面向对象编程指南(第2版)>读书笔记(二) 目录 一.基本类型 1.1 字符串 1.2 ...

  8. 《JavaScript面向对象编程指南》读书笔记①

    概述 JavaScript快忘完了,想看一本专业书拾遗,所以看了这本<JavaScript面向对象编程指南>. 个人觉得这本书讲的很透彻很易懂,一些原来有疑惑的地方在这本书里面豁然开朗,看 ...

  9. Javascript 面向对象编程(一):封装 by 阮一峰

    <Javascript高级程序设计(第二版)>(Professional JavaScript for Web Developers, 2nd Edition) 它们都是非常优秀的Java ...

随机推荐

  1. 假如Kafka集群中一个broker宕机无法恢复,应该如何处理?

    假如Kafka集群中一个broker宕机无法恢复, 应该如何处理? 今天面试时遇到这个问题, 网上资料说添加新的broker, 是不会自动同步旧数据的. 笨办法 环境介绍 三个broker的集群, z ...

  2. RQNOJ PID4 数列

    题目描述 给定一个正整数k(3≤k≤15),把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增的序列,例如,当k=3时,这个序列是: 1,3,4,9,10,12,13,… (该序列实际上就是 ...

  3. Django的日常-数据传输

    目录 Django的日常-1 Django中最常用的三个东西 HTTPresponse render redirect 静态文件相关 form表单的get与post 神奇的request 模板的传值方 ...

  4. DDOS到底是什么,怎么预防,看看就明白了

    可怕的DDOS怎么预防 分布式拒绝服务(DDoS: distributed denial-of-service)攻击是恶意破坏目标服务器.服务或网络的正常通信量的企图,其方法是用大量Internet通 ...

  5. AVR446步进电机算法推导及应用

    https://blog.csdn.net/Renjiankun/article/details/80513839?utm_source=copy

  6. Vue之获取用户当前所在省市

    今天小编给大家带来的是使用Vue获取用户所在城市,Vue是很强大的,给大家准备好现成的插件供大家调用,下面的Demo小编使用的是百度API. 首先我们从百度平台申请百度地图的秘钥,申请成功后我们将&l ...

  7. Vue引入日期格式插件moment.js

    因为需求需要,接口传递过来的日期格式是一个时间戳,因此需要进行格式转换,老大给了插件地址,让我自己研究 插件地址:http://momentjs.cn/ 因为没有使用过,所有就开始各种百度,参考各位大 ...

  8. 前端实现预览ppt,word,xls,pdf文件

    1.前端实现pdf文件在线预览功能 ps:刚好工作上有这个需求,所以到处找了一下处理方案,大家有需要可以试一下这几种方案,找到合适自己的 方式一. pdf文件理论上可以在浏览器直接打开预览但是需要打开 ...

  9. 微信小程序控件

    1   scrollview 窗口view的滑动 <scroll-view scroll-y class='scroll-view-y' bindscrolltoupper="uppe ...

  10. 检测apache状态,当apache处于非running状态如何脚本启动

    编写脚本内容 #!/bin/bashURL="http://127.0.0.1/"curlit(){curl --connect-timeout 15 --max-time 20 ...