主要内容数组
了解函数的调用过程有助于深刻学习与分析JavaScript代码. 本文是JavaScript高级这 个系列中的第三篇文章,主要介绍JavaScript中函数的四种使用形式.
在JavaScript中,函数是一等公民,函数在JavaScript中是一个数据类型,而非像C#或 其余描述性语言那样仅仅做为一个模块来使用. 函数有四种调用模式,分别是:函数调用形
式、方法调用形式、构造器形式、以及apply形式. 这里全部的调用模式中,最主要的区别在
于关键字 this 的意义. 下面分别介绍这个几种调用形式.浏览器
函数调用形式是最多见的形式,也是最好理解的形式. 所谓函数形式就是通常声明函数 后直接调用便是. 例如:app
1 // 声明一个函数,并调用 2 function func() { 3 alert("Hello World"); 4 } 5 func();
或者函数
1 // 使用函数的Lambda表达式定义函数,而后调用 2 var func = function() { 3 alert("你好,传智播客"); 4 }; 5 func();
这两段代码都会在浏览器中弹出一个对话框,显示字符串中的文字. 这个就是函数调用.学习
能够发现函数调用很简单,就是平时学习的同样. 这里的关键是,在函数调用模式中, 函数里的 this 关键字指全局对象,若是在浏览器中就是 window 对象. 例如:动画
1 var func = function() { 2 alert(this); 3 }; 4 func();
此时,会弹出对话框,打印出 [object Window]this
函数调用模式很简单,是最基本的调用方式. 可是一样的是函数,将其赋值给一个对 象的成员之后,就不同了. 将函数赋值给对象的成员后,那么这个就不在称为函数,而
应该叫作方法. 例如:spa
1 // 定义一个函数 2 var func = function() { 3 alert("我是一个函数么?"); 4 }; 5 // 将其赋值给一个对象 6 var o = {}; 7 o.fn = func; // 注意这里不要加圆括号 8 // 调用 9 o.fn();
此时,o.fn 则是方法,不是函数了. 实际上 fn 的方法体与 func 是如出一辙的,可是这
里有个微妙的不一样. 看下面的代码:code
1 // 接上面的代码 2 alert(o.fn === func);
打印结果是 true ,这个代表两个函数是同样的东西. 可是修改一下函数的代码:对象
1 // 修改函数体 2 var func = function() { 3 alert(this); 4 }; 5 var o = {}; 6 o.fn = func; 7 // 比较 8 alert(o.fn === func); 9 // 调用 10 func(); 11 o.fn();
这里的运行结果是,两个函数是相同的,所以打印结果是 true. 可是因为两个函数的调用
是不同的,func 的调用,打印的是 [object Window],而 o.fn 的打印结果是 [object Object].
这里即是函数调用与方法调用的区别. 函数调用中,this 专指全局对象 window,而 在方法中 this 专指当前对象. 即 o.fn 中的 this 指的就是对象 o.
一样是函数,在单纯的函数模式下,this 表示 window;在对象方法模式下,this 指 的是当前对象. 除了这两种状况,JavaScript 中函数还能够是构造器. 将函数做为构造器
来使用的语法就是在函数调用前面加上一个 new 关键字. 如代码:
1 // 定义一个构造函数 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();
上面的案例首先建立一个构造函数Person,而后使用构造函数建立对象p. 这里使用 new 语法. 而后在使用对象调用sayHello()方法. 这个使用构造函数建立对象的案例比较简单.
从案例能够看到,此时 this 指的是对象自己.
除了上面简单的使用之外,函数做为构造器还有几个变化. 分别为:
一、 全部须要由对象使用的属性,必须使用 this 引导;
二、 函数的 return 语句意义被改写,若是返回非对象,就返回this;
咱们须要分析建立对象的过程,方能知道 this 的意义. 以下面代码:
1 var Person = function() { 2 this.name = "传智播客"; 3 }; 4 var p = new Person();
这里首先定义了函数 Person,下面分析一下整个执行:
一、 程序在执行到这一句的时候,不会执行函数体,所以 JavaScript 的解释器并不知道这个
函数的内容.
二、 接下来执行 new 关键字,建立对象,解释器开辟内存,获得对象的引用,将新对象的引
用交给函数.
三、 紧接着执行函数,将传过来的对象引用交给 this. 也就是说,在构造方法中,this 就是
刚刚被 new 建立出来的对象.
四、 而后为 this 添加成员,也就是为对象添加成员.
五、 最后函数结束,返回 this,将 this 交给左边的变量.
分析过构造函数的执行之后,能够获得,构造函数中的 this 就是当前对象.
在构造函数中 return 的意义发生了变化,首先若是在构造函数中,若是返回的是一个对 象,那么就保留原意. 若是返回的是非对象,好比数字、布尔和字符串,那么就返回 this,如
果没有 return 语句,那么也返回 this. 看下面代码:
1 // 返回一个对象的 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);
执行代码,这里打印的结果是"牛亮亮". 由于构造方法中返回的是一个对象,那么保留 return
的意义,返回内容为 return 后面的对象. 再看下面代码:
1 // 定义返回非对象数据的构造器 2 var ctr = function() { 3 this.name = "赵晓虎"; 4 return "牛亮亮"; 5 }; 6 // 建立对象 7 var p = new ctr(); 8 // 使用 9 alert(p); 10 alert(p.name);
代码运行结果是,先弹窗打印[object Object],而后打印"赵晓虎". 由于这里 return 的是一个
字符串,属于基本类型,那么这里的 return 语句无效,返回的是 this 对象. 所以第一个打印的 是[object Object]而第二个不会打印 undefined.
除了上述三种调用模式之外,函数做为对象还有 apply 方法与 call 方法可使用,这即是 第四种调用模式,我称其为 apply 模式.
首先介绍 apply 模式,首先这里 apply 模式既能够像函数同样使用,也能够像方法同样使用 能够说是一个灵活的使用方法. 首先看看语法:
函数名.apply(对象, 参数数组);
这里看语法比较晦涩,仍是使用案例来讲明:
一、 新建两个 js 文件,分别为"js1.js"与"js2.js";
二、 添加代码
1 // js1.js 文件中 2 var func1 = function() { 3 this.name = "传智播客"; 4 }; 5 func1.apply(null); 6 alert(name); 7 8 // js2.js 文件 9 var func2 = function() { 10 this.name = "传值播客"; 11 }; 12 var o = {}; 13 func2.apply(o); 14 alert(o.name);
三、 分别运行着两段代码,能够发现第一个文件中的 name 属性已经加载到全局对象 window 中;
而第二个文件中的 name 属性是在传入的对象 o 中. 即第一个至关于函数调用,第二个至关
于方法调用.
这里的参数是方法自己所带的参数,可是须要用数组的形式存储在. 好比代码:
1 // 一个数组的例子 2 var arr1 = [1,2,3,[4,5],[6,7,8]]; 3 // 将其展开 4 var arr2 = arr1.conact.apply([], arr1);
而后介绍一下 call 模式. call 模式与 apply 模式最大的不一样在于 call 中的参数不用数组. 看下面代码就清楚了:
1 // 定义方法 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, "男");
上面的代码,apply 模式与 call 模式的结果是同样的.
实际上,使用 apply 模式和 call 模式,能够任意的操做控制 this 的意义,在函数 js 的设 计模式中使用普遍. 简单小结一下,js 中的函数调用有四种模式,分别是:函数式、方法式、构造
器式和 apply 式. 而这些模式中,this 的含义分别为:在函数中 this 是全局对象 window,在方
法中 this 指当前对象,在构造函数中 this 是被建立的对象,在 apply 模式中 this 能够随意的
指定. 在 apply 模式中若是使用 null,就是函数模式,若是使用对象,就是方法模式.
下面经过一个案例结束本篇吧. 案例说明:有一个div,id为dv,鼠标移到上面去高度增大2倍,鼠标离开恢复. 下面直接上js代码
1 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 };