浅谈JavaScript中的Function引用类型
引言
在JavaScript中最有意思的就是函数了,这一切的根源在于函数实际上是一个对象。每一个函数都是Function类型的实例,而且都和其他引用类型的实例一样具有属性和方法。函数作为一个对象,因此函数名称实际是一个指向函数对象的指针,不会与某一个函数进行绑定。
函数没有重载
前面部分介绍过,函数名称实际是指向函数对象的一个指针,这样就不难理解Javacript中的函数不存在函数重载了。请看下面的例子
- function addNumber(number) {
- return 100 + number;
- }
- function addNumber(number) {
- return 200 + number;
- }
- var result = addNumber(300);
- alert(result);//输出500
在这个例子里面我们定义了两个同名函数,第一个函数我们在定义的时候会在全局作用域的变量对象中引用第一个函数对象,addNumber指向第一个函数对象。当运行到第二个函数的时候addNumber又指向第二个函数对象。所以addNumber现在指向第二个函数对象。所以下面在调用它引用的函数时,会执行第二个函数。
函数声明与函数表达式
函数通常是使用函数声明语法定义的,当然函数表达式的语法也是可以的,不过它们之间有一些很重要的差异。实际上,解析器在向执行环境中加载数据时,会率先读取函数表达式,使其在任何代码之前可用(可以访问)。对于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正解析执行。
作为值的函数
在JavaScript中函数名本身就是变量,因此函数也可以作为值来使用。函数不仅可用作为参数传递给另一个参数,也可以作为另一个函数的结果返回。我们来看下下面的例子
- //通过属性名称来对数组元素进行排序
- function createComparisonFunction(propertyName) {
- return function (obj1, obj2) {
- var val1 = obj1[propertyName];
- var val2 = obj2[propertyName];
- if (val1 < val2) {
- return -1;
- }
- else if (val1 > val2) {
- return 1;
- }
- else {
- return 0;
- }
- }
- }
- var data = [{ name: "gxw", age: 25 }, { name: "zmm", age: 20 }];
- //按照name属性来排序
- data.sort(createComparisonFunction("name"));
- alert(data[0].name);//输出gxw
- //按照age属性来排序
- data.sort(createComparisonFunction("age"));
- alert(data[0].name);//输出zmm
在这个例子中我们看到我们在createcomparisonFunction中返回了一个匿名函数,在匿名函数中我们根据属性来对对象数组进行排序。
函数内部属性
在函数内部有两个非常特殊的对象,它们分别是:arguments和this。arguments对象在前面可能也介绍了一点。它是一个类似于数组的对象,但是它不是数组。它包含着传入函数的所有的参数。arguments对象还有一个callee属性,这个属性是一个指针,指向拥有这个arguments对象的函数。一个非常经典的例子是阶乘,请看例子:
- function factorial(number) {
- if (number <= 1) {
- return 1;
- }
- else {
- //return number * factorial(number - 1);
- return number * arguments.callee(number - 1);
- }
- }
- alert(factorial(5));
在这个例子中我们看到我们定义了一个名叫factorial的阶乘函数。在第6行一般是我们常规的做法。但是这样有一个问题,阶乘函数和它的名称紧紧的耦合在一起,一旦以后改变了阶乘函数的名称,就需要在函数内部也修改名称。这时候通过使用arguments.callee可以代替第一种做法。消除了函数与名称紧紧耦合在一起的问题。
在函数内部this对象引用的是函数据以执行的环境对象(当在网页的全局作用域中调用函数时,this对象引用的就是window对象)。来看下面的例子:
- var color = "windows color";
- var obj = { color: "obj color" };
- function sayColor() {
- alert(this.color);
- }
- function sayColor2() {
- return function () {
- return this.color;
- }
- }
- sayColor(); //windows color
- obj.sayColor = sayColor;
- obj.sayColor();//obj color
- alert(sayColor2()()); //windows color
- obj.sayColor2 = sayColor2;
- alert(obj.sayColor2()());//windows color
在这个例子中,我们看到在全局作用域下调用函数this指向的是window对象。看代码的第17行。如果在对象上调用函数的时候,我们看到this指向的当前调用函数的对象。重点是如果在函数内部还有一个函数的时候this指向的是什么呢?通过sayColor2函数我们知道在匿名函数中this指向的是window对象。那为什么不是包含作用域(或者外部作用域)的this对象呢?在前面我们介绍过,在函数被调用时,其活动对象都会自动获得2个特殊对象,this和arguments对象。内部函数在这两个变量时,只会搜索到活动对象为止,所以用于不可能获取到外部作用域中的这2个对象。
函数属性和方法
JavaScript中函数时对象,因此函数也有属性和方法。每一个函数都包含两个属性,它们分别是:length和prototype属性。length属性表示函数希望接受到的参数的个数。例如一个函数在定义的时候参数列表定义了3个参数,那么length就是3。记住:定义的参数个数可以通过函数名.length来获取。实际传递的参数个数和参数值可以到arguments中获取。
在JavaScript定义的全部属性中,最神秘的就是prototype属性了。对于JavaScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。在JavaScript中prototype是不可枚举的,所以for-in循环是找不到prototype属性的。
每个函数都包含两个非继承而来的方法,call和apply方法。这两个函数的用途都是在特定的作用域中调用函数,实际上是等于设置函数体内this对象的值。事实上,传递参数并非是call和apply真正 用武之地,它们强大的地方在于可以扩充函数赖以生存的作用域。来看下面的例子:
- var color = "windows color";
- var obj = { color: "obj color" };
- function sayColor() {
- alert(this.color);
- }
- sayColor(); //windows color
- sayColor.call(this); //windows color
- sayColor.call(window); //windows color
- sayColor.call(obj); //obj color
注意看代码的第12行,通过call函数指定函数体内this的值是obj对象。这样输出的就是obj color了。使用call或者apply来扩充作用域就是对象不需要与函数有任何耦合关系。在前面的例子中,我们先将sayColor绑定到obj对象上,然后使用obj.sayColor()来调用函数。通过call或者apply我们可以省去那些不必要的步骤。
浅谈JavaScript中的Function引用类型的更多相关文章
- 浅谈JavaScript中的闭包
浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...
- 浅谈JavaScript中的null和undefined
浅谈JavaScript中的null和undefined null null是JavaScript中的关键字,表示一个特殊值,常用来描述"空值". 对null进行typeof类型运 ...
- 浅谈JavaScript中的正则表达式(适用初学者观看)
浅谈JavaScript中的正则表达式 1.什么是正则表达式(RegExp)? 官方定义: 正则表达式是一种特殊的字符串模式,用于匹配一组字符串,就好比用模具做产品,而正则就是这个模具,定义一种规则去 ...
- 浅谈JavaScript中的继承
引言 在JavaScript中,实现继承的主要方式是通过原型链技术.这一篇文章我们就通过介绍JavaScript中实现继承的几种方式来慢慢领会JavaScript中继承实现的点点滴滴. 原型链介绍 原 ...
- 浅谈JavaScript中的内存管理
一门语言的内存存储方式是我们学习他必须要了解的,接下来让我浅谈一下自己对他的认识. 首先说,JavaScript中的变量包含两种两种类型: 1)值类型或基本类型:undefined.null.numb ...
- 浅谈JavaScript中闭包
引言 闭包可以说是JavaScript中最有特色的一个地方,很好的理解闭包是更深层次的学习JavaScript的基础.这篇文章我们就来简单的谈下JavaScript下的闭包. 闭包是什么? 闭包是什么 ...
- 浅谈JavaScript中的变量、参数、作用域和作用域链
基本类型和引用类型 在JavaScript中有两种数据类型值.基本类型值和引用类型值.基本类型值指的是简单的数据段,而引用类型值指的是可能由多个值构成的对象.在JavaScript中有5种基本数据类型 ...
- 浅谈JavaScript中的string拥有方法的原因
我们都知道,JavaScript数据类型分两大类,基本类型(或者称原始类型)和引用类型. 基本类型的值是保存在栈内存中的简单数据段,它们是按值访问的.JS中有五种基本类型:Undefined.Null ...
- 浅谈JavaScript中继承的实现
谈到js中的面向对象编程,都有一个共同点,选择原型属性还是构造函数,两者各有利弊,而就片面的从js的对象创建以及继承的实现两个方面来说,官方所推荐的是两个相结合,各尽其责,各取其长,在前面的例子中,我 ...
随机推荐
- js json 对象相互转换
字符串转对象(strJSON代表json字符串) var obj = eval(strJSON); var obj = strJSON.parseJSON(); var obj = JSO ...
- 中间值为什么为l+(r-l)/2,而不是(l+r)/2
二分法的算法中,我们看到一些代码里取中间值: MID=l+(r-l)/2; 为什么是这个呢?不就是(l+r)/2吗?为什么要多此一举呢? 其实还是有不一样的,看看他们的区别吧: l,r是指针的时候只能 ...
- js-JavaScript高级程序设计学习笔记9
依然第十三章 事件 1.页面上的所有元素都支持鼠标事件,除了mouseenter和mouseleave,所有鼠标事件都会冒泡. 2.修改键:shift.ctrl.alt.meta.四个属性表示修改键的 ...
- POJ2010 Moo University - Financial Aid(二分法)
题目地址 分析:如果用二分法,关键是score和aid分开排序,score排序是为了充分利用中位数的性质,这样就可以确定m左右必须各选N/2个,到这之后有人是用dp求最优解,可以再次按照aid排序一次 ...
- iOS “智慧气象”APP中用到的第三方框架汇总
“智慧气象”是我最近在公司接手的项目,已经完成最新版本的更新并上架,在此分享下其中用到的第三方框架的使用. 应用地址:APP商店搜索“智慧气象” MJRefresh(下拉刷新)业界知名下拉刷新框架就不 ...
- Spring BeanUtils的用法
package test; import java.util.Date; import org.springframework.beans.BeanUtils; import test.basic.B ...
- PHPstorm激活
最近想学习一下PHP 于是下载了很不错的phpstorm 但这老外的工具是要购买正版的 所以就搜了一下破解激活的教程 发现现在网上的在线破解在2016.2版本里面大多已被封杀 尝试了本地破解也发现大 ...
- Beta版本冲刺第四天 12.10
一.站立式会议照片: 二.项目燃尽图: Android端 后台 三.项目进展: 成 员 昨天完成任务 今天完成任务 明天要做任务 问题困难 心得体会 胡泽善 日期合理性的判断,一个是用户反馈的查看 管 ...
- NOIp 0916 爆零记
题目来自神犇chad 上次爆零是说着玩,这次真的爆零了QAQ 好吧貌似是TYVJ的模拟赛打多了..一直按照TYVJ的格式提交的压缩包.. 然后莫名其妙就AK了hhh 来的时候迟到了半小时,昨晚痛苦的补 ...
- SQL Server编程(01)流程控制
批处理 应用程序向SqlServer发送的一组命令,Sql Server会将其编译成一个可执行单元,称为执行计划,执行计划中的语句每次执行一条. 每个不同的批处理用GO命令分割.GO命令不是SQL语句 ...