【名师课堂】JavaScript高级之函数的四种调用形式

 蒋坤:(.Net主讲老师)
  个人简介:精通.Net技术,熟悉。Net Framework基础类库,对CLR运行原理深有研究,善于利用图形逻辑表述技术难点。曾经参与项目有:广东华兴OA系统、中小学跟踪测评与教学测评系统。

  学生评价:授课很有激情。


蒋坤老师

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

  了解函数的调用过程有助于深入学习与分析JavaScript代码。 本文是JavaScript高级这个系列中的第三篇文章,主要介绍JavaScript中函数的四种使用形式。
  在JavaScript中,函数是一等公民,函数在JavaScript中是一个数据类型,而非像C#或其他描述性语言那样仅仅作为一个模块来使用。函数有四种调用模式,分别是:函数调用形式、方法调用形式、构造器形式、以及apply形式。这里所有的调用模式中,最主要的区别在于关键字this的意义。下面分别介绍这个几种调用形式。

  一、函数调用形式

  函数调用形式是最常见的形式,也是最好理解的形式。 所谓函数形式就是一般声明函数后直接调用即是。 例如:
  1. <font face="微软雅黑">// 声明一个函数,并调用
  2. function func() {
  3.         alert("Hello World");
  4. }
  5. func();
  6.         或者// 使用函数的Lambda表达式定义函数,然后调用
  7. var func = function() {
  8.         alert("你好,传智播客");
  9. };
  10. func();</font>
复制代码
  这两段代码都会在浏览器中弹出一个对话框,显示字符串中的文字。 这个就是函数调用。可以发现函数调用很简单,就是平时学习的一样。 这里的关键是,在函数调用模式中,函数里的this关键字指全局对象,如果在浏览器中就是window对象。例如:
  1. <font face="微软雅黑">  var func = function() {
  2.         alert(this);
  3. };
  4. func();</font>
复制代码
  此时,会弹出对话框,打印出 [object Window]。

  二、方法调用模式

  函数调用模式很简单,是最基本的调用方式。 但是同样的是函数,将其赋值给一个对象的成员以后,就不一样了。将函数赋值给对象的成员后,那么这个就不在称为函数,而应该叫做方法。例如:
  1. <font face="微软雅黑">// 定义一个函数
  2. var func = function() {
  3.         alert("我是一个函数么?");
  4. };
  5. // 将其赋值给一个对象
  6. var o = {};
  7. o.fn = func; // 注意这里不要加圆括号
  8. // 调用
  9. o.fn();</font>
复制代码
  此时,o.fn则是方法,不是函数了。 实际上fn的方法体与func是一模一样的,但是这里有个微妙的不同。看下面的代码:
  1. <font face="微软雅黑">// 接上面的代码
  2. alert(o.fn === func);
  3. 打印结果是true ,这个表明两个函数是一样的东西。 但是修改一下函数的代码:
  4. // 修改函数体
  5. var func = function() {
  6.         alert(this);
  7. };
  8. var o = {};
  9. o.fn = func;
  10. // 比较
  11. alert(o.fn === func);
  12. // 调用
  13. func();
  14. o.fn();</font>
复制代码
  这里的运行结果是,两个函数是相同的,因此打印结果是true。但是由于两个函数的调用是不一样的,func的调用,打印的是[object Window],而 o.fn的打印结果是[object Object]。
  这里便是函数调用与方法调用的区别。 函数调用中,this专指全局对象window,而
  在方法中this专指当前对象。 即o.fn 中的this指的就是对象o。

  三、构造器调用模式

  同样是函数,在单纯的函数模式下,this表示window;在对象方法模式下,this指的是当前对象。除了这两种情况,JavaScript中函数还可以是构造器。 将函数作为构造器来使用的语法就是在函数调用前面加上一个new关键字。 如代码:
  1. <font face="微软雅黑">// 定义一个构造函数
  2. var Person = function() {
  3.         this.name = "传智播客";
  4.         this.sayHello = function() {
  5.                 alert("你好,这里是" + this.name);
  6.         };
  7. };
  8. // 调用构造器,创建对象
  9. var p = new Person();
  10. // 使用对象
  11. p.sayHello();</font>
