zh-rocco / fe-notes

:memo: 前端笔记

Home Page:https://zh-rocco.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

【JS】this

zh-rocco opened this issue · comments

commented

this

this 是和 执行上下文环境 息息相关的一个特殊对象。因此,它也可以称为 上下文对象[context object] (激活执行上下文的上下文)。

this 是执行上下文环境的一个属性,而不是某个变量对象的属性。

this 指向

this 的指向。除去不常用的 witheval的情况,具体到实际应用中,this 的指向大致可以分为以下 4 种:

  • 作为对象的方法调用
  • 作为普通函数调用
  • 构造器调用
  • callapplybind 调用

1. 作为对象的方法调用

当函数作为对象的方法被调用时,this 指向该对象

2. 作为普通函数调用

当函数不作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的 this 总是指向全局对象。在浏览器的 JavaScript 里,这个全局对象是 window 对象。

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

3. 构造器调用

当用 new 运算符调用函数时,该函数 总会返回一个对象,通常情况下,构造器里的 this 就指向返回的这个对象。

但用 new 调用构造器时,还要注意一个问题,如果构造器显式地返回了一个 object 类型的对象,那么此次运算结果最终会返回这个对象,而不是我们之前期待的 this

var MyClass = function() {
  this.name = 'sven';
  // 显式地返回一个对象
  return {
    name: 'anne',
  };
};
var obj = new MyClass();
alert(obj.name); // 输出: anne

如果构造器不显式地返回任何数据,或者是返回一个非对象类型的数据,就不会造成上述问题:

var MyClass = function() {
  this.name = 'sven';
  return 'anne'; // 返回 string 类型
};
var obj = new MyClass();
alert(obj.name); // 输出: sven

4. callapplybind 调用

当使用 call 或者 apply 的时候,如果我们传入的第一个参数为 null,函数体内的 this 会指向默认的宿主对象,在浏览器中则是 window;但如果是在严格模式下,函数体内的 this 还是为 null

有时候我们使用 call 或者 apply 的目的不在于指定 this 指向,而是另有用途,比如借用其他对象的方法。那么我们可以传入 null 来代替某个具体的对象:

Math.max.apply( null, [ 1, 2, 5, 3, 4 ] ) // 输出:5

实现一个 bind

简易版

Function.prototype.bind = function(context) {
  var self = this; // 保存原函数
  // 返回一个新的函数
  return function() {
    return self.apply(context, arguments); // 执行新的函数的时候,会把之前传入的 context 当作新函数体内的 this
  };
};

完整版

Function.prototype.bind = function() {
  var self = this, // 保存原函数
    context = [].shift.call(arguments),
    args = [].slice.call(arguments);
  // 返回一个新的函数
  // 需要绑定的 this 上下文
  // 剩余的参数转成数组
  return function() {
    return self.apply(context, [].concat.call(args, [].slice.call(arguments))); // 执行新的函数的时候,会把之前传入的 context 当作新函数体内的 this,并且组合两次分别传入的参数,作为新函数的参数
  };
};

var obj = {
  name: 'sven',
};
var func = function(a, b, c, d) {
  alert(this.name); // 输出: sven
  alert([a, b, c, d]); // 输出: [ 1, 2, 3, 4 ]
}.bind(obj, 1, 2);
func(3, 4);