【javascript 函数基础知识】
函数实际上是对象,每个函数都是 Function 类型的实例,而且都会与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
【概念标签】
函数申明提升 函数表达式 匿名函数 作为值的函数 arguments 对象 callee 的属性 递归函数 函数的 call() 方法 函数的 bind() 方法 闭包 函数重载
【定义函数的方法】
1.定义函数的三种方法:函数申明和函数表达式。
函数申明的语法如下:
function functionName(arg0,arg1,agr2){
    //函数体
}
函数申明有一个重要特征是函数申明提升,即在代码执行之前会先读取函数申明。这意味着可以把函数的申明语句放在调用它的语句后面,例:
sayHi();
function sayHi(){
alert('hi');
}
函数表达式语法如下:
var myFunction = function(arg0,arg1,arg2){
    //函数体
};
函数申明 {} 末尾不需要添加分号,而使用函数表达式的时候 {} 末尾需要添加分号,就像申明其他变量一样。
这种情形看起来像是常规的变量赋值语句,即创建一个函数并将它赋值给变量myFunction 。这种情况下创建的函数叫匿名函数,因为function关键字后面没有跟标识符。函数表达式和其他表达式一样,在使用前必须先赋值,以下的代码会导致错误:
sayHi(); //错误:函数还不存在
var sayHi = function(){
alert('hi');
}
使用 Function 构造函数(不推荐使用),语法如下:
function sum = new Function ('num1','num2','return num1 + num2');
【作为值的函数】
函数名本身就是一个变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。例如,假设有一个对象数组,我们想要根据某个对象属性对数组进行排序。而传递给数组 sort() 方法的比较函数要接收两个参数,即要比较的值。可是我们需要一种方式来指明按照哪个属性来排序。下面我们来实现这个案例。要解决这个问题,可以定义一个函数,它接收一个属性名,然后根据这个属性来创建一个比较函数,代码如下:
function createComparisonFunction(propertyName){
    return function(obj1,obj2){
        var value1 = obj1[propertyName];
        var value2 = obj2[propertyName];
        if(value1 < value2){
            return -1;
        }
        if(value1 > value2){
            return 1;
        }
        else{
            return 0;
        }
    }
}
这里要注意一行代码,var value1 = obj1[propertyName]; 这条语句使用方括号表示法来取得给定属性的值,语句的执行结果与 obj1.propertyName 是一样的。需要注意的是,在使用方括号语法时,应该将要访问的属性以字符串的形式放在方括号中。例如:
person['name'];
person.name;
接下来我们创建一个对象数组,并以指定的方式对这个对象数组进行排序,代码如下:
var data = [{name : 'Zachary',age : 19},{name : 'Nicolas',age : 29}];
data.sort(createComparisonFunction('name'));
console.log(data[0].name);   // Nicolas
data.sort(createComparisonFunction('age'));
console.log(data[0].name);   // Zachary
这里我们涉及到一个 sort() 方法,该方法用于对数组的元素进行排序。例如以下的代码实现了对数组元素按照从小到大的顺序排列:
function sortNumber(a, b)
{
var obj = a - b;
return obj;
} var arr = new Array(6)
arr[0] = "10";
arr[1] = "5";
arr[2] = "40";
arr[3] = "25";
arr[4] = "1000";
arr[5] = "1"; document.write(arr + "<br />");
document.write(arr.sort(sortNumber));
代码的输出结果为:
10,5,40,25,1000,1
1,5,10,25,40,1000
【函数的内部属性】
在函数内部,有两个特殊的对象, arguments 和 this 。
首先介绍 arguments 对象。arguments 是一个类数组的对象,并不是 Array 的实例,它包含着传入函数中的所有参数,可以通过方括号语法访问它的每一个元素。使用 arguments 的 length 属性可以确定传进来多少个参数,例如以下代码:
function showArguments(){
    console.log(arguments.length,arguments[0],arguments[1],arguments[1]);
}
showArguments('arg1','arg2','arg3');
代码的输出结果为:3 "arg1" "arg2" "arg2"
arguments 对象有一个名叫 callee 的属性,这个属性是一个指针,指向拥有 arguments 对象的函数。以下代码实现了经典的递归阶乘函数:
function factorial(num){
    if(num <= 1){
        return 1;
    }
    else{
        return num * factorial(num - 1);
    }
}
在这里我们用到了一个递归算法,需要熟悉递归函数的概念,递归函数是在一个函数通过名字调用自身的情况下构成的。但是这个函数的执行与函数名 funcFactorial 紧紧耦合在了一起,为了消除这种紧密的耦合现象,我们可以使用 arguments.callee ,代码如下:
function factorial(num){
    if(num < 1){
        return 1;
    }
    else{
        return num * arguments.callee(num - 1);
    }
}
这样的话无论引用函数时使用什么名字,都可以保证正常完成递归调用。例如以下代码:
function factorial(num){
    if(num < 1){
        return 1;
    }
    else{
        return num * arguments.callee(num - 1);  //使用了 arguments.callee
    }
}
var newFactorial = factorial;
factorial = function(){
    return 0;
}
var result1 = factorial(5);  //
var result2 = newFactorial(5);  //
console.log(result1,result2);
如果我们不使用 arguments.callee ,例如以下的代码,便不能够正确的调用 newFactorial 函数:
function factorial(num){
    if(num < 1){
        return 1;
    }
    else{
        return num * factorial(num - 1);  //没有使用 arguments.callee
    }
}
var newFactorial = factorial;
factorial = function(){
    return 0;
}
var result1 = factorial(5);  //
var result2 = newFactorial(5);   //
console.log(result1,result2);
这是什么原因呢?我自己思考了一下,得到的结论如下:我们使用了这样一个语句 var newFactorial = factorial ,使用不带圆括号的函数名是访问函数指针,而不是调用函数。所以 newFactorial 访问到的是 factorial 的函数指针,相当于我们把递归函数变成了如下的样子:
function newFactorial(num){
    if(num < 1){
        return 1;
    }
    else{
        return num * factorial(num - 1);
    }
}
这个时候 else 语句还在执行 factorial ,显然是调用不到自身函数的,所以如果我们把代码改为如下的样子:
function factorial(num){
    if(num < 1){
        return 1;
    }
    else{
        return num * newFactorial(num - 1);  // 改动的地方
    }
}
var newFactorial = factorial;
factorial = function(){
    return 0;
}
var result1 = factorial(5);  //
var result2 = newFactorial(5);  //
console.log(result1,result2);
这样我们便可以输出我们想要的结果了。
接下来介绍 this 对象。this 引用的是函数据以执行的环境对象,来看下面的例子:
window.color = 'red';
var o = {color : 'blue'};
function sayColor(){
console.log(this.color);
}
sayColor(); // red
o.sayColor = sayColor;
o.sayColor(); // blue
上面的这个函数 sayColor() 是在全局作用域中定义的,它引用了 this 对象。由于在调用函数之前 this 的值并不确定,因此 this 可能会在代码执行过程中引用不同的对象。当在全局作用域中调用 sayColor() 的时候, this 引用的就是全局对象 window ,所以对 this.color 的求值就转换成了对 window.color 的求值。当我们把这个函数赋值给对象 o 并调用 o. sayColor() 时, this.color 求值就会转换为 o.color 求值。
【函数的 call() 方法】
call() 方法强大的地方在于它可以扩充函数的作用域,检查如下的代码:
window.color = 'red';
var o = {color : 'blue'};
function sayColor(){
console.log(this.color);
}
sayColor(); // red
sayColor.call(this); // red
sayColor. call(window); // red
sayColor.call(o); //blue
观察代码的最后一行,当运行 sayColor.call(o) 的时候,函数体内的 this 对象指向了 o ,这样便可以不用之前的 o.sayColor = sayColor;o.sayColor(); 这个语句了。
【函数的 bind() 方法】
这个方法会创建一个对象实例,其 this 的值会被绑定到传给 bind() 函数的值。检查如下代码:
window.color = 'red';
var o = {color : 'blue'};
function sayColor(){
console.log(this.color);
}
var newSayColor = sayColor.bind(o);
newSayColor(); //blue
【闭包】
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另外一个函数。
【没有重载】
函数重载:同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,也就是说用同一个运算符完成不同的运算功能。这就是重载函数。重载函数常用来实现功能类似而所处理的数据类型不同的问题。
将函数名想象为指针,有助于理解为什么 ECMAScript 中没有函数重载的概念。请看如下代码:
function addNumber(str){
    return str + '100';
}
function addNumber(num){
    return num + 200;
}
window.onload = function(){
    var addContent = addNumber('100');
    console.log(addContent);
}
设想一下,倘若函数重载生效的话,以上代码输出的内容应该为 : 100100 ,而正确的输出结果为 : 100200 。再考察如下的代码:
function addNumber(str1,str2){
    return str1 + str2 + '102';
}
function addNumber(num){
    return num + 200;
}
window.onload = function(){
    var addNum = addNumber('100','101');
    console.log(addNum);
}
输出结果为 : 100200
函数是编程重要的一部分,需要加强练习,多多学习。贴一句鼓励自己的话在这里:不会不可怕,不学才可怕。
【javascript 函数基础知识】的更多相关文章
- 快速掌握JavaScript面试基础知识(三)
		