复制代码

  上面的案例首先创建一个构造函数Person,然后使用构造函数创建对p。这里使用new语法。然后在使用对象调用sayHello()方法。这个使用构造函数创建对象的案例比较简单。从案例可以看到,此时this指的是对象本身。

  除了上面简单的使用以外,函数作为构造器还有几个变化。分别为:
  1、 所有需要由对象使用的属性,必须使用this引导;
  2、 函数的return语句意义被改写,如果返回非对象,就返回this;

  3.1 构造器中的 this

  我们需要分析创建对象的过程,方能知道this的意义。如下面代码:
  1. <font face="微软雅黑">var Person = function() {
  2.         this.name = "传智播客";
  3. };
  4. var p = new Person();</font>
复制代码
  这里首先定义了函数Person,下面分析一下整个执行:
  1、 程序在执行到这一句的时候,不会执行函数体,因此JavaScript 的解释器并不知道这个函数的内容。
  2、 接下来执行new关键字,创建对象,解释器开辟内存,得到对象的引用,将新对象的引用交给函数。
  3、紧接着执行函数,将传过来的对象引用交给this. 也就是说,在构造方法中,this就是刚刚被new创建出来的对象。
  4、 然后为this添加成员,也就是为对象添加成员。
  5、 最后函数结束,返回 this,将 this 交给左边的变量。
  分析过构造函数的执行以后,可以得到,构造函数中的this就是当前对象。

  3.2 构造器中的 return

  在构造函数中return的意义发生了变化,首先如果在构造函数中,如果返回的是一个对象,那么就保留原意。 如果返回的是非对象,比如数字、布尔和字符串,那么就返回this,如果没有return语句,那么也返回this。 看下面代码:
  1. <font face="微软雅黑">// 返回一个对象的 return
  2. var ctr = function() {
  3.         this.name = "赵晓虎";
  4.         return {
  5.                 name:"牛亮亮"
  6.         };
  7. };
  8. // 创建对象
  9. var p = new ctr();
  10. // 访问name属性
  11. alert(p.name);</font>
复制代码
  执行代码,这里打印的结果是“牛亮亮”。因为构造方法中返回的是一个对象,那么保留return的意义,返回内容为return后面的对象。再看下面代码:
  1. <font face="微软雅黑">// 定义返回非对象数据的构造器
  2. var ctr = function() {
  3.         this.name = "赵晓虎";
  4.         return "牛亮亮";
  5. };
  6. // 创建对象
  7. var p = new ctr();
  8. // 使用
  9. alert(p);
  10. alert(p.name);</font>
复制代码
  代码运行结果是,先弹窗打印[object Object],然后打印“赵晓虎”。 因为这里return的是一个字符串,属于基本类型,那么这里的return语句无效,返回的是this对象。因此第一个打印的是[object Object],而第二个不会打印undefined。

  四、apply调用模式

  除了上述三种调用模式以外,函数作为对象还有apply方法与call方法可以使用,这便是第四种调用模式,我称其为apply模式。
  首先介绍apply模式,首先这里apply 模式既可以像函数一样使用,也可以像方法一样使用可以说是一个灵活的使用方法。 首先看看语法:
  函数名。apply(对象, 参数数组);
  这里看语法比较晦涩,还是使用案例来说明:
  1、 新建两个 js 文件,分别为"js1.js"与"js2.js";
  2、 添加代码
  1. <font face="微软雅黑">// js1.js 文件中
  2. var func1 = function() {
  3.         this.name = "传智播客";
  4. };
  5. func1.apply(null);
  6. alert(name);
  7. // js2.js 文件
  8. var func2 = function() {
  9.         this.name = "传值播客";
  10. };
  11. var o = {};
  12. func2.apply(o);
  13. alert(o.name);</font>
