3 min read

你不知道的JavaScript:this与对象原型

Table of Contents

简介

《this & Object Prototypes》深入讲解 JavaScript 最让人困惑的 this 关键字和原型系统。


this 的四种绑定规则

1. 默认绑定

function foo() {
  console.log(this.a);
}

var a = 2;
foo(); // 2 - this 指向全局对象 (非严格模式)
// 严格模式下 this 是 undefined

2. 隐式绑定

function foo() {
  console.log(this.a);
}

const obj = {
  a: 2,
  foo: foo
};

obj.foo(); // 2 - this 指向 obj

隐式丢失:

const obj = {
  a: 2,
  foo: foo
};

const bar = obj.foo; // 引用传递
var a = "global";
bar(); // "global" - 丢失 obj 绑定

3. 显式绑定

function foo() {
  console.log(this.a);
}

const obj = { a: 2 };

// call - 立即调用
foo.call(obj); // 2

// apply - 立即调用(参数是数组)
foo.apply(obj, [1, 2]);

// bind - 返回新函数(永久绑定)
const boundFoo = foo.bind(obj);
boundFoo(); // 2

4. new 绑定

function Foo(a) {
  this.a = a;
}

const foo = new Foo(2);
console.log(foo.a); // 2 - this 指向新创建的对象

绑定优先级

new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定
function foo(something) {
  this.a = something;
}

const obj1 = {};
const obj2 = {};

foo.call(obj1, "obj1");
foo.call(obj2, "obj2");

console.log(obj1.a); // "obj1"
console.log(obj2.a); // "obj2"

// bind 优先级高于 call
const boundFoo = foo.bind(obj1);
boundFoo("bound");
console.log(obj1.a); // "bound"

箭头函数的 this

箭头函数不绑定自己的 this,继承外层作用域的 this:

function foo() {
  // 返回箭头函数
  return (a) => {
    console.log(this.a);
  };
}

const obj1 = { a: "obj1" };
const obj2 = { a: "obj2" };

const bar = foo.call(obj1);
bar.call(obj2); // "obj1" - this 永远指向 obj1

原型链

什么是原型?

每个 JavaScript 对象都有一个原型对象(prototype),对象可以从原型继承属性。

const obj = { a: 1 };
console.log(obj.__proto__ === Object.prototype); // true

原型链查找

const grandparent = { a: 1 };
const parent = { b: 2 };
const child = { c: 3 };

// 设置原型
Object.setPrototypeOf(child, parent);
Object.setPrototypeOf(parent, grandparent);

console.log(child.a); // 1 - 沿着原型链查找
console.log(child.b); // 2
console.log(child.c); // 3

原型方法

const obj = { a: 1 };

// hasOwnProperty - 检查自身属性
obj.hasOwnProperty("a"); // true
obj.hasOwnProperty("toString"); // false

// in 操作符 - 检查自身和原型链
"a" in obj; // true
"toString" in obj; // true

对象创建

1. 对象字面量

const obj = { a: 1 };

2. 构造函数

function Foo(name) {
  this.name = name;
}

Foo.prototype.sayHi = function() {
  console.log("Hi, " + this.name);
};

const foo = new Foo("Lily");
foo.sayHi(); // "Hi, Lily"

3. Object.create()

const parent = { a: 1 };
const child = Object.create(parent);
console.log(child.a); // 1 - 继承 parent

4. ES6 class

class Foo {
  constructor(name) {
    this.name = name;
  }
  
  sayHi() {
    console.log("Hi, " + this.name);
  }
}

const foo = new Foo("Lily");
foo.sayHi();

小结

  • ✅ 理解 this 的四种绑定规则
  • ✅ 掌握 call/apply/bind 的用法
  • ✅ 理解原型链的工作机制
  • ✅ 熟悉 ES6 class 的语法糖本质
  • ✅ 能够实现继承和原型链

相关阅读: