通过调用标识符确定this

阿弥陀佛

  • this代表函数执行时的环境,只能是在函数执行时才被确实的
  • 不应该在函数没有执行时去关注this;因为此时函数根本没有执行,代表执行环境的this更是未知数
  • 确定JS函数的this,只在该函数执行时

在函数执行时,确定this值

  1. 未使用绑定字:是否通过对象的方法来引用
    • 是:this就指向该对象
    • 否:宽松模式是window;严格模式是undefined
  2. 使用绑定词
    • this是该绑定的对象(如new、call、apply)
    • 特殊绑定词
      • bind:只是绑定this,但没有执行函数,所以this不确定
        • 两种情况:一是在使用new执行时,this是new出来的对象,其它情况则是bing绑定的对象
      • super:this是实例本身

未使用绑定字

以下为宽松模式下

  1. 以下fn均表示一個宽松环境的函数, obj表示一個对象

    fn();	// window
    obj1.fn();	// obj1
    obj2.obj3.fn()	// obj3
    (function f1(){})()
    fn.call(obj4)  // obj4
    new fn()   // 实例
    

    函数fn执行时,是否通过对象的方法来引用

    • fn() : 否 =》 thiswindow
    • obj1.fn() : 是,通过对象obj1的方法来引用 =》 thisobj1
    • obj2.obj3.fn(): 是,通过对象obj2的方法来引用 =》thisobj3
    • 立即执行函数f1:否 =》 thiswindow
      执行函数时未使用绑定词,通过否通过对象的方法来引用以确定this
  2. // 伪代码: fn表示宽松模式的下的函数 
    const A = {
        fn: fn
    };
    const B = {
        b1: A.fn,  
        b2: A    
    };
    var c = objA.fn;
    fn();      // window
    A.fn();    // A
    B.b1()     // B
    B.b2.fn(); // A
    c();       // window
    

    分析表

    调用形式 是否通过对象的方法来引用 对象 this
    fn0() window
    A.fn() A A
    B.b1() B B
    B.b2.fn() b2,b2的值是A A
    c() window
  3. 
    var obj = {
        y: function fn1(x) {
            console.log(this);
            x > 1 && fn1(x - 1);
        },
        z: function fn2(x) {
            console.log(this);
            x > 1 && obj.z(x - 1);
        },
    };
    
    obj.y(2);  //obj 、 window
    obj.z(2);  //obj 、 obj 
    

    分析表

    调用形式 是否通过对象的方法来引用 对象 this
    obj.y(1) obj obj
    fn1(x - 1) window
    obj.z(1) obj obj
    obj.z(x-1) obj obj
  4. 函数没有执行,this就不确定

    let A = {
        fn: function () {
            console.log(this); 
        },
        fn2: (function () {
            console.log(this); 
            return fn;
        })()
    };
    
    let B = {};
    
    function fn3(cb) {
        console.log(this); // window
        B.fn = cb;
        cb();   // window
        B.fn(); // B
    }
    
    A.fn();   
    fn3(A.fn);
    

    分析表

    调用形式 是否通过对象的方法来引用 对象 this
    A.fn() A A
    fn3(A.fn) window
    cb() window
    B.fn() B B
  5. 函数的this,同样只能在其被调用时决定,不在于函数被赋值给谁

    function fn() { console.log(this) };
    Function.prototype.fn = fn
    let obj = {};
    Function.prototype.f1 = function () {
        console.log(this);
        return function f2() {
            console.log(this);
        }
    }
    Function.prototype.fn(); //  Function.prototype
    obj.f = fn.f1(); // fn
    obj.f();  // obj
    fn.f1()(); // fn、 window
    let f2 = fn.f1();
    f2();   // window
    

使用绑定

四个可以绑定this关键词: new、call、apply、bing

  1. callapply

    1. 执行并且指定一个对象作为 this 的值
    2. 可使用apply来展开一个数组
      ES5:可传入类数组对象
  2. 硬绑定bing -- 两种情况

    1. 在使用new执行时,this是new出来的对象
    2. bing绑定的对象
    3. 原理
      function fakeBind(fn, obj) {
          return function () {
              return fn.apply(obj, arguments);
          };
      }
      
      1. 返回一个新函数
      2. 新函数与原函数的函数体相同
      3. 但其this被绑定到指定的对象

绑定到空/原始值

  1. 空对象: Object.create(null),这是一个对象
  2. 绑定到null / undefined
    • 宽松模式:window
    • 严格模式:null、undefined
  3. 绑定到原始值(字符串类型、布尔类型或者数字类型)会封装成对应的对象
    let o = {
        a: function () {
            console.log(this);
        }
    };
    
    o.a();		// o
    o.a.call(null);  // window
    o.a.call(1);	// Number(1)
    

其他