复制代码
  3、 分别运行着两段代码,可以发现第一个文件中的name属性已经加载到全局对象window中;
  而第二个文件中的name属性是在传入的对象o中。即第一个相当于函数调用,第二个相当于方法调用。
  这里的参数是方法本身所带的参数,但是需要用数组的形式存储在。 比如代码:
  1. <font face="微软雅黑">// 一个数组的例子
  2. var arr1 = [1,2,3,[4,5],[6,7,8]];
  3. // 将其展开
  4. var arr2 = arr1.conact.apply([], arr1);</font>
复制代码
  然后介绍一下call 模式。 call 模式与apply 模式最大的不同在于call 中的参数不用数组。看下面代码就清楚了:
  1. <font face="微软雅黑">// 定义方法
  2. var func = function(name, age, sex) {
  3.         this.name = name;
  4.         this.age = age;
  5.         this.sex = sex;
  6. };
  7. // 创建对象
  8. var o = {};
  9. // 给对象添加成员
  10. // apply 模式
  11. var p1 = func.apply(o, ["赵晓虎", 19, "男"]);
  12. // call 模式
  13. var p2 = func.call(o, "赵晓虎", 19, "男");</font>
复制代码
  上面的代码,apply 模式与call 模式的结果是一样的。
  实际上,使用apply 模式和call 模式,可以任意的操作控制this的意义,在函数js的设计模式中使用广泛。简单小结一下,js中的函数调用有四种模式,分别是:函数式、方法式、构造器式和apply 式。而这些模式中,this 的含义分别为:在函数中 this 是全局对象 window,在方法中 this指当前对象,在构造函数中this是被创建的对象,在apply模式中this可以随意的指定。在apply模式中如果使用null,就是函数模式,如果使用对象,就是方法模式。

  五、案例

  下面通过一个案例结束本篇吧。
  案例说明:有一个div,id为dv,鼠标移到上面去高度增大2倍,鼠标离开恢复。下面
  接上js代码:
  1. <font face="微软雅黑">var dv = document.getElementById("dv");
  2. var height = parseInt(dv.style.height || dv.offsetHeight);
  3. var intervalId;
  4. dv.onmouseover = function() {
  5.         // 停止已经在执行的动画
  6.         clearInterval(intervalId);
  7.         // 得到目标高度
  8.         var toHeight = height * 2;
  9.         // 获得当前对象
  10.         var that = this;
  11.         // 开器计时器,缓慢变化
  12.         intervalId = setInterval(function() {
  13.                 // 得到现在的高度
  14.                 var height = parseInt(dv.style.height || dv.offsetHeight);
  15.                 // 记录每次需要变化的步长
  16.                 var h = Math.ceil(Math.abs(height - toHeight) / 10);
  17.                 // 判断变化,如果步长为0就停止计时器
  18.                 if( h > 0 ) {
  19.                         // 这里为什么要用that呢?思考一下吧
  20.                         that.style.height = (height + h) + "px";
  21.                 } else {
  22.                         clearInterval(intervalId);
  23.                 }
  24.         }, 20);
  25. };
  26. dv.onmouseout = function() {
  27.         // 原理和之前一样
  28.         clearInterval(intervalId);
  29.         var toHeight = height;
  30.         var that = this;
  31.         intervalId = setInterval(function() {
  32.                 var height = parseInt(dv.style.height || dv.offsetHeight);
  33.                 var h = Math.ceil(Math.abs(height - toHeight) / 10);
  34.                 if( h > 0 ) {
  35.                         that.style.height = (height - h) + "px";
  36.                 } else {
  37.                         clearInterval(intervalId);
  38.                 }
  39.         }, 20);
  40. };</font>
复制代码


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值