淘先锋技术网

首页 1 2 3 4 5 6 7

TypeScript 中的箭头函数与 JavaScript 中的箭头函数之间的区别

当涉及到 TypeScript 中的箭头函数与 JavaScript 中的箭头函数之间的区别时,有几个关键的差异需要注意:

  1. 类型注解:TypeScript 允许为箭头函数提供参数和返回类型的类型注解,这使得函数的输入和输出类型可以明确地指定。这对于代码的可读性和类型检查非常有用。例如:
    这里的类型是(x: number, y: number) => number,表示函数接受两个number类型的变量,并返回一个number类型的值
// TypeScript
const add: (x: number, y: number) => number = (x, y) => x + y;
// JavaScript
const add = (x, y) => x + y;
  1. 上下文类型推断:在 TypeScript 中,当使用箭头函数作为回调函数或赋值给变量时,编译器可以根据上下文中的类型推断函数的参数类型和返回类型。这意味着在某些情况下,您不需要显式指定箭头函数的类型注解。例如:
// TypeScript
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map((num) => num * 2);
// JavaScript
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map((num) => num * 2);

在上述示例中,TypeScript 可以根据 numbers.map 的类型推断出回调函数的参数类型为 number,并且可以根据 numbers 的元素类型推断出返回类型为 number[]

  1. 箭头函数中的 this:在 JavaScript 中,箭头函数没有自己的 this 值,而是继承自外围作用域的 this 值。这意味着箭头函数中的 this 与定义函数时的上下文无关,并且无法使用 callapplybind 方法来改变它。相比之下,在常规的 JavaScript 函数中,this 的值根据函数如何被调用来确定。在 TypeScript 中,箭头函数的 this 行为与 JavaScript 中保持一致。例如:
// TypeScript
const obj = {
  name: "Alice",
  sayHello: function () {
    setTimeout(() => {
      console.log("Hello, " + this.name);
    }, 1000);
  },
};

obj.sayHello(); // 输出 "Hello, Alice",箭头函数继承了外围作用域的 this 值
// JavaScript
const obj = {
  name: "Alice",
  sayHello: function () {
    setTimeout(function () {
      console.log("Hello, " + this.name);
    }, 1000);
  },
};

obj.sayHello(); // 输出 "Hello, undefined",普通函数中的 this 值受调用方式影响

在上述 TypeScript 示例中,箭头函数内的 this.name 访问了外部函数的 this.name,而普通函数的 this 指向了全局对象(

在浏览器中通常是 window),因此输出为 undefined

总结来说,TypeScript 的箭头函数在语法上与 JavaScript 的箭头函数非常相似,但通过类型注解和上下文类型推断提供了更强大的类型系统支持,并且继承了 JavaScript 中箭头函数中的 this 行为。这些功能使得箭头函数在 TypeScript 中更加灵活和可靠。

来点例子试一试

这里的类型是( connection: Connection ) => void表示该函数接受一个 connection参数,然后没有返回值

export const enableBetterErrorHandlingAndLogging: (
  connection: Connection
) => void = connection => {
  const connectionConsole = useConnectionConsole(connection, { trace: false });
  console.log = connectionConsole('log');
  console.info = connectionConsole('info');
  console.error = connectionConsole('error');
  process.on('uncaughtException', handleError);
  process.on('unhandledRejection', handleError);
};

这里的类型是( documents: TextDocuments<TextDocument> ) => (params: Params) => Promise<Result[]>表示该函数接受一个 documents 参数,然后返回一个函数,返回的函数接受一个 params 参数,返回一个 Promise,解析为 Result[] 数组。

export const autoRenameTag: (
  documents: TextDocuments<TextDocument>
) => (params: Params) => Promise<Result[]> =
  (documents) =>
  async ({ textDocument, tags }) => {
    await new Promise((r) => setTimeout(r, 20));
    const document = documents.get(textDocument.uri);
    if (!document) {
      return NULL_AUTO_RENAME_TAG_RESULT;
    }
    if (textDocument.version !== document.version) {
      return NULL_AUTO_RENAME_TAG_RESULT;
    }
    const text = document.getText();
    const results: Result[] = tags
      .map((tag) => {
        const result = doAutoRenameTag(
          text,
          tag.offset,
          tag.word,
          tag.oldWord,
          document.languageId
        );
        if (!result) {
          return result;
        }
        (result as any).originalOffset = tag.offset;
        (result as any).originalWord = tag.word;
        return result as Result;
      })
      .filter(Boolean) as Result[];
    return results;
  };

技巧

其实总结下ts函数阅读技巧,就是看第一个:=之间的内容,就是ts类型

再来看下更套娃的

如果函数返回函数,返回函数再返回函数,再返回函数再再返回函数,则箭头函数则需要无限套娃定义ts类型,比如下方代码
这里的类型是( connection: Connection, { trace }: { trace?: boolean } ) => (method: 'log' | 'info' | 'error') => (...args: any[]) => void
该函数的参数:
connection:表示与某个连接相关的对象。
{ trace }:可选的配置对象,包含一个布尔类型的属性 trace,用于指示是否输出跟踪信息。
该函数返回一个函数,返回函数的参数为 method,表示要进行的操作类型,可以是 ‘log’、‘info’ 或 ‘error’。返回函数再次返回一个函数,再次返回函数的接受任意数量的参数,并将这些参数作为数组中的元素进行处理,再次返回函数最后没有返回值。

const useConnectionConsole: (
  connection: Connection,
  { trace }: { trace?: boolean }
) => (method: 'log' | 'info' | 'error') => (...args: any[]) => void =
  (connection, { trace = false } = {}) =>
  method =>
  (...args) => {
    if (trace) {
      const stack = new Error().stack || '';
      let file = stack.split('\n')[2];
      file = file.slice(file.indexOf('at') + 'at'.length, -1);
      const match = file.match(/(.*):(\d+):(\d+)$/);
      if (match) {
        const [_, path, line, column] = match;
        connection.console[method]('at ' + path + ':' + line);
      }
    }
    const stringify: (arg: any) => string = arg => {
      if (arg && arg.toString) {
        if (arg.toString() === '[object Promise]') {
          return JSON.stringify(arg);
        }
        if (arg.toString() === '[object Object]') {
          return JSON.stringify(arg);
        }
        return arg;
      }
      return JSON.stringify(arg);
    };
    connection.console[method](args.map(stringify).join(''));
  };