JavaScript函数的4种调用方法详解


Posted in Javascript onApril 22, 2014

在JavaScript中,函数是一等公民,函数在JavaScript中是一个数据类型,而非像C#或其他描述性语言那样仅仅作为一个模块来使用。函数有四种调用模式,分别是:函数调用形式、方法调用形式、构造器形式、以及apply形式。这里所有的调用模式中,最主要的区别在于关键字 this 的意义,下面分别介绍这个几种调用形式。

本文主要内容:

1.分析函数的四种调用形式
2.弄清楚函数中this的意义
3.明确构造函对象的过程
4.学会使用上下文调用函数

一、函数调用形式

函数调用形式是最常见的形式,也是最好理解的形式。所谓函数形式就是一般声明函数后直接调用即是。例如:

// 声明一个函数,并调用
function func() {
    alert("Hello World");
}
func();

或者:
// 使用函数的Lambda表达式定义函数,然后调用
var func = function() {
    alert("你好,程序员");
};
func();

这两段代码都会在浏览器中弹出一个对话框,显示字符串中的文字,这个就是函数调用。

可以发现函数调用很简单,就是平时学习的一样,这里的关键是,在函数调用模式中,函数里的 this 关键字指全局对象,如果在浏览器中就是 window 对象。例如:

var func = function() {
    alert(this);
};
func();

此时,会弹出对话框,打印出 [object Window]。

二、方法调用模式

函数调用模式很简单,是最基本的调用方式。但是同样的是函数,将其赋值给一个对象的成员以后,就不一样了。将函数赋值给对象的成员后,那么这个就不在称为函数,而应该叫做方法。例如:

// 定义一个函数
var func = function() {
    alert("我是一个函数么?");
};
// 将其赋值给一个对象
var o = {};
o.fn = func; // 注意这里不要加圆括号
// 调用
o.fn();

此时,o.fn 则是方法,不是函数了。实际上 fn 的方法体与 func 是一模一样的,但是这里有个微妙的不同。看下面的代码:
// 接上面的代码
alert(o.fn === func);
打印结果是true,这个表明两个函数是一样的东西,但是修改一下函数的代码:
// 修改函数体
var func = function() {
    alert(this);
};
var o = {};
o.fn = func;
// 比较
alert(o.fn === func);
// 调用
func();
o.fn();

这里的运行结果是,两个函数是相同的,因此打印结果是 true。但是由于两个函数的调用是不一样的,func 的调用,打印的是 [object Window],而 o.fn 的打印结果是 [object Object]。

这里便是函数调用与方法调用的区别,函数调用中,this 专指全局对象 window,而在方法中 this 专指当前对象,即 o.fn 中的 this 指的就是对象o。

三、构造器调用模式

同样是函数,在单纯的函数模式下,this 表示 window;在对象方法模式下,this 指的是当前对象。除了这两种情况,JavaScript 中函数还可以是构造器。将函数作为构造器来使用的语法就是在函数调用前面加上一个 new 关键字。如代码:

// 定义一个构造函数
var Person = function() {
    this.name = "程序员";
    this.sayHello = function() {
        alert("你好,这里是" + this.name);
    };
};
// 调用构造器,创建对象
var p = new Person();
// 使用对象
p.sayHello();

上面的案例首先创建一个构造函数Person,然后使用构造函数创建对象p。这里使用 new 语法。然后在使用对象调用sayHello()方法,这个使用构造函数创建对象的案例比较简单。从案例可以看到,此时 this 指的是对象本身。除了上面简单的使用以外,函数作为构造器还有几个变化,分别为:

1、所有需要由对象使用的属性,必须使用 this 引导;

2、函数的 return 语句意义被改写,如果返回非对象,就返回this。

构造器中的 this

我们需要分析创建对象的过程,方能知道 this 的意义。如下面代码:

var Person = function() {
    this.name = "程序员";
};
var p = new Person();

这里首先定义了函数 Person,下面分析一下整个执行:

1、程序在执行到这一句的时候,不会执行函数体,因此 JavaScript 的解释器并不知道这个函数的内容。

2、接下来执行 new 关键字,创建对象,解释器开辟内存,得到对象的引用,将新对象的引用交给函数。

3、紧接着执行函数,将传过来的对象引用交给 this。也就是说,在构造方法中,this 就是刚刚被 new 创建出来的对象。

4、然后为 this 添加成员,也就是为对象添加成员。

5、最后函数结束,返回 this,将 this 交给左边的变量。

分析过构造函数的执行以后,可以得到,构造函数中的 this 就是当前对象。

构造器中的 return