译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer interv ...
 - 快速掌握JavaScript面试基础知识(二)
		
译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer interv ...
 - javascript的基础知识及面向对象和原型属性
		
自己总结一下javascript的基础知识,希望对大家有用,也希望大家来拍砖,毕竟是个人的理解啊 1.1 类型检查:typeof(验证数据类型是:string) var num = 123; cons ...
 - JavaScript 函数基础
		
1. JavaScript 函数基础 1. 定义方法 2. 函数的调用方法 3. 函数方法 apply : 将函数作为数组的方法来调用 将参数以数组形式传递给该方法 call : 将函数作为对象的 ...
 - javaScript系列 [01]-javaScript函数基础
		
[01]-javaScript函数基础 1.1 函数的创建和结构 函数的定义:函数是JavaScript的基础模块单元,包含一组语句,用于代码复用.信息隐蔽和组合调用. 函数的创建:在javaScri ...
 - JavaScript 之基础知识
		
JavaScript 基础知识 JavaScript 是属于网络的脚本语言! JavaScript 被数百万计的网页用来改进设计.验证表单.检测浏览器.创建cookies,以及更多的应用. JavaS ...
 - JavaScript笔记——基础知识(一)
		
<Script>标签属性 <script>xxx</script>这组标签,是用于在 html 页面中插入 js 的主要方法.它主要有以下 几个属性: charse ...
 - 10分钟学会Python函数基础知识
		
