通过调用标识符确定this
阅读原文时间:2023年07月10日阅读:1

this的性质

  1. 作用:表示函数执行时的环境

  2. 值:一个对象

  3. 特点:动态性

确定this的难度

  • JS语言的动态性:

函数的this在执行时才能确定

  • 函数为一级公民

可作实参、返回值、数据赋值进行传递

解决方法

  • 书写函数时,要预想该函数会被如何调用
  • 调用标识符确定this值
  • 以变量代替this、使用绑定

函数的调用标识符

1. 函数是复杂数据类型

2. 变量可获取函数值--引用地址

3. 函数调用时,该调用名字是该函数此次调用时的标识符

以标识符确定this值

唯一能确定this的途径:函数的调用标识符

  1. 未使用绑定字: 函数的调用标识符是否属于一个对象的成员

是:this就指向该对象

否:宽松模式是window;严格模式是未定义

  1. 使用绑定字 (执行时) : 把this绑定到指定对象上

开启严格模式

  • 函数体内使用严格关键词

  • class方法与模块方法自动运行在严格模式中

  • 不包括方法简写

// 伪代码
//以下fn均表示一個宽松环境的函数, obj表示一个对象
    fn();   // window
    obj1.fn();  // obj1
    obj2.obj3.fn()  // obj3
    fn.call(obj4)  // obj4
    new fn()   // 实例

函数fn执行时,其调用标识符均是fn

每个标识符fn所属的对象都不同,其this就不同

  • fn() : 调用标识符fn不属于任何对象:thiswindow

  • obj1.fn() : 调用标识符fn属於对象obj1的成员

    thisobj1

  • obj2.obj3.fn(): 调用标识符fn属於对象obj2的成员

    thisobj3

  • 未使用绑定时,函数的调用标识符属于对象的一个成员时,其this就是该对象

确定 this 值 :

只要查看该函数调用时的标识符

该标识符决定当前函数执行时的this值

1 函数自我调用

  1. 调用标识符是该函数本身的标识符

  2. 该标识符不属于任何对象的属性

  3. 此时this指向

    宽松模式:全局对象window、global(node环境)·

    严格模式: this为未定义  

  4. 立即执行函数也是函数自我调用,

    其调用标识符是anonymou

    其this,永远是window/未定义

2 一个函数的多种调用形式

  1. 函数的引用地址被赋值给一个对象的属性

  2. 函数调用时: 若调用标识符属于对象的一个成员

      this就指向该对象

  3. 例:

    ​ 1.函数作为数据进行赋值

    // 伪代码: 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值

​ 分析表

调用形式

调用标识符

标识符所属对象

this

fn0()

fn0

window

A.fn()

fn()

A

A

B.b1()

b1()

B

B

B.b2.fn()

fn()

b2,b2的值是A

A

c()

c()

window

​ 2. 函数作为数据进行赋值

    var obj = {
      y: function fn1(x) {
        console.log(this);
        while (x) {
          fn1(x - 1);
          break;
        }
      },
      z: function fn2(x) {
        console.log(this);
        while (x) {
          obj.z(x - 1);
          break;
        }
      },
    };


obj.y(1); //obj 、 window"
obj.z(1); //obj 、window"

调用形式

调用标识符

标识符所属对象

this

obj.y(1)

y()

obj

obj

fn1(x - 1)

fn1()

window

obj.z(1)

z()

obj

obj

obj.z(x-1)

z()

obj

obj

​ 3.函数作为参数传入

​ 1.参数传递是一种隐式赋值

​ 2.传参:类似于: argument = A.fn ;

​ 参数cb获取了函数fn的引用

// 伪代码: fn表示宽松模式的下的函数
let A = {
      fn,
      fn2: (function () {
        console.log(this);
      })()
    },
   B = {};
function b(cb) {
  console.log(this);  // window
  B.fn = cb;
  cb();   // window
  B.fn(); // B
}
A.fn();   //  A
b(fn);

调用形式

调用标识符

标识符所属对象

this

A.fn()

fn()

A

A

b(fn)

b()

window

cb()

cb()

window

B.fn()

fn()

B

B

​ 3.参数所属对象要看情况

​ 1.如在浏览器方法中的定时器

​ 其回调函数参数是属于window对象的成员

​ 回调函数thiswindow对象

var obj = {
  fo: function fo() {
    'use strict'
    console.log(this);
  }
};
obj.fo(); // obj
setTimeout(obj.fo, 100); // window

​ 2.如事件监听

​ 其回调参数所属对象是元素本身

  1. 其他

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

调用形式

调用标识符

标识符所属对象

this

Fn.proto.x()

x()

Fn.proto

Fn.proto

fn.f1()

f1()

fn

fn

f2的this,同样只能在其被调用时决定

不在于它被赋值给谁

调用形式

调用标识符

标识符所属对象

this

obj.f()

f()

obj

obj

fn.f1()()

匿名函数()

window

f2()

f2()

window

