首页 > 编程语言 >javascript的 this 详解以及apply与call的用法意义及区别

javascript的 this 详解以及apply与call的用法意义及区别

时间:2023-05-24 13:04:55浏览次数:77  
标签:function 函数 对象 javascript call var apply foo


[color=red][b]关于JavaScript中apply与call的用法意义及区别[/b][/color]
[url]http://www.cnitblog.com/yemoo/archive/2007/11/30/37070.aspx[/url]


[color=red][b]JAVASCRIPT THIS详解[/b][/color]
在面向对象编程语言中,对于this关键字我们是非常熟悉的。比如C++、C#和Java等都提供了这个关键字,虽然在开始学习的时候觉得比较难,但只要理解了,用起来是非常方便和意义确定的。JavaScript也提供了这个this关键字,不过用起来就比经典OO语言中要"混乱"的多了。

下面就来看看,在JavaScript中各种this的使用方法有什么混乱之处?

1、在HTML元素事件属性中inline方式使用this关键字:

<div onclick="
 // 可以在里面使用this

 ">division element</div>



我们一般比较常用的方法是在此使用:javascirpt: EventHandler(this),这样的形式。不过这里其实可以写任何合法的JavaScript语句,要是高兴在此定义个类也可以(不过将会是个内部类)。这里的原理是脚本引擎生成了一个div实例对象的匿名成员方法,而onclick指向这个方法。



2、用DOM方式在事件处理函数中使用this关键字:



<div id="elmtDiv">division element</div>
 <script language="javascript">
 var div = document.getElementById('elmtDiv');
 div.attachEvent('onclick', EventHandler);

 function EventHandler()
 {
    // 在此使用this
 }
 </script>



这时的EventHandler()方法中的this关键字,指示的对象是IE的window对象。这是因为EventHandler只是一个普通的函数,对于attachEvent后,脚本引擎对它的调用和div对象本身没有任何的关系。同时你可以再看看EventHandler的caller属性,它是等于null的。如果我们要在这个方法中获得div对象引用,应该使用:this.event.srcElement。



3、用DHTML方式在事件处理函数中使用this关键字:



<div id="elmtDiv">division element</div>
 <script language="javascript">
 var div = document.getElementById('elmtDiv');
 div.onclick = function()
 {
    // 在此使用this
 };
 </script>



这里的this关键字指示的内容是div元素对象实例,在脚本中使用DHTML方式直接为div.onclick赋值一个EventHandler的方法,等于为div对象实例添加一个成员方法。这种方式和第一种方法的区别是,第一种方法是使用HTML方式,而这里是DHTML方式,后者脚本解析引擎不会再生成匿名方法。



4、类定义中使用this关键字:



function JSClass()
  {
      var myName = 'jsclass';
      this.m_Name = 'JSClass';
  }

  JSClass.prototype.ToString = function()
  {
      alert(myName + ', ' + this.m_Name);
  };

  var jc = new JSClass();
  jc.ToString();



这是JavaScript模拟类定义中对this的使用,这个和其它的OO语言中的情况非常的相识。但是这里要求成员属性和方法必须使用this关键字来引用,运行上面的程序会被告知myName未定义。




5、为脚本引擎内部对象添加原形方法中的this关键字:



Function.prototype.GetName = function()
  {
      var fnName = this.toString(); 
      fnName = fnName.substr(0, fnName.indexOf('(')); 
      fnName = fnName.replace(/^function/, ''); 
      return fnName.replace(/(^\s+)|(\s+$)/g, '');
  }
  function foo(){}
  alert(foo.GetName());




这里的this指代的是被添加原形的类的实例,和4中类定义有些相似,没有什么太特别的地方。



6、结合2&4,说一个比较迷惑的this关键字使用:



function JSClass()
  {
      this.m_Text = 'division element';
      this.m_Element = document.createElement('DIV');
      this.m_Element.innerHTML = this.m_Text;

      this.m_Element.attachEvent('onclick', this.ToString);
  }

  JSClass.prototype.Render = function()
  {
      document.body.appendChild(this.m_Element);
  }     

  JSClass.prototype.ToString = function()
  {
      alert(this.m_Text);
  };

  var jc = new JSClass();
  jc.Render(); 
  jc.ToString();



