发布于 2026-01-06 3 阅读
0

你需要了解的 4 个 JavaScript 知识点:.call()、.bind()、.apply()、多个 Promise 执行、代理、组合、结论。DEV 全球展示挑战赛,由 Mux 呈现:展示你的项目!

你需要了解的 4 个 JavaScript 知识点

.call()、.bind()、.apply()

多次承诺执行

代理人

作品

结论

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

作为一名 JavaScript 开发人员,我发现了很多有趣的东西,我想和大家分享 4 件我认为如果你想成为一名更优秀的 JavaScript 开发人员应该知道的事情。

.call()、.bind()、.apply()

如果你曾经使用过 React,我确信你见过 ` bind()method` 方法,也许你曾经用过它,但并不真正了解它的含义。或许你在某些 JavaScript 库中也见过类似的方法,但你不明白call(), bind(), apply()它们是如何工作的。

首先你需要理解的是它的this含义。this它指的是内存中当前上下文的对象引用,而它所指向的引用会根据上下文执行阶段的不同而改变。

这些方法允许我们更改引用的位置this

.bind()

const user = {
  name: "Peter",
  lastName: "Parker",
  fullName: function () {
    return `${this.name} ${this.lastName}`;
  },
};
const print = function (greet) {
  console.log(`${greet} ${this.fullName()}`);
};

print("hi");
Enter fullscreen mode Exit fullscreen mode

执行这段代码时会报错:this.fullname() is not a function因为print函数内部this引用了全局对象。如果想在打印函数内部访问用户上下文,可以使用bind类似这样的方法:

const myBindedPrint = print.bind(user);
myBindedPrint("hi");
Enter fullscreen mode Exit fullscreen mode

那么,我们做了什么呢?我们复制了打印函数并将其保存到myBindedPrint变量中。这个bind()方法允许我们创建一个带有特殊功能的副本,我们可以将要this引用的上下文作为参数传递。

。称呼()

.call()执行一个函数,例如,如果我们使用 ` ()and`,它允许我们将指向该函数的引用作为第一个参数传递。this.

const user = {
  name: "Peter",
  lastName: "Parker",
  fullName: function () {
    return `${this.name} ${this.lastName}`;
  },
};

print.call(user, "hello");
Enter fullscreen mode Exit fullscreen mode

执行该操作时,我们得到的结果与使用时相同.bind(),区别在于使用时.call()我们不会创建副本,而是直接执行它。

申请()

call()嗯,两者之间只有一个区别,apply()那就是调用方式。.apply()方法接收的参数是一个数组,像这样。

print.apply(user, ['hello'])
Enter fullscreen mode Exit fullscreen mode

在什么情况下可以使用它?

函数借用

当我们想在不同的对象之间共享函数时,比如将一个函数“借用”给另一个对象,就需要用到这种方法。让我们来看一个例子。

const user = {
  name: "Peter",
  lastName: "Parker",
  getFullName: function () {
    return `${this.name} ${this.lastName}`;
  },
};
const dog = {
  name: "Thoby",
  lastName: "Parker",
};
const result = user.getFullName.apply(dog);
console.log(result);
Enter fullscreen mode Exit fullscreen mode

bind ()并且call ()与每个上下文中“指代”的apply ()位置有关this,这就是为什么它很有用,因为有了这些工具,我们可以决定这意味着什么,这在解决与执行上下文相关的问题时非常强大。

多次承诺执行

我见过一些初级程序员常犯的一个错误。

假设我们有一个方法,它会调用我们的 API 将产品保存到数据库中,这是一个异步操作。现在,假设我们有一个产品数组,我们想要保存它们,然后等待保存操作完成才能继续执行其他操作。我们需要执行多个 Promise。我见过类似的情况。

const saveProduct = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve({ data: {}, success: true }), 3000);
  });

const saveProducts = async (products) => {
  try {
    const response = await products.map(
      async (product) => await saveProduct(product)
    );
    console.log("success");
    return response;
  } catch (err) {
    console.log(err);
  }
};
const products = [{ name: "Pollo" }, { name: "Cerveza" }, { name: "Agua" }];
saveProducts(products).then((response) => console.log("response", response));
Enter fullscreen mode Exit fullscreen mode

看起来似乎合情合理,但这一行代码 =>console.log("success")会立即执行,请检查!

你需要做的,大概是这样的。