3.事件处理

  1. 内联事件:


    function fn(v) {
    console.log(this, v); // window、 button-AMTF
    }
    let obj = { fn }; // obj、 button-JLSJ
    document.getElementsByTagName("button")[0]
    .onclick = function () {
    console.log(this); // button-AMTF
    }

  2. 外联事件与事件监听

    btn.addEventListener('click', fn);
    function fn(e) {
      if (e.target == this) {
        // 只在元素本身被觸發時執行
       // doSomething...
       console.log(e.target, this);
      }
    }
  3. IE的attachEvent的回调函数的this是window

    修改this:把函数赋值给对象,再执行这个对象的方法

    function attachEvent (elm, event, cb) {
    elm.attachEvent(("on" + event), function () {
    elm.cb = cb;
    elm.cb(window.event);
    delete elm.cb;
    });
    }
    // 或者使用绑定
    function attachEvent(elm, event, cb) {
    elm.attachEvent(("on" + event), cb.bing(elm, window.event));
    }

确定this值: 绑定关键字指定对象即为this值

同样只能在函数执行时确定

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

1.callapply

  1. 直接指定一个对象作为 this 的值

  2. 可使用apply来展开一个数组

    ES5:可传入类数组对象

2. 硬绑定bing

  1. 原理

    function fakeBind(fn, obj) {
    return function () {
    return fn.apply(obj, arguments);
    };
    }

  2. 在使用了bing的函数是尚未执行的,

    this是不确定的: 只能在执行时确定

    使用new调用:为new创建的对象

    没使用new调用:为bing绑定的对象

    只会被new改变

3. 构造器的new绑定

一定會用新创建的实例对象替换原來的this

无论该函数是否已被其他绑定字绑定过

4. 自带绑定

JS、库、宿主环境的内置函数,提供一个参数

​ 用于绑定 this值

例:数组的forEach的参数2

5. 绑定到空/原始值

  1. 不错的空对象: Object.create(null);
  2. 绑定到null / undefined
  • 宽松模式:绑定到全局对象
  • 严格模式:null / undefined
  1. 绑定到原始值(字符串类型、布尔类型或者数字类型)会封装成对应的对象

new String、new Boolean、new Number

    let o = {
      a: function () {
        console.log(this);
      }
    };

    o.a();        // o
    o.a.call(null);  // window
    o.a.call(1);    // Number(1)

确定this值 :

箭头函数没有this值

通过引用其上属函数的this值

若该箭头函数没有上属函数,则其this值为未定义

  1. 无法使用绑定关键词修改箭头函数的this值

    因为他没有this值

  2. 唯一修改this值:

    给他套上普通函数,再修改该普通函数的this

    function x() {
    return (a) => {
    //this 取自 x()
    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. 确定箭头函数的this:

    • 没有所属函数时,只能是未定义
    • 有所属函数,取决于所属函数执行时的this

1.闭包函数

在一个函数中返回另一个函数

    function f1() {
      console.log(this);
      return function f2() {
        console.log(this);
      }
    }
    f1()();

 函数f1返回的函数的this值,由调用时决定

   f1( )( )·:两次this都是指向了window

​ f2执行时,如立即执行函数

    const obj = {};
    obj.y = f1();
    obj.y();
    let fn = fn1();
    fn();

​ 把f1的返回值,赋值给一个变量

  f2的this:调用时决定--fn()、obj.y()

  调用标识符所属的对象不相同

​ 其 f2的this也不相同

2. 反柯里化:

反柯里化:让对象去借用一个原本不属于它的方法

​ 普通函數寫法

    Function.prototype.uncurrying = function () {
      var self = this;
      return function () {
        let thisArg = [].shift.call(arguments);
        return self.apply(thisArg, arguments);
      }
    }

​ 箭頭函數寫法

    Function.prototype.uncurrying = function () {
      return (...args) => this.call( ...args)
    }
  1. 箭头函数的this是取值其外层函数的this,

  2. 外层函数uncurrying的this值,决定于调用时的

    let push = Array.prototype.push.uncurrying();

  3. 此时uncurrying的调用者是push函数

则此时的this是指向push函数

3. 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;
    };

4. 自定义默认绑定对象

实现:修改例3的bind函數的返回值的apply的参数1

默认绑定--只当this指向全局对象或者undefined

   把this绑定到默认的对象

        return self.apply(
          ((!this || this === (window || global)) ?
            thisArg : this
          ),
          concat(baseArg, slice(arguments))
        );

​ 对apply参数1的定制,可实现多种绑定

5. 多重綁定

  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是箭頭函數--this綁定無效,但其沒有所屬的函數,所以f2.call(this)的this值為未定義

​ 而在寬鬆模式下的綁定到未定義會改爲绑定到全局对象

6. 立即执行函数

    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));

立即执行函数的this

  • 未使用绑定时,永远是window/未定义
  • 执行时使用绑定,是被绑定的对象
  • 箭头函数,其this如同继承的属性,取自其所属的函数

7.函数式编程相关

在函数中返回一个对象,进行链式调用

    let x = function () {
      let obj = { y, z }
      return obj;        // 等效于 return { y, z };
      function y() { console.log(this); return this }
      function z() { console.log(this); return this }
    };
    x().y().z();
  • 因为返回的是对象

    x() === obj ,而 x().y() === obj.y()

    x().y() === obj ,而x().y().z()=== obj.z()

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器