在JavaScript编程中,理解this、call和apply是道槛,如果能正确的理解它们的本质及其应用。那么在以后的JavaScript中会得心应手。

this

跟别的语言大相径庭的是,JavaScript的 this 总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。

this 的指向

具体到实际应用中,this的指向可以分为以下4中。

  • 作为对象的方法调用

当函数作为对象的方法被调用时,this指向该对象:

var obj={
    num:1,
    getNum:function(){
        alert( this === obj); //输出:true
        alert( this.a ); //输出:1
    }
};

obj.getNum();
  • 作为普通函数调用

当函数不作为对象的属性被调用时,也就是我们常说的普通函数调用方式,此时的this总是指向全局对象。在浏览器的JavaScript里,这个全局对象是window对象。

window.name = 'globalName';

var myObj={
    name:'khadron',
    getName:function(){
        return this.name;
    }
};

var getName = myObj.getName;

console.log( getName() ); //输出:globalName

在ECMAScript5的strict模式下,这种情况下的this已经被规定为不会指向全局对象,而是undefined:

function func(){
    'use strict'
    alert( this ); //输出:undefined
};

func();
  • 构造器调用

JavaScript中没有类,但是可以从构造器中创建对象,同时也提供了new运算符,使得构造器看起来更像是一个类。

出了宿主提供的一些内置函数,大部分JavaScript函数都可以当作构造器使用。构造器的外表跟普通函数一模一样,它们的区别在于被调用的方式。当用new运算符调用函数时,该函数总返回一个对象,通常情况下,构造器里的this就是指向返回的这个对象:

var MyClass=function(){
    this.name = 'khadron';
};

var obj = MyClass();
alert( obj.name ); //输出:khadron

但用new调用构造器=时,还需要注意有一个问题,如果构造函数显示地返回一个object类型的对象,那么此次运算结果最终会返回这个对象,而不是我们之前期待的this:

var MyClass = function(){
    this.name = 'khadron';
    return {    //显示地返回一个对象
        name: 'q'
    };
};

var obj=new MyClass();
alert( obj.name ); //输出: q

如果构造器不显示地返回任何数据,或者返回一个非对象类型的数据,就不会造成上述问题:

var MyClass = function(){
    this.name='khadron';
    return 'q'; //返回string类型
};

var obj = new MyClass();
alert( obj.name ); //输出:khadron
  • Function.prototype.call或Function.prototype.apply调用

跟普通函数调用相比,用Function.prototype.call或Function.prototype.apply可以动态地改变传入函数的this:

var obj1={
    name: 'khadron',
    getName:function(){
        return this.name;
    }
};

var obj2={
    name:'q'
};

console.log( obj1.getName() ); //输出:khadron
console.log( obje1.getName.call( obj2 )); //输出:q

丢失 this 问题

我们先看下面的代码:

var obj={
    name:'khadron',
    getName:function(){
        return this.name;
    }
}
console.log(obj.getName()); //输出:khadron

var getNameFunc=obj.getName();
console.log(getNameFunc()); //输出:undefined

当调用obj.getName时,getName方法是作为obj对象的属性被调用的,此时的this指向obj对象,所以obj.getName()输出’khadron’。

当另外一个变量getNameFunc来引用obj.getName,并且调用getNameFunc时,此时是普通函数调用方式,this指向是全局变量window,所以程序执行的结果是undefined。

call 和 apply

ECAMScript3给Function的原型定义了两个方法,他们是Function.prototype.call和Function。prototype.apply。在实际开发中,特别是在一些函数式风格的代码编写中,call 和 apply方法尤为有用。

call 和 apply 的区别

call 和 apply 的作用一模一样,区别仅在于传入参数形式的不同。

apply接受两个参数,第一个参数制定了函数体内this对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数:

var func = function(a,b,c){
    alert( [a,b,c] ); // 输出:[1,2,3]
};

func.apply( null,[]1,2,3 );

在上段代码中,参数1,2,3被放在数组中一起传入func函数,它们分别对相应func参数列表中的a、b、c。

call传入的参数数量不固定,跟apply相同的是,第一个参数也是代表函数体内的this指向,从第二个参数开始往后,每个参数被一次传入函数:

var func = function(a,b,c){
    alert( [a,b,c] ); //输出:[1,2,3]
};

func.call( null,1,2,3 );

当调用一个函数时,JavaScript的解释器并不会计较形参和实参的数量、类型以及顺序上的区别,JavaScript的参数在内部就是一个数组来表示的。

call是包装在apply上面的一课语法糖,如果我们明确地知道函数接受多少个参数,而且想一目了然地表达形参和实参的对应关系,那么也可以用call来传递参数。

当调用call或者apply的时候,如果我们传入的第一个参数为null,那么函数体内的this会指向默认的宿主对象,在浏览器中则是window:

var func = function(a,b,c){
    alert( this === window ); //输出true
};

func.apply( null,[1,2,3] );

但如果是在严格模式下,函数体内的this指向为null

var func = function(a,b,c){
    'use strict'
    alert( this === null ); //输出true
}

有时候我们使用call 或者 apply 的目的不在于指定this指向,而是另有用途,比如借用其他对象的方法,我们可以传入null来代替某个具体的对象:

Math.max.apply( null, [1,2,4,5,3]); //输出5

[JavaScript] this、call和apply详解的更多相关文章

  1. javascript中this,call,apply详解

    javascript是一门解释型的语言,与很多面向对象语言相比有着不同特性,所以不能用面向对象的原理来理解this这个关键字. 在JS中,要真正理解this倒底指向哪个对象,必须先了解JS的作用域和原 ...

  2. javascript中的this作用域详解

    javascript中的this作用域详解 Javascript中this的指向一直是困扰我很久的问题,在使用中出错的机率也非常大.在面向对象语言中,它代表了当前对象的一个引用,而在js中却经常让我觉 ...

  3. javascript中=、==、===区别详解

    javascript中=.==.===区别详解今天在项目开发过中发现在一个小问题.在判断n==""结果当n=0时 n==""结果也返回了true.虽然是个小问题 ...

  4. Javascript 调试利器 Firebug使用详解

    Javascript 调试利器 Firebug使用详解 有时候,为了更清楚方便的查看输出信息,我们可能需要将一些调试信息进行分组输出,那么可以使用console.group来对信息进行分组,在组信息输 ...

  5. JavaScript中return的用法详解

    JavaScript中return的用法详解 最近,跟身边学前端的朋友了解,有很多人对函数中的this的用法和指向问题比较模糊,这里写一篇博客跟大家一起探讨一下this的用法和指向性问题. 1定义 t ...

  6. javascript 中合并排序算法 详解

    javascript 中合并排序算法 详解 我会通过程序的执行过程来给大家合并排序是如何排序的...  合并排序代码如下: <script type="text/javascript& ...

  7. JavaScript中this的用法详解

    JavaScript中this的用法详解 最近,跟身边学前端的朋友了解,有很多人对函数中的this的用法和指向问题比较模糊,这里写一篇博客跟大家一起探讨一下this的用法和指向性问题. 1定义 thi ...

  8. 高性能JavaScript模板引擎实现原理详解

    这篇文章主要介绍了JavaScript模板引擎实现原理详解,本文着重讲解artTemplate模板的实现原理,它采用预编译方式让性能有了质的飞跃,是其它知名模板引擎的25.32 倍,需要的朋友可以参考 ...

  9. JavaScript对象的property属性详解

    JavaScript对象的property属性详解:https://www.jb51.net/article/48594.htm JS原型与原型链终极详解_proto_.prototype及const ...

  10. javascript常用经典算法实例详解

    javascript常用经典算法实例详解 这篇文章主要介绍了javascript常用算法,结合实例形式较为详细的分析总结了JavaScript中常见的各种排序算法以及堆.栈.链表等数据结构的相关实现与 ...

随机推荐

  1. html的小例子

    常用的前端实例: 1略 2.在网页商城中的图片当我们把鼠标放上去之后,图片会显示一个有颜色的外边框,图片某一部分的字体的颜色并发生改变 鼠标放上去之前 鼠标放上去之后: 实现的代码: <!DOC ...

  2. iOS 之UIBezierPath

    代码地址如下:http://www.demodashi.com/demo/11602.html 在之前的文章中,由于用到过UIBezierPath这个类,所以这里就对这个类进行简单的记录一下,方便自己 ...

  3. HDU 4287 Intelligent IME(map运用)

    转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4287 Intellig ...

  4. freemarker相关

    大部分引入:http://www.blogjava.net/alinglau36/archive/2011/02/23/344970.html Freemarker操作字符串 1.substring( ...

  5. lua类库 middleclass学习笔记

    middleclass使在lua中面象对象变的简单 抄了一遍他的示例代码运行着试了试,基本懂了 local class = require 'middleclass' --类的继承 Person = ...

  6. UDP与TCP报文格式,字段意义

    UDP报文 1.UDP有两个字段:数据字段和首部字段. 首部字段 首部字段很简单,只有8个字节,由4个字段组成,每个字段的长度都是两个字节.   1)源端口:源端口号.在需要对方回信时选用.不需要时可 ...

  7. 开源管理系统OSSIM设置 语言为中文简体

    最近研究OSSIM系统,OSSIM的安装是做好的ISO,操作系统选择的是CentOS 64Bit系统.我使用的OSSIM 4.11 的ISO安装,虽然系统说明支持中文,实际上,只是台湾的繁体中文而以. ...

  8. SVN Client API的.net 接口 SharpSvn介紹 Checkout操作实例

    Subversion是一個文件版本管理工具, 廣泛的被大家採用來作為源代碼版本管理. 已有的工具不管是其自帶的命令行工具還是Windows UI的tortoiseSVN等還是很方便實用的, 但是如果想 ...

  9. MySQL(8)--Cluster 7.4 rpm centos7

    还没来得及写.我就知道一个月完毕不了,暂时添加的工作总是拖后腿. .. MySQL集群 docker下安装MySQL Cluster报错了.是我image的包少了perl.老实装吧.image又小不了 ...

  10. java序列化,二进制和数据流

    类的序列化需要支持Serializable接口,如果类支持序列化,可以通过ObjectOutPutStream和ObjectInputStream将对象和Stream之间进行方便的转换. 首先来 看一 ...