• this的指向


    this 的指向

    前面我们学习 JavaScript 基础的对象章节时,曾经接触过 this 关键字。对象里面的 this 关键字指向当前对象。在学习 DOM 编程中的事件章节时,也遇到过 this 关键字,指向绑定事件的元素。

    在本小节中,我们就一起来总结一下 JavaScript 中 this 指向的几种情况,同时来看一下如何对 this 的指向进行修改。

    this 指向的默认情况

    1. this 指向当前对象

    如果是在对象里面使用 this,则指向当前对象。this 可以用在方法内,获取对对象属性的访问。示例如下:

    const person = {
       name: 'xiejie',
       age: 18,
       intro: function () {
           console.log(this); // { name: 'xiejie', age: 18, intro: [Function: intro] }
           console.log(`My name is ${this.name},I'm ${this.age} years old`); // My name is xiejie,I'm 18 years old
      }
    }
    person.intro();

    再来看一个构造函数中的 this 指向,也是同样指向当前对象,示例如下:

    const Computer = function (name, price) {
       this.name = name;
       this.price = price;
    }
    Computer.prototype.showSth = function () {
       console.log(`这是一台${this.name}电脑`);
    }
    let apple = new Computer('苹果', 15000);
    let asus = new Computer('华硕', 4500);
    apple.showSth(); // 这是一台苹果电脑
    asus.showSth(); // 这是一台华硕电脑

    2. 普通函数中的 this 指向

    如果是普通调用的函数,this 则指向全局对象,如下:

    const test = function () {
       console.log(this); // global 对象
       // 在 node 里面,全局对象就是 global 对象
       // 如果是在浏览器里面,那么全局对象代表 window 对象
    }
    test(); // 普通函数调用,this 将会指向全局对象

    需要说明的是,以普通函数的方式调用的时候,无论嵌套多少层函数,this 都始终指向全局对象,如下:

    const test = function () {
       const test2 = function () {
           const test3 = function () {
               console.log(this); // global 对象
          }
           test3();
      }
       test2();
    }
    test();

    接下来我们再来看一个例子,如下:

    var name = "PHP";
    const obj = {
       name: "JavaScript"
    }
    const show = function () {
       console.log("Hello," + this.name);
    }
    obj.fn = show; // 将 show() 方法赋值给 obj 对象的 fn 属性
    obj.fn(); // Hello,JavaScript
    show(); // Hello,undefined 如果是浏览器环境则是 Hello,PHP

    这里,我们将show()方法赋值给 obj 对象的 fn 属性,所以在执行obj.fn()时是以对象的形式来调用的,this 将会指向 obj 对象。而后面的show()方法则是单纯的以普通函数的形式来进行调用的。所以 this 指向全局对象。

    3. this 指向绑定事件的元素

    关于这一种 this 的指向,我们在学习事件章节时也接触过。DOM 元素绑定事件时,事件处理函数里面的 this 指向绑定了事件的元素。这个地方一定要注意它和 target 的区别,target 是指向触发事件的元素。

    示例如下:

    <body>
       <ul id="color-list">
           <li>red</li>
           <li>yellow</li>
           <li>blue</li>
           <li>green</li>
           <li>black</li>
           <li>white</li>
       </ul>
       <script>
           // this 是绑定事件的元素
           // target 是触发事件的元素 和 srcElememnt 等价
           let colorList = document.getElementById("color-list");
           colorList.addEventListener("click", function (event) {
               console.log('this:', this);
               console.log('target:', event.target);
               console.log('srcElement:', event.srcElement);
          })
       </script>
    </body>

    当我点击如下位置时打印出来的信息如下:

    -w623

    有些时候我们会遇到一些困扰,比如在 div 节点的事件函数内部,有一个局部的 callback 方法,该方法被作为普通函数调用时,callback 内部的 this 是指向全局对象 window 的,示例如下:

    <body>
       <div id="div1">我是一个div</div>
       <script>
           window.id = 'window';
           document.getElementById('div1').onclick = function(){
               console.log(this.id); // div1
               const callback = function(){
                   console.log(this.id); // 因为是普通函数调用,所以 this 指向 window
              }
               callback();
          }
       </script>
    </body>

    此时有一种简单的解决方案,可以用一个变量保存 div 节点的引用,如下:

    <body>
       <div id="div1">我是一个div</div>
       <script>
           window.id = 'window';
           document.getElementById('div1').onclick = function(){
               console.log(this.id); // div1
               const that = this; // 保存当前 this 的指向
               const callback = function(){
                   console.log(that.id); // div1
              }
               callback();
          }
       </script>
    </body>

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

    <body>
       <div id="div1">我是一个div</div>
       <script>
           window.id = 'window';
           document.getElementById('div1').onclick = function(){
               console.log(this); // <div id="div1">我是一个div</div>
               const callback = function(){
                   "use strict"
                   console.log(this); // undefined
              }
               callback();
          }
       </script>
    </body>

    改变 this 的指向

    在前面的小节中,我们已经总结出了在 JavaScript 中关于 this 指向的几种情况。但是,这个 this 的指向,我们是可以进行修改的。

    接下来,我们就来看一看几种修改 this 指向的方法。

    1. 方法借用函数修改 this 指向

    前面我们有介绍过 JavaScript 中的方法借用模式,使用call()或者apply()可以借用其他对象的方法而不用通过继承。

    在借用的同时,call()apply()也算是间接修改了 this 的指向。示例如下:

    const Test = function () {
       this.name = "JavaScript";
       this.say = function () {
           console.log(`这是${this.name}`);
      }
    }
    const test = new Test();
    test.say(); // 这是JavaScript
    const a = {
       name: "PHP"
    };
    test.say.call(a); // 这是PHP

    这里我们借用了 test 对象的say()方法。本来对于 test 对象的say()方法来讲,this 是指向 test 对象的,但是现在 a 对象借用了say()方法以后,this 就指向了 a 对象,所以打印出了"这是PHP"。

    2. bind 方法绑定 this 指向

    第二种方式是可以通过bind()方法来绑定 this 的指向。bind()方法的语法如下:

    fun.bind(thisArg[, arg1[, arg2[, ...]]])

    参数:

    thisArg:当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。
    arg1,arg2,...:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

    具体的示例如下:

    const a = {
       name: "PHP"
    };
    const Test = function () {
       this.name = "JavaScript";
       this.say = function () {
           console.log(`这是${this.name}`);
      }.bind(a);
    }
    const test = new Test();
    test.say(); // 这是 PHP

    这里我们在say()方法后面使用了bind()方法,并将 a 对象作为参数传递进去。这个时候,当我们以 test 对象的身份来调用say()方法时,打印出来的仍然是"这是PHP",这是因为bind()方法在这里将 this 的指向绑定到了 a 对象上。

    3. 箭头函数的 this 指向

    当我们的 this 是以函数的形式调用时,this 指向的是全局对象。不过对于箭头函数来讲,却比较特殊。箭头函数的 this 指向始终为外层的作用域。

    先来看一个普通函数作为对象的一个方法被调用时,this 的指向,如下:

    const obj = {
       x : 10,
       test : function(){
           console.log(this); // 指向 obj 对象
           console.log(this.x); // 10
      }
    }
    obj.test();
    // { x: 10, test: [Function: test] }
    // 10

    可以看到,普通函数作为对象的一个方法被调用时,this 指向当前对象。在上面的例子中,就是 obj 这个对象,this.x 的值为 10。

    接下来是箭头函数以对象的方式被调用的时候的 this 的指向,如下:

    var x = 20;
    const obj = {
       x: 10,
       test: () => {
           console.log(this); // {}
           console.log(this.x); // undefined
      }
    }
    obj.test();
    // {}
    // undefined

    这里的结果和上面不一样,this 打印出来为{},而 this.x 的值为 undefined。

    为什么呢?实际上刚才我们有讲过,箭头函数的 this 指向与普通函数不一样,它的 this 指向始终是指向的外层作用域。所以这里的 this 实际上是指向的全局对象。

    如果证明呢?方法很简单,将这段代码放入浏览器运行,在浏览器中用 var 所声明的变量会成为全局对象 window 的一个属性,如下:

    -w639

    接下来我们再来看一个例子,来证明箭头函数的this指向始终是指向的外层作用域。

    var name = "JavaScript";
    const obj = {
       name: "PHP",
       test: function () {
           const i = function () {
               console.log(this.name);
               // i 是以函数的形式被调用的,所以 this 指向全局
               // 在浏览器环境中打印出 JavaScript,node 里面为 undeifned
          }
           i();
      }
    }
    obj.test(); // JavaScript

    接下来我们将 i 函数修改为箭头函数,如下:

    var name = "JavaScript";
    const obj = {
       name : "PHP",
       test : function(){
           const i = ()=>{
               console.log(this.name);
               // 由于 i 为一个箭头函数,所以 this 是指向外层的
               // 所以 this.name 将会打印出 PHP
          }
           i();
      }
    }
    obj.test();// PHP

    最后需要说一点的就是,箭头函数不能作为构造函数,如下:

    const Test = (name, age) => {
       this.name = name;
       this.age = age;
    };
    const test = new Test("xiejie", 18);
    // TypeError: Test is not a constructor

    总结

    1. 所谓编程范式,指的是计算机编程的基本风格或典范模式。大致可以分为命令式编程和声明式编程。

    2. 面向对象的程序设计是站在哲学的角度上,将人类思维融入到了程序里面的一种编程范式。

    3. 描述对象的时候可以通过两个方面来进行描述,分别是对象的外观和功能。在程序中与之对象的就是属性和方法。

    4. JavaScript 中的对象都是基于原型的,从一个原型对象中可以克隆出一个新的对象。

    5. 在 JavaScript 中每个对象都有一个原型对象。可以通过proto属性来找到一个对象的原型对象。

    6. 在其他语言中,对象从类中产生。而在 JavaScript 中,通过构造函数来模拟其他语言中的类。

    7. 类与对象的关系可以总结为,类是对对象的一种概括,而对象是类的一种具体实现。

    8. 面向对象的 3 大特征为封装,继承和多态。

    9. 封装是指对象的属性或者方法隐藏于内部,不暴露给外部。

    10. 继承是指一个子类继承一个父类。在继承了父类以后,子类就拥有了父类所有的属性和方法。

    11. 多态是指不同的对象可以拥有相同的方法,不过是以不同的方式来实现它。

    12. this 的指向根据使用的地方不同分为好几种情况,但是我们可以通过一些方式来修改 this 的指向。

    13.  

     

  • 相关阅读:
    [转]java抽象类和接口和继承之间关系
    HTML accesskey 属性
    [转]OGNL使用小结
    [转] iBATIS框架理论学习篇
    Python Pyramid with MySQL
    Python Pyramid with PostgreSQL
    PostgreSQL 安装
    wpa_supplicant on linux
    postgresql 常用命令 {转载}
    Arduino 各种模块篇 [留个小任务,现在去学习python 网络编程]
  • 原文地址:https://www.cnblogs.com/liqiang95950523/p/13447758.html
Copyright © 2020-2023  润新知