在构造函数中 return 的意义发生了变化,首先如果在构造函数中,如果返回的是一个对象,那么就保留原意。如果返回的是非对象,比如数字、布尔和字符串,那么就返回 this,如果没有 return 语句,那么也返回 this,看下面代码:

// 返回一个对象的 return
var ctr = function() {
    this.name = "赵晓虎";
    return {
        name:"牛亮亮"
    };
};
// 创建对象
var p = new ctr();
// 访问name属性
alert(p.name);

执行代码,这里打印的结果是”牛亮亮”。因为构造方法中返回的是一个对象,那么保留 return 的意义,返回内容为 return 后面的对象,再看下面代码:
// 定义返回非对象数据的构造器
var ctr = function() {
    this.name = "赵晓虎";
    return "牛亮亮";
};
// 创建对象
var p = new ctr();
// 使用
alert(p);
alert(p.name);

代码运行结果是,先弹窗打印[object Object],然后打印”赵晓虎”,因为这里 return 的是一个字符串,属于基本类型,那么这里的 return 语句无效,返回的是 this 对象,因此第一个打印的是[object Object]而第二个不会打印 undefined。

四、apply调用模式

除了上述三种调用模式以外,函数作为对象还有 apply 方法与 call 方法可以使用,这便是第四种调用模式,我称其为 apply 模式。

首先介绍 apply 模式,首先这里 apply 模式既可以像函数一样使用,也可以像方法一样使用,可以说是一个灵活的使用方法。首先看看语法:函数名.apply(对象, 参数数组);

这里看语法比较晦涩,还是使用案例来说明:

1、新建两个 js 文件,分别为”js1.js”与”js2.js”;

2、添加代码

// js1.js 文件中
var func1 = function() {
    this.name = "程序员";
};
func1.apply(null);
alert(name);
// js2.js 文件
var func2 = function() {
    this.name = "程序员";
};
var o = {};
func2.apply(o);
alert(o.name);

3、分别运行着两段代码,可以发现第一个文件中的 name 属性已经加载到全局对象 window 中; 而第二个文件中的 name 属性是在传入的对象 o 中,即第一个相当于函数调用,第二个相当 于方法调用。

这里的参数是方法本身所带的参数,但是需要用数组的形式存储在,比如代码:

// 一个数组的例子
var arr1 = [1,2,3,[4,5],[6,7,8]];
// 将其展开
var arr2 = arr1.conact.apply([], arr1);
然后介绍一下 call 模式,call 模式与 apply 模式最大的不同在于 call 中的参数不用数组,看下面代码就清楚了:
// 定义方法
var func = function(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
};
// 创建对象
var o = {};
// 给对象添加成员
// apply 模式
var p1 = func.apply(o, ["赵晓虎", 19, "男"]);
// call 模式
var p2 = func.call(o, "赵晓虎", 19, "男");

上面的代码,apply 模式与 call 模式的结果是一样的。

实际上,使用 apply 模式和 call 模式,可以任意的操作控制 this 的意义,在函数 js 的设 计模式中使用广泛。简单小结一下,js 中的函数调用有四种模式,分别是:函数式、方法式、构造 器式和 apply 式. 而这些模式中,this 的含义分别为:在函数中 this 是全局对象 window,在方 法中 this 指当前对象,在构造函数中 this 是被创建的对象,在 apply 模式中 this 可以随意的指定.。在 apply 模式中如果使用 null,就是函数模式,如果使用对象,就是方法模式。

五、综合例子

下面通过一个案例结束本篇吧。案例说明:有一个div,id为dv,鼠标移到上面去高度增大2倍,鼠标离开恢复,下面直接上js代码:

