this

this 是 JavaScript 执行上下文的一个属性,它的指向在代码运行时决定。

指向

  1. 函数调用,指向 window 对象
  2. 方法调用,指向调用该方法的对象
  3. 强绑定,callapplybind
  4. new 构造调用,指向新创建的对象

new

  • new

    1. 创建新对象
    2. 新对象__proto__指向构造函数的 prototype
    3. 执行构造函数,this 绑定到新对象
    4. 构造函数返回值 == 对象 ? 返回构造函数 return 值 : 返回新对象
  • 原理(手写)

function myNew(fn, ...args) {
  let obj = Object.create(fn.prototype);
  let res = fn.call(obj, ...args);

  if (res && (typeof res == "object" || typeof res == "function")) {
    return res;
  }

  return obj;
}

关键是理解Object.create的作用,它又可以通过new实现。Polyfill🕵️‍♂️

if (typeof Object.create !== "function") {
  Object.create = function (proto) {
    if (typeof proto !== "object" && typeof proto !== "function") {
      throw new TypeError("Object prototype may only be an Object: " + proto);
    }

    function F() {}
    F.prototype = proto;

    return new F();
  };
}

箭头函数

箭头函数没有自己的this,箭头函数的 this 将绑定父级作用域上下文。

使用 setTimeout 来模拟网络请求,请求到数据后如何可以存放到 data 中呢?

var obj = {
  data: [],
  getData: function () {
    var _this = this;
    setTimeout(function () {
      // 模拟获取到的数据
      var res = ["abc", "cba", "nba"];
      _this.data.push.apply(_this.data, res);
    }, 1000);
  },
};

obj.getData();
let obj = {
  data: [],
  getData() {
    setTimeout(() => {
      // 模拟获取到的数据
      const res = ["abc", "cba", "nba"];
      this.data.push(...res);
    }, 1000);
  },
};

obj.getData();

箭头函数的出现取代了 ES5 之前使用的作用域机制。

测试

函数调用

function foo(func) {
  func();
}

var obj = {
  name: "13pro",
  bar: function () {
    console.log(this);
  },
};

foo(obj.bar);
answer

window 对象

方法调用

var name = "window";
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  },
};

function sayName() {
  person.sayName(); //   (person.sayName)();
  (b = person.sayName)(); // var b = person.sayName;  b();
}
answer
function sayName() {
  // 关联
  person.sayName(); // person
  // 独立函数调用,没有和任何对象关联
  (b = person.sayName)(); // window
}

构造调用

var name = "window";
function Person(name) {
  this.name = name;
  this.foo1 = function () {
    console.log(this.name);
  };
  this.foo2 = () => console.log(this.name);
  this.foo3 = function () {
    return function () {
      console.log(this.name);
    };
  };
  this.foo4 = function () {
    return () => {
      console.log(this.name);
    };
  };
}
var person1 = new Person("person1");
var person2 = new Person("person2");

person1.foo1();
person1.foo1.call(person2);

person1.foo2();
person1.foo2.call(person2);

person1.foo3()();
person1.foo3.call(person2)();
person1.foo3().call(person2);

person1.foo4()();
person1.foo4.call(person2)();
person1.foo4().call(person2);
var name = "window";
function Person(name) {
  this.name = name;
  this.obj = {
    name: "obj",
    foo1: function () {
      return function () {
        console.log(this.name);
      };
    },
    foo2: function () {
      return () => {
        console.log(this.name);
      };
    },
  };
}
var person1 = new Person("person1");
var person2 = new Person("person2");

person1.obj.foo1()();
person1.obj.foo1.call(person2)();
person1.obj.foo1().call(person2);

person1.obj.foo2()();
person1.obj.foo2.call(person2)();
person1.obj.foo2().call(person2);

小结

  • this 值在运行时确定,与代码定义位置无关
  • 牢记 this 绑定的 4 种规则
  • 箭头函数 this 值绑定父级作用域,无法被改变