我就说说结果,页面运行后会显示:"division element",确定后点击文字"division element",将会显示:"undefined"。



7、CSS的expression表达式中使用this关键字:



<table width="100" height="100">
      <tr>
          <td>
              <div style="width: expression(this.parentElement.width); 
                    height: expression(this.parentElement.height);">
                  division element</div>
          </td>
      </tr>
  </table>



这里的this看作和1中的一样就可以了,它也是指代div元素对象实例本身。



8、函数中的内部函数中使用this关键字:



function OuterFoo()
  {
      this.Name = 'Outer Name';

      function InnerFoo()
      {
          var Name = 'Inner Name'; 
          alert(Name + ', ' + this.Name);
      }
      return InnerFoo;
  }
  OuterFoo()();



运行结果显示是:"Inner Name, Outer Name"。按我们在2中的讲解,这里的结果如果是"Inner Name, undefined"似乎更合理些吧?但是正确的结果确实是前者,这是由于JavaScript变量作用域的问题决定的,详细了解推荐参看"原来JScript中的关键字'var'还是有文章的"一文及回复。




[b][color=red]你不知道的 JavaScript - “this”[/color][/b]



JavaScript 里的 this 到底指得是什么?很多人都会告诉你 this 指的是当前对象。这样理解对么?在大多数情况下确实没错。比如我们经常会在网页上写这样的 JavaScript:



<input type="submit" value="提交" onclick="this.value='正在提交数据'" />


这里的this显然指的是当前对象,即这个提交按钮。通常,我们使用this的情况都与此类似。但是有什么情况不是这样的呢?



大家看看这个例子:


var foo = function() {
    console.log(this);
}
foo();
new foo();


比较一下 foo() 和 new foo() 的运行结果,你会发现,前者 this 指向的并非 foo 本身,而是当前页面的window对象,而后者才真正的指向foo。这是为什么呢?



其实这牵涉到JavaScript的一条重要特性,就是所谓的“闭包”。闭包这个概念说复杂也不复杂,但也不是简单到能用一两句话说清。偶会在以后的文章中深入探讨这个Javascript 最重要的特性。现在,我要告诉大家的是,因为闭包的存在,JavaScript中的作用域变得相当重要。



所谓的作用域,简单的说,就是创建一个函数时在什么环境下创建的。而this变量的值,如果没有指定的话,就是函数当前的作用域。




在前面的例子里,foo() 函数是在全局作用域(这里就是window对象),所以this的值是当前的window对象。而通过 new foo() 这样的形式,其实是创建了一个foo()的副本,并在这个副本上进行的操作,所以这里的this就是foo()的这个副本。




这样讲可能有点抽象,大家来看个实际的例子:


<input type="button" id="aButton" value="demo" onclick="" />
<script type="text/javascript">
function demo() {
    this.value = Math.random();
}
</script>


如果直接调用demo() 函数,程序就会报错,因为demo函数是在window对象中定义的,所以demo的拥有者(作用域)是window,demo的this也是window。而window是没有value属性的,所以就报错了。




如果我们通过创建副本的方式,将这个函数的副本添加到一个HTML元素,那么他的所有者就成了这个元素,this也指代了这个元素:


document.getElementById("aButton").onclick = demo;


这样就将aButton的onlick属性设置为demo()的一个副本,this也指向了aButton。



你甚至可以为多个不同的HTML元素创建不同的函数副本。每个副本的拥有者都是相对应的HTML元素,各自的this也都指向他们的拥有者,不会造成混乱。



但是,如果你这样定义某个元素的onlick事件:


<input type="button" id="aButton" value="demo" onclick="demo()" />


点击这个button之后,你会发现,程序又会报错了——this又指向了window!



其实,这种方法并没有为程序创建一个函数,而只是引用了这个函数。



具体看一下区别吧。


使用创建函数副本的方法:


<input type="button" id="aButton" value="demo" />
<script type="text/javascript">
var button = document.getElementById("aButton");
function demo() {
    this.value = Math.random();
}
button.onclick= demo;
alert(button.onclick);
</script>




得到的输出是:


function demo() {
    this.value = Math.random();
}




使用函数引用的方法:


<input type="button" id="aButton" value="demo" onclick="demo()" />
<script type="text/javascript">
var button = document.getElementById("aButton");
function demo() {
    this.value = Math.random();
}
alert(button.onclick);
</script>



得到的输出是:


function onclick() {
    demo();
}




这样就能看出区别了吧。函数引用的方式中,onclick事件只是直接调用demo()函数,而demo()函数的作用域仍旧是window对象,所以this仍然指向window。



这样就又引出了一个问题:既然函数副本这么好用,为什么还需要函数引用的方法呢?答案是性能。每新建一个函数的副本,程序就会为这个函数副本分配一定的内存。而实际应用中,大多数函数并不一定会被调用,于是这部分内存就被白白浪费了。而使用函数引用的方式,程序就只会给函数的本体分配内存,而引用只分配指针,这样效率就高很多。程序员么,节约为主,恩



所以我们来看一个更好的解决方案:


<script type="text/javascript">
function demo(obj) {
    obj.value = Math.random();
}
</script>
<input type="button" value="demo" onclick="demo(this)" />
<input type="button" value="demo" onclick="demo(this)" />
<input type="button" value="demo" onclick="demo(this)" />




这样,效率和需求就都能兼顾了。




最后再多讲一句:在前面的文章里,我特别强调了“如果没有指定this的话”。其实this是可以指定的。Function对象有两个方法:call()和apply()。这两个方法都支持指定一个函数中的this。有兴趣的话您可以去查一下Javascript的手册,看看这两个函数都是干什么用的。而我们经常用的 new foo() 可以用以下这段伪代码来描述:


function new (somefunction) {
    var args = [].slice.call(arguments, 1);
    somefunction.prototype.constructor = somefunction;
    somefunction.apply(somefunction.prototype, args);
    return somefunction.prototype;
}




现在明白了,在本文开头的第一个例子里,new foo() 的 this 为什么是 foo 了吧.




[color=red][b]关于JavaScript中apply与call的用法意义及区别[/b][/color]


[url]http://www.cnitblog.com/yemoo/archive/2007/11/30/37070.aspx[/url]


JavaScript中有一个call和apply方法,其作用基本相同,但也有略微的区别。



先来看看JS手册中对call的解释:



call 方法


调用一个对象的一个方法,以另一个对象替换当前对象。



call([thisObj[,arg1[, arg2[, [,.argN]]]]])



参数


thisObj


可选项。将被用作当前对象的对象。



arg1, arg2, , argN


可选项。将被传递方法参数序列。



说明


call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。



如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。



说明白一点其实就是更改对象的内部指针,即改变对象的this指向的内容。这在面向对象的js编程过程中有时是很有用的。



引用网上一个代码段,运行后自然就明白其道理。



<input type="text" id="myText"   value="input text">
<script>
    function Obj(){this.value="对象!";}
    var value="global 变量";
    function Fun1(){alert(this.value);}

    window.Fun1();   //global 变量, 默认window赋值给方法里面的this
    Fun1.call(window);  //global 变量, 调用方法,并把方法里面的this指向window这个参数
    Fun1.call(document.getElementById('myText'));  //input text, 调用方法,并把方法里面的this指向document.getElementById('myText')这个参数
    Fun1.call(new Obj());   //对象!, 调用方法,并把方法里面的this指向new Obj()这个参数
</script>



call函数和apply方法的第一个参数都是要传入给当前对象的对象,及函数内部的this。后面的参数都是传递给当前对象的参数。


运行如下代码:


<script>
   var func=new function(){this.a="func"}
    var myfunc=function(x){
        var a="myfunc";
        alert(this.a);
        alert(x);
    }
    myfunc.call(func,"var");
</script>



