js中call与apply的用法小结


Posted in Javascript onDecember 28, 2013

前天去面试,有个gg问了一些js知识,其中有一道call与apply用法的题目,尽管在365天前用过call方法,但当时还是没能答上来,今天深入总结一下

call和apply,它们的作用都是将函数绑定到另外一个对象上去运行

两者的格式和参数定义:

call( thisArg [,arg1,arg2,… ] );       // 参数列表,arg1,arg2,...
apply(thisArg [,argArray] );                 // 参数数组,argArray

上面两个函数内部的this指针,都会被赋值为thisArg,这可实现将函数作为另外一个对象的方法运行的目的

一、call 的简单用法

首先,我们先看个简单的例子(call):

<!doctype html>
<html>
 <head>
  <title> call-apply </title>
 </head>
 <body>
  <input type="text" id="idTxt" value="input text">
  <script type="text/javascript">
   var value = "global var";
   function mFunc()
   {
    this.value = "member var";
   }
   function gFunc()
   {
    alert(this.value);
   }  
   window.gFunc();         // show gFunc, global var
   gFunc.call(window);        // show gFunc, global var
   gFunc.call(new mFunc());      // show mFunc, member var
   gFunc.call(document.getElementById('idTxt')); // show element, input text
  </script>
  <script language="javascript">
   var func = new function()
   {
    this.a = "func";
   }
   var func2 = function(x)
   {
    var a = "func2";
    alert(this.a);    
    alert(x);
   }
   func2.call(func, "func2");      // show func and func2
  </script>
 </body>
</html>

然后,运行结果如下:

global var
global var
member var
input text
func
func2

测试环境:Google Chrome 10.0.648.45

最后,分析结果

1、全局对象window调用函数gFunc,this指向window对象,因此this.value为global var

2、函数gFunc调用call方法,this默认指向第一个参数window对象,因此this.value也为global var

3、函数gFunc调用call方法,this默认指向第一个参数new mFunc(),即mFunc的对象,因此this.value为mFunc的成员变量member var

4、函数gFunc调用call方法,this默认指向第一个参数input text控件,即id=‘idTxt'的控件,因此this.value为input控件的value值input text

5、函数func2调用call方法,this默认指向第一个参数func函数对象,因此this.value为this.a,即func

6、函数func2调用call方法,第二个参数属于函数对象func2的参数,因此alert(x)为第二个参数func2


二、call 继承用法与改进

js使用call模拟继承

测试代码:

<!doctype html>
<html>
 <head>
  <title> call - apply for inherit </title>
 </head>
 <body>
  <script type="text/javascript">
   function baseA()  // base Class A
   {
    this.member = "baseA member";
    this.showSelfA = function()
    {
     window.alert(this.member);
    }
   }
   function baseB()  // base Class B
   {
    this.member = "baseB member";
    this.showSelfB = function()
    {
     window.alert(this.member);
    }
   }
   function extendAB()  // Inherit Class from A and B
   {
    baseA.call(this); // call for A
    baseB.call(this); // call for B
   }
   window.onload = function()
   {
    var extend = new extendAB(); 
    extend.showSelfA();  // show A
    extend.showSelfB();  // show B
   }
  </script>
 </body>
</html>
运行结果如下:

baseB member
baseB member

测试环境:Google Chrome 10.0.648.45

结果分析:

预期的结果,应该是输出 baseA member 和 baseB member,但实际输出却是 baseB member 和 baseB member

(已在IE9、8、6,Maxthon、Chrome、FF、Opera、Safari、360等浏览器测试过,结果都是后者:baseB member)

至此,机器是不会错的,这就需要我们深入分析

我们可能会很容易想到是this引起的,this两次都指向了baseB对象,但是推测真是这样吗?

为了探究实质,我们借助chrome浏览器的调试工具,下断点,进行调试,结果发现:

js中call与apply的用法小结

当调用extend.showSelfA();时,此时的this指向extendAB(并不是我们推测的两次都指向baseB对象

真实原因是extendAB对象的成员变量member在被baseB.call(this);实例化时,被baseB的成员member覆盖了,即extendAB的成员member由baseA member赋值成了baseB member

当然,我们也可以对上面baseA代码稍作修改,来验证我们调试分析的正确性:

function baseA()  // base Class A
{
 this.memberA = "baseA member";   // member改成memberA,以区分baseB中的member
 this.showSelfA = function()
 {
  window.alert(this.memberA);    // 显示memberA
 }
}

再次运行chrome等浏览器,结果如下:

baseA  member
baseB member

结果和我们的预期相同,同时chrome调试信息也验证了我们的正确性:

js中call与apply的用法小结

继承改进(prototype)

以上模拟继承方法,仔细分析不是最好的。

因为每次在函数(类)中定义了成员方法,都会导致实例有副本,因此可以借助prototype原型,进行改进

改进举例如下:

<!doctype html>
<html>
 <head>
  <title> call - apply for prototype </title>
 </head>
 <body>
  <script type="text/javascript">
   var Class = {
    create: function()    // create Function
    {
     return function()
     {
      this.initialize.apply(this, arguments);
     }
    }
   };
   var Person = Class.create();  // Create Class Person
   Person.prototype = {    // prototype initialize
    initialize: function(obj1, obj2)
    {
     this.obj1 = obj1;
     this.obj2 = obj2;
    },
    showSelf: function()
    {
     alert("obj: " + this.obj1 + " and " + this.obj2);
    }
   }
   // instance Class
   var person = new Person("man", "women"); // two params
   person.showSelf();       // show person
  </script>
 </body>
</html>

运行结果如下:
obj: man and women
Javascript 相关文章推荐
解决FLASH需要点击激活的代码
Dec 20 Javascript
基于jquery的网页SELECT下拉框美化代码
Oct 28 Javascript
JQuery加载图片自适应固定大小的DIV
Sep 12 Javascript
jQuery遍历页面所有CheckBox查看是否被选中的方法
Apr 14 Javascript
Jquery中的$.each获取各种返回类型数据的使用方法
May 03 Javascript
理解 JavaScript Scoping &amp; Hoisting(二)
Nov 18 Javascript
用v-html解决Vue.js渲染中html标签不被解析的问题
Dec 14 Javascript
利用vue和element-ui设置表格内容分页的实例
Mar 02 Javascript
JavaScript同源策略和跨域访问实例详解
Apr 03 Javascript
详解vue中的父子传值双向绑定及数据更新问题
Jun 13 Javascript
JavaScript canvas仿代码流瀑布
Feb 10 Javascript
JS 逻辑判断不要只知道用 if-else 和 switch条件判断(小技巧)
May 27 Javascript
SinaEditor使用方法详解
Dec 28 #Javascript
Jqgrid表格随窗口大小改变而改变的简单实例
Dec 28 #Javascript
jqGrid随窗口大小变化自适应大小的示例代码
Dec 28 #Javascript
jQuery对Select的操作大集合(收藏)
Dec 28 #Javascript
Jqgrid设置全选(选择)及获取选择行的值示例代码
Dec 28 #Javascript
jquery改变tr背景色的示例代码
Dec 28 #Javascript
javaScript对文字按照拼音排序实现代码
Dec 27 #Javascript
You might like
如何用PHP实现插入排序?
2013/04/10 PHP
获取php页面执行时间,数据库读写次数,函数调用次数等(THINKphp)
2013/06/03 PHP
从零开始学YII2框架(五)快速生成代码工具 Gii 的使用
2014/08/20 PHP
php中base_convert()进制数字转换函数实例
2014/11/20 PHP
thinkphp特殊标签用法概述
2014/11/24 PHP
JavaScript实现滚动栏效果的方法
2015/04/27 PHP
实例讲解php实现多线程
2019/01/27 PHP
PHP getNamespaces()函数讲解
2019/02/03 PHP
PHP 进程池与轮询调度算法实现多任务的示例代码
2019/11/26 PHP
jQuery中DOM树操作之使用反向插入方法实例分析
2015/01/23 Javascript
JavaScript表格常用操作方法汇总
2015/04/15 Javascript
对象题目的一个坑 理解Javascript对象
2015/12/22 Javascript
将json转换成struts参数的方法
2016/11/08 Javascript
JavaScript实现多栏目切换效果
2016/12/12 Javascript
JavaScript实现三级联动菜单实例代码
2017/06/26 Javascript
[js高手之路]图解javascript的原型(prototype)对象,原型链实例
2017/08/28 Javascript
requireJS模块化实现返回顶部功能的方法详解
2017/10/16 Javascript
自定义PC微信扫码登录样式写法
2017/12/12 Javascript
vue实现a标签点击高亮方法
2018/03/17 Javascript
微信小程序scroll-view实现滚动穿透和阻止滚动的方法
2018/08/20 Javascript
vue-i18n结合Element-ui的配置方法
2019/05/20 Javascript
JavaScript实现网页计算器功能
2020/10/29 Javascript
python列表操作使用示例分享
2014/02/21 Python
python常用web框架简单性能测试结果分享(包含django、flask、bottle、tornado)
2014/08/25 Python
python使用logging模块发送邮件代码示例
2018/01/18 Python
python Web开发你要理解的WSGI &amp; uwsgi详解
2018/08/01 Python
Django框架模板的使用方法示例
2019/05/25 Python
pyqt5 lineEdit设置密码隐藏,删除lineEdit已输入的内容等属性方法
2019/06/24 Python
关于Python3 lambda函数的深入浅出
2019/11/27 Python
Python如何生成xml文件
2020/06/04 Python
pycharm激活码免费分享适用最新pycharm2020.2.3永久激活
2020/11/25 Python
传统HTML页面实现模块化加载的方法
2018/10/15 HTML / CSS
Reebok俄罗斯官方网上商店:购买锐步运动服装和鞋子
2016/09/26 全球购物
四查四看自我剖析材料
2014/09/19 职场文书
2015年高三毕业班班主任工作总结
2015/10/22 职场文书
基于Redis zSet实现滑动窗口对短信进行防刷限流的问题
2022/02/12 Redis