const saveProducts = async (products) => {
  try {
    const response = await Promise.all(
      products.map((product) => saveProduct(product))
    );
    console.log("succes");
    return response;
  } catch (err) {
    console.log(err);
  }
};
Enter fullscreen mode Exit fullscreen mode

如果你运行一下,就会发现它正如我们预期的那样工作。我们的 `=>`console.log("success")行只有在所有 Promise 都解析完毕后才会执行。此外,我们的saveProducts方法会返回所有 Promise 的响应。

代理人

ES2015 代理提供了一个 API,用于捕获或拦截对对象执行的任何操作,并修改该对象的行为。JavaScript 代理有很多用途,例如:

  • 拦截
  • 对象虚拟化。
  • 资源管理。
  • 在调试应用程序时进行性能分析并生成日志。
  • 安全和访问控制。

要实现代理,你需要了解一些术语。

  • 目标:您要代理的原始对象。
  • 处理程序:一个定义哪些操作将被拦截以及如何重新定义被拦截操作的对象。

我们来看一个例子。

const person = {
  name: "Peter",
};

const handler = {
  get: function (target, key) {
    return key in target
      ? target[key]
      : `Property ${key} doesn't exist in this object`;
  },
};

const proxy = new Proxy(person, handler);
console.log(proxy.name); // Peter
console.log(proxy.lastName); // Property lastName doesn't exist in this object
Enter fullscreen mode Exit fullscreen mode

使用代理可以做很多事情。我将向您展示一个实用案例。

缓存
const getArticles = (person) => {
  fetch("api-url").then((articles) => {
    // do something with articles
  });
};
Enter fullscreen mode Exit fullscreen mode

这意味着每次需要查阅某个人的文章时,都需要发起新的请求。而另一种方法是,在首次请求时缓存文章,后续请求可以直接从缓存中获取。

const cache = {
  Peter: ["Article 1", "Article 2"],
};

const handler = {
  get: function (target, person) {
    if (target[person]) {
      return target[person];
    } else {
      // fetch here
      fetch("api-url").then((articles) => {
        target[person] = articles;
        return articles;
      });
    }
  },
};

const proxy = new Proxy(cache, handler);
Enter fullscreen mode Exit fullscreen mode

这样,只有当该人员不在缓存对象中时,才会执行 fetch 操作。

使用代理可以做很多事情,例如验证、提供对象的只读视图、私有属性等等。

作品

组合是一个简单却强大的概念。它是一种使用多个函数的简单方法。每个函数接收一个输入,并将其输出传递给下一个函数。

或许你曾经使用过这种构图方式,却并不了解其背后的概念含义。我将为你举一个简单的例子。

假设我们要清理用户输入的内容,我们有一个函数可以删除空格,还有一个函数可以删除特殊字符。

const withoutSpaces = (value) => value.replace(/ /g, "");
const removeSpecialChars = (value) => value.replace(/[^a-zA-Z ]/g, "");
Enter fullscreen mode Exit fullscreen mode

我们可以通过以下方式将这些函数合并成一个函数:

const compose = (f1, f2) => (value) => f2(f1(value));
const emptyInput = compose(withoutSpaces, removeSpecialChars);
console.log(emptyInput("  d'ani   el")); // daniel
Enter fullscreen mode Exit fullscreen mode

我们的 compose 函数会返回一个新函数,该函数接收一个参数并返回最终的字符串。如果你仔细观察f2 (f1 (value)),你会发现我们实际上是将第一个函数的结果传递给了第二个函数,就这么简单。

如果我们想要组合多个函数,我们可以使用 reduceRight 方法。

const withoutSpaces = (value) => value.replace(/ /g, "");
const removeSpecialChars = (value) => value.replace(/[^a-zA-Z ]/g, "");
const toLowerCase = (value) => value.toLowerCase();
const compose = (...fns) => (initialVal) =>
  fns.reduceRight((val, fn) => fn(val), initialVal);

const emptyInput = compose(withoutSpaces, removeSpecialChars, toLowerCase);
console.log(emptyInput("  d'AVID   ")); // david
Enter fullscreen mode Exit fullscreen mode

结论

希望这些概念能让你学到/理解一些新东西,如果你有什么补充,请在评论区留言。

接下来几天,我将撰写一些关于 JavaScript 中其他有趣概念的文章。

文章来源:https://dev.to/dcastro/4-javascript-things-you-need-to-know-37el