可见分别弹出了func和var。到这里就对call的每个参数的意义有所了解了。



对于apply和call两者在作用上是相同的,但两者在参数上有区别的。


[color=red]对于第一个参数意义都一样,但对第二个参数:


apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。[/color]


如 func.call(func1,var1,var2,var3)对应的apply写法为:func.apply(func1,[var1,var2,var3])



[b]同时使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入[/b]


标签:function,函数,对象,javascript,call,var,apply,foo
From: https://blog.51cto.com/u_3871599/6338632

相关文章

  • 报错:TypeError: Found non-callable @@iterator
    代码://自动导入插件constautpImport=require('unplugin-auto-import/webpack')(...autoImportConfig)报错 TypeError:Foundnon-callable@@iterator原因:使用...扩展符的时候该对象是不可迭代对象。这里  autoImportConfig是一个对象,该函数参数需要传入......
  • #yyds干货盘点#JavaScript的数学对象——Math对象
    Math对象●js给我们提供了一些操作数字的方法●也是一种数据类型是复杂数据类型●Math对象的通用语法:Math.xxx()random()●Math.random()这个方法是用来生成一个0~1之间的随机数●每次执行生成的数字都不一样,但是一定是0~1之间的●生成的数字包含0,但是不包含1var......
  • How to Delete a Git Branch Both Locally and Remotely
    TL;DRversionhttps://www.freecodecamp.org/news/how-to-delete-a-git-branch-both-locally-and-remotely///deletebranchlocallygitbranch-dlocalBranchName//deletebranchremotelygitpushorigin--deleteremoteBranchName#ToMakeLinuxshowuser@h......
  • javascript中的错误类型
    javascript中的错误类型:SyntaxErrorTypeErrorReferenceErrorRangeErrorURLErrorErrorSyntaxError语法错误//当您在编写一个函数时忘记了括号,)来括起您的代码,您将收到一个SyntaxError错误functionsay(text){returntext;}say('shark';//outputUncaug......
  • JavaScript正则获取a标签中的path路径值-流程引擎-计算引擎
    直接上代码://获取附件中的链接地址functionget_file_path_from_encode_value(x){vararrLink=[];x.replace(/<a[^>]*path=['"]([^'"]+)[^>]*/gi,function(match,capture){arr......
  • JavaScript函数
    1函数定义使用function关键字来定义,即functionfName(para,...){statment;...;},可使用在函数声明语句与函数定义表达式这两种形式中函数名称标识符fName。是函数声明语句必需的部分。它的用途就像变量的名字,新定义的函数对象会赋值给这个变量但对函数定义表达式来说......
  • 记录--九个超级好用的 Javascript 技巧
    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助前言在实际的开发工作过程中,积累了一些常见又超级好用的Javascript技巧和代码片段,包括整理的其他大神的JS使用技巧,今天筛选了9个,以供大家参考。1、动态加载JS文件在一些特殊的场景下,特别是一些库和框架......
  • javascript基础2
    script标签里一旦使用src加载外部js文件后,这个script标签就不能写js语句了警告框!通知用户出错了。alert("哈哈!")输入框-输入内容prompt得到输入内容永远都是字符串varn1=prompt("第一个数")varn2=prompt("第二个数")问询框-confirm("呵呵")使用弹出框来显示结果......
  • JavaScript中实现文件上传下载的三种解决方案(推荐)
    ​ 以ASP.NETCoreWebAPI 作后端 API ,用 Vue 构建前端页面,用 Axios 从前端访问后端 API,包括文件的上传和下载。 准备文件上传的API #region 文件上传  可以带参数        [HttpPost("upload")]        publicJsonResultuploadProject(I......
  • 【iOS开发】UIWebView调用JS点击事件(stringByEvaluatingJavaScriptFromString)
    一、场景描述产品需求是移动端app要调用h5页面,然后监听h5代码中的某个方法,最终执行h5中的具体代码。二、具体代码.m文件@interfaceViewController()<UIWebViewDelegate>@property(nonatomic,strong)UIWebView*webView;@end@implementationViewController-(void)viewDid......