Skip to content

原型链

实例对象被构造函数构造出来后,其 __proto__ 会指向其构造函数的 prototype,当访问实例对象的属性时,如果有则直接访问,否则向上追溯原型的 __proto__ 中的属性,直到找到或返回 undefined 为止。

对比项__proto__prototype
中文含义原型链链接原型对象
谁拥有所有对象(包括函数、数组等)只有函数才有
存的是什么存的是一个引用,指向构造函数的prototype存的是一个对象,里面放着共享的属性和方法
作用连接实例和原型,形成原型链作为“仓库”,存放所有实例共享的东西

this 绑定

  1. 普通函数调用,this 指向全局
  2. 函数作为对象的方法被调用时,this 指向这个对象
  3. 通过 callapplybind 手动指定 this 指向谁
  4. 用 new 调用函数时,this 指向新创建的空对象

一个例子:

js
// 一个简单的 throttle 函数(故意不加 apply)
function simpleThrottle(fn, context) {
  let timer = null
  return function(...args) {
    if (timer) return
    timer = setTimeout(() => {
      fn.apply(context, args) // 把this.替换成传入的context.
      timer = null
    }, 100)
  }
}

// 用户对象
const user = {
  name: '张三',
  sayHi: function(age) {
    console.log(`我叫${this.name},今年${age}岁`)
  }
}

// 包装一下
const throttledSayHi = simpleThrottle(user.sayHi, user)

// 调用
throttledSayHi(18)
// 输出:我叫undefined,今年18岁  ❌ this.name 是 undefined
// 因为 this 指向了 window/global,不是 user

闭包

一个函数可以“记住”并访问它定义时的作用域,即使这个函数在它定义的作用域之外执行。

当内部函数引用了外部函数的变量时,即使外部函数执行完毕,JavaScript 引擎也不会销毁这个变量,因为内部函数的闭包仍然持有对该变量所在作用域的引用。这就是闭包能够“记住”外部变量的原因,也是实现私有数据的基础。

js
function outer() {
  let count = 0;          // 外部函数的局部变量
  function inner() {      // 内部函数(闭包)
    count++;              // 可以访问 count
    console.log(count);
  }
  return inner;           // 把内部函数返回出去
}

const fn = outer();       // outer 执行完毕,按理 count 应该消失
fn();  // 输出 1          // 但闭包让 count 仍然存在
fn();  // 输出 2

ES2022 的 私有字段(#) 是语言层面提供的真正私有,和闭包模拟的私有不同:

javascript
class Example {
  #privateField = 1;          // 私有属性
  #privateMethod() {          // 私有方法
    console.log('private');
  }
  publicMethod() {
    console.log(this.#privateField);
    this.#privateMethod();
  }
}

ES5 继承

js
function Parent(name) {
  this.name = name;
}
Parent.prototype.sayName = function() {
  console.log(this.name);
};

function Child(name, age) {
  Parent.call(this, name);  // 只调用一次
  this.age = age;
}

// 核心:用 Object.create 创建一个以 Parent.prototype 为原型的对象
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

const c = new Child('小红', 8);
c.sayName(); // 输出: 小红

箭头函数

普通函数:this 看谁调用。
箭头函数:this 看它写在哪。