• 490 JavaScript的this的五种指向


    • THIS:
      全局上下文中的THIS是WINDOW;
      块级上下文中没有自己的THIS,它的THIS是继承所在上下文中的THIS的;【和箭头函数类似。】
      在函数的私有上下文中,THIS的情况会多种多样,也是接下重点研究的.
      *
    • THIS不是执行上下文(EC才是执行上下文),THIS是执行主体
    • 例如:刘德华拿着加了五个鸡蛋的鸡蛋灌饼去北京大饭店吃早餐(事情本身是吃早餐,刘德华吃早餐,这件事情的主体是刘德华【THIS】,在北京饭店吃,北京饭店是事情发生所在的上下文【EC】)
      *
      *
    • 如何区分执行主体?
      1. 事件绑定:给元素的某个事件行为绑定方法,当事件行为触发,方法执行,方法中的THIS是当前元素本身(特殊:IE6~8中基于attachEvent方法实现的DOM2事件绑定,事件触发,方法中的THIS是WINDOW,而不是元素本身)。
      1. 普通方法执行(包含自执行函数执行、普通函数执行、对象成员访问调取方法执行等):只需要看函数执行的时候,方法名前面是否有“点”,有“点”,“点”前面是谁,THIS就是谁,没有“点”,THIS就是WINDOW[非严格模式]/UNDEFINED[严格模式]。
      1. 构造函数执行(NEW XXX):构造函数体中的THIS是当前类的实例。
      1. ES6中提供了ARROW FUNCTION(箭头函数): 箭头函数没有自己的THIS,它的THIS是继承所在上下文中的THIS。
      1. 可以基于CALL/APPLY/BIND等方式,强制手动改变函数中的THIS指向:这三种模式是很直接很暴力的(前三种情况在使用这三个方法的情况后,都以手动改变的为主)。
    // 验证 块级上下文中没有自己的this,它的this是继承所在上下文中的this的;【和箭头函数类似】
    console.log(c) // undefined
    if (true) {
      console.log(c) // 函数c
      let b = 2
      function c() {
        console.log(3)
      }
      let e = 5
      console.log(this.b) // undefined
      console.log(this.c) // 函数c
      console.log(this.e) // undefined
    }
    
    console.log(c) // 函数c
    

    // 事件绑定 DOM0  DOM2
    let body = document.body;
    body.onclick = function () {
      // 事件触发,方法执行,方法中的THIS是BODY
      console.log(this);
    };
    body.addEventListener('click', function () {
      console.log(this); // => BODY
    });
    
    
    // IE6~8中的DOM2事件绑定
    box.attachEvent('onclick', function () {
      console.log(this); // => WINDOW
    });
    
    
    // ----------------------------------
    
    
    // IIFE
    (function () {
      console.log(this); // => window
    })();
    
    
    // ----------------------------------
    
    
    let obj = {
      fn: (function () {
        console.log(this); // => window
        return function () { }
      })() //把自执行函数执行的返回值赋值给OBJ.FN
    };
    
    
    // ----------------------------------
    
    
    function func() {
      console.log(this);
    }
    let obj = {
      func: func
    };
    func(); // => 方法中的THIS: WINDOW 【前面没有点,window调用func】
    obj.func(); // => 方法中的THIS: OBJ【前面有点,obj调用func】
    
    
    // ----------------------------------
    
    
    // => 数组实例基于原型链机制,找到ARRAY原型上的SLICE方法([].slice),然后再把SLICE方法执行,此时SLICE方法中的THIS是当前的空数组
    [].slice();
    Array.prototype.slice(); // => SLICE方法执行中的THIS:Array.prototype
    [].__proto__.slice(); // => SLICE方法执行中的THIS:[].__proto__ === Array.prototype
    
    
    // ----------------------------------
    
    
    function func() {
      // THIS  =>  WINDOW
      console.log(this);
    }
    document.body.onclick = function () {
      // THIS  =>  BODY
      func();
    };
    
    
    // ----------------------------------
    
    
    
    function Func() {
      this.name = "F";
      // => 构造函数体中的THIS在“构造函数执行”的模式下,是当前类的一个实例,并且THIS.XXX = XXX是给当前实例设置的私有属性
      console.log(this);
    }
    
    Func.prototype.getNum = function getNum() {
      // 而原型上的方法中的THIS不一定都是实例,主要看执行的时候,“点”前面的内容
      console.log(this);
    };
    let f = new Func; // Func {name: "F"}
    f.getNum(); // Func {name: "F"}
    f.__proto__.getNum(); // {getNum: ƒ, constructor: ƒ}
    Func.prototype.getNum(); // {getNum: ƒ, constructor: ƒ}
    
    
    // ----------------------------------
    
    
    let obj = {
      func: function () {
        console.log(this);
      },
      sum: () => {
        console.log(this);
      }
    };
    obj.func(); // => THIS:OBJ
    obj.sum(); // => THIS是所在上下文(EC(G))中的THIS: WINDOW
    obj.sum.call(obj); // => THIS:WINDOW,箭头函数是没有THIS,所以哪怕强制改也没用  
    
    
    // ----------------------------------
    
    
    let obj = {
      i: 0,
      // func:function(){}
      func() {
        // THIS: OBJ
        let _this = this;
        setTimeout(function () {
          // THIS:WINDOW 回调函数中的THIS一般都是WINDOW(但是有特殊情况)
          _this.i++;
          console.log(_this);
        }, 1000);
      }
    };
    obj.func();
    
    
    // ----------------------------------
    
    
    let obj = {
      i: 0,
      func() {
        setTimeout(function () {
          // 基于BIND把函数中的THIS预先处理为OBJ
          this.i++;
          console.log(this);
        }.bind(this), 1000);
      }
    };
    obj.func();
    
    
    // ----------------------------------
    
    
    // 建议不要乱用箭头函数(部分需求用箭头函数还是很方法便的)
    let obj = {
      i: 0,
      func() {
        setTimeout(() => {
          // 箭头函数中没有自己的THIS,用的THIS是上下文中的THIS,也就是OBJ
          this.i++;
          console.log(this);
        }, 1000);
      }
    };
    obj.func();
    
    

    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
      <title>哈哈</title>
    </head>
    
    <body>
      <script>
        /* function func() {
          /!*
           * EC(FUNC)私有上下文
           *    作用域链: <当前上下文,函数作用域>
           *    初始化THIS:(执行主体 !== 执行上下文)
           *    初始化ARGUMENTS:(箭头函数不用,因为没有ARGUMENTS)
           *    形参赋值:
           *    变量提升:
           *    代码执行:
           *!/
          console.log(this);
        } */
      </script>
    
      <script>
        /*
         * 函数的执行主体是谁,和函数在在哪创建的,以及在哪执行的都没有关系 
         *    规律:
         *    1. document.body.onclick=function(){}:this -> body
         *    2. 方法执行,我们去看方法名前面是否有“点”,有“点”它前面是谁THIS就是谁,没有“点”在非严格模式下THIS是window,严格模式下THIS是UNDEFIND
         */
    
        window.name = "window";
        function func() {
          console.log(this);
        }
    
        let obj = {
          name: 'OBJ',
          func
        };
    
        func(); // => this: window
        obj.func(); // => this: obj 
    
    
        // -----------------------
    
    
        let arr = [];
        arr.slice(); // => slice中的this: arr
        arr.__proto__.slice(); // => slice中的this:arr.__proto__
    
    
        // -----------------------
    
    
        // 匿名函数(自执行函数、回调函数等)执行,一般方法中的THIS都是window|undefined
        (function () {
          console.log(this);
        })();
    
    
        // -----------------------
    
    
        setTimeout(function () {
          // 回调函数:把函数作为值传递给另外一个函数,在另外一个函数的某个阶段把它执行
          console.log(this); // window
        }, 1000);
    
    
        // -----------------------
    
    
        [10, 20, 30].forEach(function (item, index) {
          console.log(this); // forEach中第二个参数不传递this: window,传递[context],则this:[context]
        }, [context]);
      </script>
    </body>
    
    </html>
    

    练习题

    var x = 3;
    obj = { x: 5 };
    
    obj.fn = (function () {
      this.x *= ++x; // this: window
      return function (y) {
        // this: 调用者
        this.x *= (++x) + y;
        console.log(x);
      }
    })();
    
    // 补充
    // x += 2 => x = x + 2
    // x *= 2 => x = x * 2
    
    var fn = obj.fn;
    // y = 6, this.x *= ++x => window.x *= ++x => window.x = 3 * 4 = 12, 即window.x = 12
    // this.x *= (++x) + y, this = obj,  => obj.x = 5 * (13 + 6) = 95
    // console.log(x) => 13
    obj.fn(6);
    
    // y = 4, window.x = 13
    // this.x *= (++x) + y, this = window, => window.x = 13 * (14 + 4) = 234
    fn(4);
    console.log(obj.x, x);
    
  • 相关阅读:
    管理经济学之第三章(消费者效用分析)
    管理经济学之第二章(供求分析)
    JVM之GC回收信息详解
    管理经济学之第一章(导论)
    JAVA的引用类型
    6.jQuery动画和队列,简单的queue()入队和dequeue()出队实现
    5.jQuery实现简单的on()和trigger()方法
    4.jQuery的clone()方法和data()方法
    3.jQuery操作DOM对象的方法
    2.jQuery简单实现get()和eq()和add()和end()方法
  • 原文地址:https://www.cnblogs.com/jianjie/p/13208292.html
Copyright © 2020-2023  润新知