var dv = document.getElementById("dv");
var height = parseInt(dv.style.height || dv.offsetHeight);
var intervalId;
dv.onmouseover = function() {
    // 停止已经在执行的动画
    clearInterval(intervalId);
    // 得到目标高度
    var toHeight = height * 2;
    // 获得当前对象
    var that = this;
    // 开器计时器,缓慢变化
    intervalId = setInterval(function() {
        // 得到现在的高度
        var height = parseInt(dv.style.height || dv.offsetHeight);
        // 记录每次需要变化的步长
        var h = Math.ceil(Math.abs(height - toHeight) / 10);
        // 判断变化,如果步长为0就停止计时器
        if( h > 0 ) {
            // 这里为什么要用that呢?思考一下吧
            that.style.height = (height + h) + "px";
        } else {
            clearInterval(intervalId);
        }
    }, 20);
};
dv.onmouseout = function() {
    // 原理和之前一样
    clearInterval(intervalId);
    var toHeight = height;
    var that = this;
    intervalId = setInterval(function() {
        var height = parseInt(dv.style.height || dv.offsetHeight);
        var h = Math.ceil(Math.abs(height - toHeight) / 10);
        if( h > 0 ) {
            that.style.height = (height - h) + "px";
        } else {
            clearInterval(intervalId);
        }
    }, 20);
};
Javascript 相关文章推荐
javascript下操作css的float属性的特殊写法
Aug 22 Javascript
2010年最佳jQuery插件整理
Dec 06 Javascript
jQuery 自定义函数写法分享
Mar 30 Javascript
jquery插件开发之实现google+圈子选择功能
Mar 10 Javascript
JavaScript如何实现跨域请求
Aug 05 Javascript
Javascript中判断一个值是否为undefined的方法详解
Sep 28 Javascript
前端编码规范(3)JavaScript 开发规范
Jan 21 Javascript
JavaWeb表单及时验证功能在输入后立即验证(含用户类型,性别,爱好...的验证)
Jun 09 Javascript
对angularjs框架下controller间的传值方法详解
Oct 08 Javascript
详解vue服务端渲染浏览器端缓存(keep-alive)
Oct 12 Javascript
js实现旋转木马轮播图效果
Jan 10 Javascript
JavaScript实现猜数字游戏
May 20 Javascript
JavaScript实现存储HTML字符串示例
Apr 21 #Javascript
JavaScript获取flash对象与网上的有所不同
Apr 21 #Javascript
js unicode 编码解析关于数据转换为中文的两种方法
Apr 21 #Javascript
jquery form 加载数据示例
Apr 21 #Javascript
三种方式获取XMLHttpRequest对象
Apr 21 #Javascript
JavaScript中的Math 使用介绍
Apr 21 #Javascript
jQuery过滤选择器:not()方法使用介绍
Apr 20 #Javascript
You might like
PHP实现分页的一个示例
2006/10/09 PHP
PHP 获取MySQL数据库里所有表的实现代码
2011/07/13 PHP
基于GD2图形库的PHP生成图片缩略图类代码分享
2015/02/08 PHP
PHP保存带BOM文件的方法
2015/02/12 PHP
php实现异步数据调用的方法
2015/12/24 PHP
javascript 构建一个xmlhttp对象池合理创建和使用xmlhttp对象
2010/01/15 Javascript
javascript-表格排序(降序/反序)实现介绍(附图)
2013/05/30 Javascript
javascript学习笔记(三)BOM和DOM详解
2014/09/30 Javascript
JS简单计算器实例
2015/01/20 Javascript
Javascript动画的实现原理浅析
2015/03/02 Javascript
深入探讨前端框架react
2015/12/09 Javascript
JS三级可折叠菜单实现方法
2016/02/29 Javascript
倾力总结40条常见的移动端Web页面问题解决方案
2016/05/24 Javascript
Bootstrap3.0学习教程之JS折叠插件
2016/05/27 Javascript
文件上传,iframe跨域数据提交的实现
2016/11/18 Javascript
微信小程序 用户数据解密详细介绍
2017/01/09 Javascript
angularJs使用$watch和$filter过滤器制作搜索筛选实例
2017/06/01 Javascript
Vue.2.0.5实现Class 与 Style 绑定的实例
2017/06/20 Javascript
vue自定义移动端touch事件之点击、滑动、长按事件
2018/07/10 Javascript
angularJs中json数据转换与本地存储的实例
2018/10/08 Javascript
Vue 组件注册实例详解
2019/02/23 Javascript
js实现图片粘贴到网页
2019/12/06 Javascript
[40:19]2018完美盛典CS.GO表演赛
2018/12/17 DOTA
[01:08:43]DOTA2-DPC中国联赛定级赛 Phoenix vs DLG BO3第一场 1月9日
2021/03/11 DOTA
python中使用urllib2伪造HTTP报头的2个方法
2014/07/07 Python
使用grappelli为django admin后台添加模板
2014/11/18 Python
python计算牛顿迭代多项式实例分析
2015/05/07 Python
将Python的Django框架与认证系统整合的方法
2015/07/24 Python
pyqt5 实现多窗口跳转的方法
2019/06/19 Python
通过字符串导入 Python 模块的方法详解
2019/10/27 Python
Keras使用tensorboard显示训练过程的实例
2020/02/15 Python
实习生个人的自我评价
2013/12/08 职场文书
寒假思想汇报
2014/01/10 职场文书
爱护花草树木的标语
2014/06/11 职场文书
党的群众路线教育实践活动个人自我剖析材料
2014/10/07 职场文书
2015新学期校长寄语(3篇)
2015/03/25 职场文书