函数是JavaScript世界里的第一公民,换句话来说,就是我们如果可以精通JavaScript函数的使用,那么对JavaScript的运用可以更游刃有余了。熟悉JavaScript的人应该都知道,同样的函数,以不同的方式调用的话,受影响最大的应该是  this 。下面我们来说说JavaScript函数的各种调用模式。

一、普通函数的调用模式

  所谓普通函数的调用模式,也是JavaScript函数的最简单的一种调用模式,直接就是函数名后接一个  ()  实现调用,看下面代码:

function func(){
console.log(this === window); //true
}
func();

  上面代码,我们用function关键字声明了一个 func 函数,并且在函数体内打印 this===window,然后我们直接调用函数func,我们可以看到控制台是直接打印出 true ,也就是说,函数的这种普通调用模式,函数体内的  this  是指向全局环境 window 的。不清楚这点的同学,可以能会遇到这样的一个bug:

var color = 'gg';
var obj = {
color : 'red',
show : function(){
function func1(){
console.log(this.color); //gg
}
func1();
}
}
obj.show();

  我们在全局环境下声明了一个变量 color 和一个对象 obj ,在对象 obj 里面我们还声明了一个 color 属性 为 'red',一个 show 方法。而且在 show 方法里面呢,我们还声明了一个函数 func1 并且调用了 func1,func1 的作用是打印 this.color。最后我们运行代码  obj.show();   调用obj里面的show方法。不清楚函数的普通调用模式的特点的同学可能会认为此时在控制台答应出来的会是  'red' 。实际上此时在控制台答应出来的应该是  gg  。因为函数  func1  的调用模式是 普通函数调用模式(即使它是在  obj  的  show  方法里面调用的),所以此时函数体内的  this  是指向 全局环境window 的,所以就打印了全局环境下的变量  color 。

  可能有些同学会问:如果我们希望  func1  函数打印出来的是 'red' 呢,应该怎么改?其实很简单,因为  obj.color  才是 'red' ,所以我们只需要把  指向  obj  的  this 引入到函数 func1  里面就行了:

var color = 'gg';
var obj = {
color : 'red',
show : function(){
var that = this;
function func1(){
console.log(that.color); //red
}
func1();
}
}
obj.show();

  在上面的代码中,因为  show  里面的   this  指向   obj   的,所以我们在  show  里面声明一个变量  that = this;用来把指向  obj  的  this  引入到  func1 中,然后再把 func1 函数体内的  this.color  改为  that.color ,此时在控制台打印出来的就是我们想要的 'red' 了。

  可能现在又有同学会问:为什么   show   里面的  this  是指向  obj 的呢?这就是我们要说的JavaScript函数的第二种调用模式:方法调用模式

二、方法调用模式

  方法调用模式,简单来说就是把一个 JavaScript函数作为一个对象的方法来调用,当一个函数被保存为一个对象的属性是,我们就把它称为方法,例如上文的  obj  对象里的  show  ,当一个方法被调用时,函数体里面的   this  就会绑定到这个对象,例如上文的 show 里面的  this  。方法调用模式也很容易辨别:obj.show(),对象名 . 属性名 () ;代码的话可以参考上文的  obj  代码 ,博主就不多写了。记住:方法的调用是可以在函数体内通过  this  访问自己所属的那个对象的。

三、构造器调用模式

  博主认为构造器调用模式是相对于其他模式来说较为复杂点的调用模式了。通过关键字  new  可以把一个函数作为构造器来调用。关键字  new  可以改变函数的返回值:

function func2(name){
this.name = name;
} name; //undefined //普通函数调用模式
var foo = func2('afei');
foo; //undefined
name; //afei //构造器调用模式
var bar = new func2('lizefei');
bar.__proto__ === func2.prototype; //true
bar; //{name:'lizefei'}
bar.name; //'lizefei'

  在上示代码中我们声明了一个函数 func2 ,分别用两种不同的调用模式去调用它。因为函数  func2  并没有显式返回值,所以作为普通函数去调用时,它什么也没有返回,所以  foo  的值是  undefined  。因为普通调用模式的   this   是指向 全局环境   window  的,所以  func2('afei');  后,全局环境下就多了一个  name 变量且等于 'afei'。

  func2  作为构造器调用时,我们可以看到,它返回的是一个对象,因为关键字  new  使得函数在调用是发生了如下的特殊变化:

  1.   创建了一个新对象,而且这个新对象是链接到 func2  的  prototype  属性的
  2.   把函数里的  this  指向了这个新对象
  3.   如果没有显式的返回值,新对象作为构造器func2的返回值进行返回(所以bar 是 {name:'lizefei'})

  这样子我们就可以看出构造器的作用:通过函数的调用来初始化新创建出来的对象。在JavaScript的面向对象编程里面,这个可是相当重要的。

  因为在函数的声明上,在未来作为构造器调用的函数和普通函数的声明没什么区别,所以导致后来的开发者很容易因为调用模式的错误导致程序出问题。所以开发者们都默契地约定,用来做构造器调用的函数的函数名的第一个字符应该大写,例如:Person,People。这样子后来的开发者一看到函数名就知道要用构造器调用模式调用此函数了。

四、使用apply()和call()方法调用

  这种调用的模式是为了更灵活控制函数运行的上下文环境而诞生的。简单的说就是为了灵活控制函数体内  this  的值。

  apply 和 call这两个方法的第一个参数都是要传递被函数上下文的对象(简单点说就是要绑定给函数  this  的对象)。其他参数就有所不同了:

  apply方法的第二个参数是一个数组,数组里面的值将作为函数调用的参数;

  call方法,从第二个参数起(包括第二个参数),剩下的参数都是作为函数调用的参数;

  让我们看看栗子:

var obj = {
name :'afei'
}
function say(ag1,ag2){
console.log(ag1+':'+ag2+" "+ this.name);
}
say.apply(obj,['apply方法','hello']); //apply方法:hello afei
say.call(obj,'call方法','hi'); //call方法:hi afei

  正如栗子所示,我们把对象 obj  作为函数  say  的上下文来调用函数  say  ,所以函数里的  this  是指向 对象  obj  的。在apply方法里,我们通过数组  ['apply方法','hello']  给  say  方法传递了两个参数('apply方法' 和 'hello'),所以打印出来是:  apply方法:hello afei。

  同理  call 也是一样,而且函数传递的方式通过上面的代码也一目了然我,博主就不多做解释了。

  另外,博主还听说apply和call这两个方法除了传递参数的方式不一样,执行的速度还是apply 比 call 要快呢。不过博主就没有实验过。

五、总结

  在JavaScript里面,函数只要的调用模式就是这几种了(在ES6里面还有一种很奇怪很特殊的函数调用模式,叫做’标签模板‘,在这里博主也不多说了,有空另更),只要掌握了这几种主要的调用模式,那么日后再也不用担心  this 的值变来变去了。

  上文如果有漏的、有错误的地方,望各位小伙伴指出,小弟虚心向学。

ps:转载请标明出处  http://www.cnblogs.com/afeihome/

