LeetCode / JavaScript No.04 计数器 II

🗿🗿🗿本系列文档在 LeetCode 30 天 JavaScript 挑战 完成后可看到题解,本人只转载方便自看

LeetCode 计数器 II

JavaScript 对象

在本质上,「对象」 只是从字符串到其他值的映射。这些值可以是任何类型:字符串、函数、其他对象等。将映射到值的字符串称为 「键」 。

const object = {
  "num": 1,
  "str": "Hello World",
  "obj": {
    "x": 5
  }
};

有三种访问对象值的方式:

  1. 点符号表示法
const val = object.obj.x;
console.log(val); // 5
  1. 方括号表示法
    当键不是有效的变量名时使用。例如 ".123"
const val = object["obj"]["x"];
console.log(val); // 5
  1. 解构语法
    在一次访问多个值时非常有用。
const { num, str } = object;
console.log(num, str); // 1 "Hello World"

有关对象的更多信息,点击 这里 进行了解。

类和原型

可以在 JavaScript 中定义 。类的构造函数返回一个对象,这个对象是该类的实例。

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log("My name is", this.name);
  }
}

const alice = new Person("Alice", 25);
alice.greet(); // 输出:"My name is Alice"

JavaScript 使用特殊对象 prototypes 来实现类。所有方法(在这个例子中是 greet)都是存储在对象的原型上的函数。

为了更具体的说明,上面的操作可以使用以下代码实现:

const alice = {
  name: "Alice",
  age: 25,
  __proto__: {
    greet: function() {
      console.log("My name is", this.name);
    }
  }
};
alice.greet(); // 输出:"My name is Alice"
  1. 为什么这里可以访问 greet 方法,即使它不是 alice 对象上的键?

因为访问对象上的键实际上比仅查看对象的键稍微复杂一点。实际上有一个算法会遍历 「原型链」。首先,·JavaScript· 查看对象上的键。如果请求的键没有找到,它会查找原型对象的键。如果仍然没有找到,会查看原型的原型,以此类推。这就是 JavaScript 中 「继承」 的实现方式。

  1. 为什么 JavaScript 会有这种奇怪的原型概念。为什么不直接将函数存储在对象本身上?

因为效率。每次创建新的 Person 对象时,都会向对象添加 agename 字段。然而,只会添加一个对原型对象的 「引用」。因此,无论创建多少个 Person 实例还是类上有多少方法,都只会生成一个原型对象。

(续)

继承和代码复用: 原型链允许对象通过原型继承属性和方法。这意味着在JavaScript中,可以创建一个对象,并将其原型指向另一个对象,从而实现属性和方法的共享。这种机制使得代码更具有可复用性,可以通过原型链来定义通用的功能,然后让其他对象共享这些功能。

动态性: JavaScript是一种动态语言,允许在运行时修改对象的结构。使用原型链,可以动态地为对象添加新的属性和方法,而不需要重新定义对象本身。

内存效率: 将方法存储在原型上,而不是每个实例对象上,可以节省内存空间。如果每个实例对象都有自己的方法,那么每个对象都需要额外的内存来存储这些方法,而使用原型链,方法只需要在原型对象上定义一次,然后所有实例对象都可以共享。

这里 阅读更多关于类的内容。

代理

JavaScript 中不经常使用但非常强大的功能之一是 「代理」。它们允许覆盖对象的默认行为。
例如,使用代理来实现 alice 对象:

const alice = new Proxy({ name: "Alice", age: 25 }, {
  get: (target, key) => {
    if (key === 'greet') {
      return () => console.log("My name is", target.name);
    } else {
      return target[key];
    }
  },
});
alice.greet(); // 输出:"My name is Alice"

以下是一些代理的潜在实用案例示例:

  • 执行验证以确保不会将不良数据输入表单。
const validator = {
  set: (obj, prop, value) => {
    if (prop === "age") {
      if (typeof value !== "number" || value < 0) {
        throw new TypeError("Age must be a positive number");
      }
    }
    obj[prop] = value;
  },
};

const person = new Proxy({}, validator);
person.age = 25; // 正常工作
person.age = -5; // 抛出异常
  • 创建每次访问键时都记录的日志。这是非常有用的开发者工具。
const object = {
  "num": 1,
  "str": "Hello World",
  "obj": {
    "x": 5
  }
};
const proxiedObject = new Proxy(object, {
  get: (target, key) => {
    console.log("Accessing", key);
    return target[key];
  }
});
proxiedObject.num; // 打印: Accessing num
  • 如果尝试写入只读值,则引发错误。
const READONLY_KEYS = ['name'];

const person = new Proxy({ name: "Alice", age: 25 }, {
  set: (target, key, value) => {
    if (READONLY_KEYS.includes(key)) {
      throw Error("Cannot write to key");
    }
    target[key] = value;
    return true;
  }
});
person.name = "Bob"; // 抛出异常

比如流行的 Immer库 内部使用了 JavaScript 的代理(Proxy)来实现其核心功能。Immer 库利用了代理的能力来拦截对原始数据的操作,并根据这些操作生成不可变的数据版本。这种方式使得可以编写可变的代码,而实际上操作的是不可变的数据。
Immer 的核心概念就是创建一个代理对象,然后通过对代理对象的操作来生成不可变数据版本。这样做使得编写代码更加简洁和直观,而不必担心不可变性的细节。

这里 阅读更多关于代理的内容。

使用代理的闭包解决 计数器 II

与其返回一个普通对象,不如返回一个模拟具有方法的对象行为的代理 Proxy。可以通过监听所有属性访问(get)事件,并且如果请求的键与方法的名称匹配,就执行相应的逻辑来实现这一点。

以下解决方案主要是为了演示。代理是一个非常强大的工具,应该在绝对需要的情况下再使用。

var createCounter = function(init) {
  let currentCount = init;
  return new Proxy({}, {
    get: (target, key) => {
      switch(key) {
        case "increment":
          return () => ++currentCount;
        case "decrement":
          return () => --currentCount;
        case "reset":
          return () => (currentCount = init);
        default:
          throw Error("Unexpected Method")
      }
    },
  });
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值