【博学谷学习记录】超强总结,用心分享 | this/call/apply/bind

【博学谷IT技术支持】

this的指向问题

在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同。

简单例子

  var a = 1
  const demo = {
    a: 2,
    func: function() {
      return this.a
    }
  }
  console.log(demo.func()) // 2
  console.log(this.a) // 1

以上简单例子,demo.func()中this的指向是对象demo, 而this.a的指向是window

全局上下文

在函数内部,this的值取决于函数被调用的方式。例如上面的this.a,声明var a,就是在全局环境下声明了a,在浏览器中,全局对象是window,this.a就是读取了window对象下a的值

在非严格模式下

 this === window // true
 
 function b() {
    return this
  }
 b() === window // true

两个效果是一致的

在严格模式下,我们调用b()函数的时候是直接调用的,并没有通过window来调用,this就会保持为undefined

function b() {
   return this // undefined
 }
 console.log(b() === undefined) // true

类上下文

在类的构造函数中,this 是一个常规对象。类中所有非静态的方法都会被添加到 this 的原型中

class Example {
    constructor() {
      const proto = Object.getPrototypeOf(this)
      console.log(Object.getOwnPropertyNames(proto))
    }
    a(){}
    b(){}
    static c() {}
}
new Example() // ['constructor', 'a', 'b']

派生类

派生类的构造函数没有初始的 this 绑定。在构造函数中调用 [super()]会生成一个 this 绑定,并相当于执行如下代码,Base为基类:

this = new Base()

派生类不能在调用 super() 之前返回,除非其构造函数返回的是一个对象,或者根本没有构造函数。

  class A {}
  class B extends A {}
  class C extends A {
    constructor() {
      return { a: 5}
    }
  }
  class D extends A {
    constructor() {}
  }
  new B()
  new C()
  new D() // ReferenceError

例子:

var a = { b: 1}
var b = 2
function whatsThis() {
    return this.b
}

这里有个有趣的现象,this.b输出的值取决于函数的调用方式,在我们正常调用的情况下,得到的值就是2,取到的是window下的a的属性,但是通过call或者apply绑定,我们获得得就是1了

whatsThis.call(a) // 1
whatsThis.apply(a) // 1

在非严格模式下使用 call 和 apply 时,如果用作 this 的值不是对象,则会被尝试转换为对象。null 和 undefined 被转换为全局对象。

如下

  function getType() {
    console.log(Object.prototype.toString.call(this));
  }

  getType.call(1);     // [object Number]
  getType.call('1'); // [object String]
  getType.call(undefined); // [object global]
  getType.call(true); // [object Boolean]

this和对象的转换

 function add(c, d) {
    console.log(this.a + this.b + c + d)
  }

  var o = {a: 1, b: 3};

  // 第一个参数是用作“this”的对象
  // 其余参数用作函数的参数
  add.call(o, 5, 7); // 16

  // 第一个参数是用作“this”的对象
  // 第二个参数是一个数组,数组中的两个成员用作函数参数
  add.apply(o, [10, 20]); // 34

bind

调用bind(someObject)会创建一个与函数具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。

  function b() {
    return this.a
  }
  let a1 = b.bind({a: 1})
  console.log(a1()) // 1
  let a2 = a1.bind({a: 2})
  console.log(a2()) // 2
  let e = {a:3, b:b, a1:a1, a2:a2}
  console.log(e.a, e.b(), e.a1(), e.a2()) // 3 3 1 1

箭头函数

在[箭头函数]中,this与封闭词法环境的this保持一致。在全局代码中,它将被设置为全局对象

 
 var globalThis = this
  var foo = (() => this)
  console.log(foo() === globalThis) // true

  var obj = { foo: foo}
  console.log(obj.foo() === globalThis) // true

  console.log(foo.call(obj) === globalThis) // true

  foo = foo.bind(obj)
  console.log(foo() === globalThis)// true

作为对象的方法

当函数作为对象里的方法被调用时,this 被设置为调用该函数的对象。

  var obj = {
    data: 1,
    f: function() {
      return this.data
    }
  }
  console.log(obj.f()) // 1

换一种方式

  var obj = { data: 1 }
  function independent() {
    return this.data
  }
  obj.f = independent;
  console.log(obj.f()) // 1

这里我们输出的还是1,原因是,f函数本身是没有data的,但是f绑定的是obj, this指向的是obj,所有拿到的data为1

this 的绑定只受最接近的成员引用的影响

  obj.b = {g: independent, data: 2};
  console.log(obj.b.g()); // 2

原型链中的 this

对于在对象原型链上某处定义的方法,同样的概念也适用。如果该方法存在于一个对象的原型链上,那么 this 指向的是调用这个方法的对象,就像该方法就在这个对象上一样。

 var obj = {
    f: function() {
      return this.a + this.b;
    }
  };
  var p = Object.create(obj);
  p.a = 1;
  p.b = 4;

  console.log(p.f()); // 5

函数p本身是没有f函数的,f函数继承自它的原型。在p.f的引用中,函数的this指向了p.

getter 与 setter 中的 this

再次,相同的概念也适用于当函数在一个 getter 或者 setter 中被调用。用作 getter 或 setter 的函数都会把 this 绑定到设置或获取属性的对象。

  function sum () {
    return this.a + this.b + this.c
  }

  var obj = {
    a: 1,
    b: 2,
    c: 3,
    get average() {
      return (this.a + this.b + this.c)/3
    }

  }
  Object.defineProperty(obj, 'sum', {
    get: sum,
    enumerable: true,
    configurable: true
  })
  console.log(obj.average, obj.sum)

构造函数

当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。

  function obj(){
    this.a = 37;
  }

  var data1 = new obj();
  console.log(data1.a); // 37


  function obj2(){
    this.a = 37;
    return {a:38};
  }

  data1 = new obj2();
  console.log(data1.a); // 38
posted @ 2023-02-20 20:58  牛牛牛牛牛牛牛牛  阅读(11)  评论(0编辑  收藏  举报