zoukankan      html  css  js  c++  java
  • JavaScript "this"

    你不知道的JavaScript -"this"

    作者:棕熊[http://www.cnblogs.com/ruxpinsp1]

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

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

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

    大家看看这个例子:

    1. var foo = function() {   
    2.     console.log(this);  
    3. }  
    4. foo();  
    5. new foo(); 

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

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

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

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

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

    1. <input type="button" id="aButton" value="demo" onclick="" /> 
    2. <script type="text/JavaScript"> 
    3. function demo() {    this.value = Math.random();  
    4. }  
    5. script> 

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

     
    如果我们通过创建副本的方式,将这个函数的副本添加到一个HTML元素中,那么它的所有者就成了这个元素,this也指代了这个元素:
    1. document.getElementById("aButton").onclick = demo

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

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


    但是如果这样定义某个元素的onlick事件:
    1. <input type="button" id="aButton" value="demo" onclick="demo()" /> 

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

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

    具体看一下区别吧,使用创建函数副本的方法:

    1. <input type="button" id="aButton" value="demo" /> 
    2. <script type="text/JavaScript"> 
    3. var button = document.getElementById("aButton");  
    4. function demo() {  
    5.     this.value = Math.random();  
    6. }  
    7. button.onclickdemo;  
    8. alert(button.onclick);  
    9. script> 

    得到的输出是:

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

    使用函数引用的方法:

    1. <input type="button" id="aButton" value="demo" 
      onclick="demo()" /> 
    2. <script type="text/JavaScript"> 
    3. var button = document.getElementById("aButton");  
    4. function demo() {  
    5.     this.value = Math.random();  
    6. }  
    7. alert(button.onclick);  
    8. script> 

    得到的输出是:

    1. function onclick() {  
    2.     demo();  

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

     

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

    程序员么,节约为主,所以我们来看一个更好的解决方案:

    1. <script type="text/JavaScript"> 
    2. function demo(obj)  {  
    3.     obj.value = Math.random();  
    4. }  
    5. script> 
    6. <input type="button" value="demo" onclick="demo(this)" /> 
    7. <input type="button" value="demo" onclick="demo(this)" /> 
    8. <input type="button" value="demo" onclick="demo(this)" /> 

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

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

    1. function new (somefunction)  {  
    2. var args = [].slice.call(arguments, 1);  
    3.     somefunctionsomefunction.prototype.
      constructor
       = somefunction;  
    4.     somefunction.apply(somefunction.prototype, args);  
    5.     return somefunction.prototype;  

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

    Top
    收藏
    关注
    评论
  • 相关阅读:
    EPANET头文件解读系列4——EPANET2.H
    EPANET头文件解读系列3——TOOLKIT.H
    EPANET头文件解读系列2——ENUMSTXT.H
    EPANET头文件解读系列1——TEXT.H
    ENUMSTXT.H中的指针数组
    main函数的参数
    函数指针与指针函数
    FMDB源码阅读
    17个提升iOS开发效率的必用工具
    UIKit性能调优实战讲解
  • 原文地址:https://www.cnblogs.com/judypol/p/1547149.html
Copyright © 2011-2022 走看看