看完本文大概需要8分钟,看完后,仔细看下代码,认真回一下,函数基本知识就OK了.最好还是把代码敲一下. 一.函数基础 简单地说,一个函数就是一组Python语句的组合,它们可以在程序中运行一次或多次运 ...
 - JavaScript对象基础知识总结
		
1.什么叫JavaScript对象? 定义:名值对的集合.简单的讲就是容纳属性值和属性值的容器,这些属性可以是无序的,基本上JavaScript中所有的事物都可以看成对象. 拓展:我们经常说,数组也是 ...
 
随机推荐
- Extended ComboBox添加图标
			
Extended ComboBox添加图标 关键点 实现过程 // MFC02Dlg.h : header fileCImageList m_imageList; // MFC02Dlg.cpp : ...
 - Android ListFragment实例Demo(自己定义适配器)
			
上一篇文章介绍了ListFragment,当中的ListView并没有自己定义适配器,实际上在实际开发中常会用到自己定义适配器,是实现更复杂的列表数据展示. 所以这篇文章添加了自己定义适配器.来进行L ...
 - Nessus漏洞扫描教程之配置Nessus
			
Nessus漏洞扫描教程之配置Nessus 配置Nessus 当安装成功Nessus工具后.就可以使用该工具实施漏洞扫描.为了使用户更好的使用该工具,将介绍一下该工具的相关设置.如服务的启动.软件更新 ...
 - Jmail的邮件发送
			
下载注册dll文件 1. dll文件下载 2.到jmail.dll所在目录,运行cmd regsvr32 目录/jmail.dll 3.c#程序中,行首引用代码 using jmail C#示例代码 ...
 - Some current MySQL Architecture writings
			
Posted on 19/09/2014 by Stewart Smith So, I’ve been looking around for a while (and a few times now) ...
 - quartz源码分析之深刻理解job,sheduler,calendar,trigger及listener之间的关系
			
org.quartz包 包org.quartz是Quartz的主包,包含了客户端接口. 其中接口有: Calendar接口: 定义了一个关联Trigger可能(或者不可能)触发的时间空间.它没有定义触 ...
 - opai_suki
 - volatile--共享数据必须保证可见性
			
在Effective Java中看到的,试了一下,有点意思,考查的知识点是volatile关键字. 下面这段代码,预期是打印it takes xxxx miliseconds. .但实际上,陷入了死循 ...
 - Apache 80 端口被占用无法重启解决办法
			
原文出处 Apache 80 端口被占用无法重启解决办法 www.111cn.net 编辑:tiger 来源:转载使用WEB服务器的朋友都知道80端口是一个用来对外让用户访问的一个端口了,像apach ...
 - Sql面试常考题(持续添加)
			
最近萌生换工作的念头,于是上网下载了一些公司的面试题,重新看了面试题中的Sql部分,这些查询题有时候只是兜一个弯角来考,对于给EF惯坏的孩子来说还是有点难度的(给面试官鄙视了几下的结果),所以列出最近 ...