事件处理

  1. 标签事件:

    1. 事件触发函数执行,该函数的调用标识符决定this
    2. 内联事件中的this是该事件的元素本身
    <button id="btn1" onclick="fn(this)">AMTB</button>
    <!-- window、 btn1-AMTF -->
    <button id="btn2" onclick="obj.fn(this)">JLSJ</button>
    <!--  obj、 btn2-JLSJ -->
    
    function fn(v) {
        console.log(this, v);  
    }
    let obj = { fn };  
    document.getElementById("btn1").onclick = fn  //  btn1
    btn.addEventListener('click', fn); //  btn1
    
  2. IE的attachEvent的回调函数的this是window

    1. 修改this:把函数赋值给对象,再执行这个对象的方法
    function attachEvent (elm, event, cb) {
        elm.attachEvent(("on" + event), function () {
            elm.cb = cb;
            elm.cb(window.event);
            delete elm.cb;
        });
    }
     
    
  3. 事件监听addEventListener、onclick之类:其this所属对象是元素本身

  4. 内联样式的中的this:元素本身 - <button onclick="fn(this)" >AMTB</button>

自带绑定

  1. 如在浏览器方法中的定时器, 回调函数thiswindow对象
    function fo() {
        'use strict'
        console.log(this);
    }
    var obj = { fo: fo  }
    obj.fo(); // obj
    setTimeout(obj.fo, 100); // window
    setTimeout(fo, 100); // window
  1. 其他
    1. getter 与 setter函数同理
    2. 实例中继承的方法同理
    3. class语法的super调用的方法中的this
      1. super是静态的,指向当前类的基类
      2. 但是super调用方法时,进行了绑定
        this绑定到当前实例
  2. 数组的forEach、map等等可以指定this

箭头函数

  1. 箭头函数没有this值,也无法使用绑定关键词修改this
  2. 通过引用其上属函数的this
    • 若该箭头函数没有上属函数,则其this值为undefined
    • 若上属函数是箭头函数,
      • 该上属函数会引用它的上属函数
      • 该上属函数所引用的this作为箭头函数的this
  3. 唯一修改this值:给他套上普通函数,再修改该普通函数的this
    function x() {
        return (a) => {
            //this 取自 x函数执行时的this
            console.log(this.a);
        };
    }
    var oA = { a: 2 };
    var oB = { a: 3 };
    var y = x.call(oA);
    y.call(oB); // 2
    
    
    由于xthis 绑定到 oA
    箭头函数的this指向 oA: y的this 也指向 oA

例子

  1. 在一个函数中返回另一个函数
    function f1() {
        console.log(this);
        return function f2() {
            console.log(this);
        }
    }
    f1()();
函数f1返回的函数的this值,由调用时决定
`f1( )( )·`:两次`this`都是指向了`window`
f2执行时,如立即执行函数
  1. 反柯里化:

    1. 普通写法
      Function.prototype.uncurrying = function () {
          var self = this;
          return function () {
              let thisArg = [].shift.call(arguments);
              return self.apply(thisArg, arguments);
          }
      }
      
    2. 箭頭函数写法
      Function.prototype.uncurrying = function uncurrying() {
        return (...args) => this.call( ...args)
      }
      
      1. 箭头函数的this是取值其外层函数的this,
      2. 外层函数uncurrying的this值,决定于调用时的
        let push = Array.prototype.push.uncurrying();
        
        • 此时uncurrying的调用者是push函数
          则此时的this是指向push函数
  2. bing的实现

    1. 工具函数

      objectCreate = Object.create || function (proto) {
          var F = function () { };
          F.prototype = proto;
              return new F();
          }
      let slice = Array.prototype.slice.uncurrying();
      let concat = Array.prototype.concat.uncurrying();
      
    2. 实现逻辑

      Function.prototype.bind = function (thisArg) {
          if (typeof this !== "function") {
              throw new TypeError("被绑定的不是函数");
          }
      
          var self = this;
          var baseArgs = slice(arguments, 1); 
      
          var bind = function () {
              return self.apply(
                  this instanceof self ? this : thisArg,
                  concat(baseArgs, slice(arguments))
              );
          }
          bind.prototype = objectCreate(self.prototype);
          return bing;
      };
      
  3. 自定义绑定对象
    例:修改例3的bind函数的返回值的apply的参数1
    默认绑定:只当this指向全局对象或者undefined时,
        把this绑定到默认的对象

    return self.apply(  
        ((!this || this === (window || global)) ?
            thisArg : 
            this
        ),
        concat(baseArg, slice(arguments))
    ); 
  1. 多重
  const obj1 = {
    name: "obj1"
  };
  const obj2 = {
    name: "obj2"
  };

  function f1() {
    console.log(this);
  }
  function f2() {
    f1.call(this);
  }
  const f3 = () => f2.call(this);

  f2.call(obj1); // obj1
  f3.call(obj2); // window

f2 .call(obj1 )

f2的this被给到了obj1,所以f1.call(this)的this是obj1;使得f1的this obj1

f3.call(obj2)

f3是箭头函数-- 其沒有所属的函数,所以f2.call(this)的this值 undefined

  1. 立即执行函数
  • 未使用绑定时,永远是window/未定义

  • 执行时使用绑定,是被绑定的对象

  • 箭头函数,来自所属函数的

    let o = {
      a: (function () {
        console.log(this);  // window
        return function () {
          console.log(this); // 调用标识符决定
        }
      })()
    };
    
    (function (fn) {
      console.log(this); // o
      fn.call(this);    // window
      (() => console.log(this))(); // o
    }).call(o, () => console.log(this));
    
    
posted @ 2020-09-14 08:49  妙9999  阅读(240)  评论(0)    收藏  举报