JavaScript函数的各种调用模式的更多相关文章

  1. ASP.net关于C#代码与javaScript函数的相互调用

    C#代码与javaScript函数的相互调用 问:1.如何在JavaScript访问C#函数?2.如何在JavaScript访问C#变量?3.如何在C#中访问JavaScript的已有变量?4.如何在 ...

  2. Javascript 函数声明、调用、闭包

    1 # Javascript 函数声明.调用.闭包 2 # 一.函数声明 3 # 1.直接声明.浏览器在执行前,会先将变量和函数声明进行提升. 4 fn(); 5 function fn () { 6 ...

  3. JavaScript 函数——语法,调用,返回值,局部变量,全局变量,未声明变量

    JavaScript 函数是被设计为执行特定任务的代码块. JavaScript 函数会在某代码调用它时被执行. ㈠函数 ⑴什么是函数 函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块. ⑵ ...

  4. 函数四种调用模式以及其中的this指向

    第一种:函数直接执行模式 function add(a,b){ console.log(this); return a+b; } add(10,20)//this===window 第二种:对象方法的 ...

  5. js函数之四大调用模式

    一.方法调用模式 当一个函数调用保存为一个对象的属性时我们称之为方法调用. var myObject = { value:0, increment:function(inc){ this.value ...

  6. JavaScript 函数定义和调用

    普通的函数定义方法: function abs(x):{ if (x >= 0){ return x; }else { return -x ; } } 两种方法是等价的 var abs = fu ...

  7. JavaScript函数定义和调用 变量作用域

     本文是笔者在看廖雪峰老师JavaScript教程时的个人总结   JavaScript中函数定义可以是这样的格式 function 函数名(参数) {     函数体 } 也可以是这样的格式     ...

  8. JavaScript 函数的定义-调用、注意事项

    函数定义 函数语句定义 function(a,b){ return a+b; } 表达式定义 var add = function(a,b){return a+b}; //函数表达式可以包含名称,这在 ...

  9. JavaScript函数参数与调用

    函数调用: /* 1. 函数调用 */ ,,,); /* 2. 方法调用 */ this.CName = "全局"; var o = { CName:"o类", ...

随机推荐

  1. C/s从文件(TXT)中读取数据插入数据库

    流程: 1.当按钮单击时,弹出OpenFileDialog 2.判断后缀名是否合法 3.导入数据库 按钮事件中的代码: 1.判断用户是否选中文件. 2.判断用户选择的文件是否为txt //第一步,当按 ...

  2. C#中string,char[],byte[]互相转换

    string 转换成 Char[] string ss = "我爱你,中国"; char[] cc = ss.ToCharArray(); Char[] 转换成string str ...

  3. 致DBA:为什么你经常犯错,是因为你做的功课不够

    专职做DBA已经6年多的事件了,看同行.同事犯了太多的错误,自己也犯了非常多的错误.一路走来,感触非常深.然而绝大多数的错误其实都是很低级的错误.有的是因为不了解某个引擎的特性导致:有的是因为对线上环 ...

  4. idea 控制台输出 中文乱码 解决方法

    使用intellij idea 14.1时,console 会输出中文乱码.下面分两种情况解决这种问题:一种是maven构建项目.一种是tomcat(不以maven构建)构建项目. 1.tomcat输 ...

  5. pod install 出现 Unable to find a specification for `xxxxx` 解决方案

    pod repo update 更新一下repo,更新完成之后即可解决无法找到xxx第三方框架的问题了

  6. 如何在Elasticsearch中安装中文分词器(IK)和拼音分词器?

    声明:我使用的Elasticsearch的版本是5.4.0,安装分词器前请先安装maven 一:安装maven https://github.com/apache/maven 说明: 安装maven需 ...

  7. 移动端页面 iPhone + Safari 页面调试 之 正确查看网络请求的姿势

    如题 本文主要将 Safari + iPhone 前端开发调试  之 正确查看网络请求的 姿势 惯例 说下问题场景: 早知道safari(Mac) + iPhone 调试的方便 能解决很多日常调试问题 ...

  8. 根据源码用HttpServletRequest获取MultipartFile的问题

    问题 由于某些原因,现在需要这样的一个文件上传接口,这个接口type(String)是必传参数,photoFile(MultipartFile)是非必传参数,即一般情况下需要接受两个参数,分别为pho ...

  9. Unity遮挡透明渐变

    遮挡透明若没有渐变实现方法: 1.透明中物体存在list中 2.每过一段时间(可以每帧,但是流畅性会降低)摄像机发送一条射线向玩家,out hitInfo 3.list与hitInfo比对,将在lis ...

  10. 线程(java课堂笔记)

    1.两种方式的差异 2.线程的生命周期 3.线程控制(线程的方法) 4.线程同步 5.线程同步锁 一. 两种方式的差异 A extends Thread :简单 不能再继承其他类了(Java单继承)同 ...