14 个你不知道的 JavaScript 技巧

本文翻译自《Little known features of JavaScript》

人们通常认为 JavaScript 是一门很容易上手的语言,但是要做到精通却不简单。是的,这是因为 JavaScript 是一种非常古老且非常灵活的语言。它充满了神秘的语法和过时的功能。到目前为止,我已经使用 JavaScript 多年了,但是还是时不时就会发现一些我不知道的隐藏语法或技巧。

我试图列出一些鲜为人知的JavaScript功能。尽管其中一些功能在严格模式下无效,但它们仍然是完全有效的JavaScript代码。但是请注意,我不建议您开始使用所有这些功能。尽管它们绝对酷,但如果您开始使用它们,很有可能会让你的队友很生气。

此处提供了所有使用的源代码。Happy Coding!

注意:本文并不包括诸如提升(Hoisting),闭包,代理,原型继承,async-await,生成器之类的东西。虽然这些功能可能不被大部分人所了解,但它们仍然是众所周知的。

void 操作符

JavaScript 具有一元void运算符。您可能已经看到它用作void(0)void 0。它的生命中只有一个目标——在表达式的正确位置返回**undefined**。使用“0”只是一个约定。您不一定必须使用“ 0”,它可以是任何有效的表达式void,并且仍返回**undefined。**

为什么创造了一个特殊的关键字来返回 undefined 而不是直接返回 undefined呢?听起来好像有一点冗余对吧?趣闻好吧,事实证明,在ES5之前,您实际上可以在大多数浏览器中为原始的 undefined 分配一个新值,像是 undefined =“ abc”。定义 undefined?!因此在那个时候,使用 void 是确保您始终返回原始 undefined 的一种方法。

构造函数后面的括号是可选的

是的,在调用构造函数时,我们在类名后添加的括号是完全可选的! (前提是您不需要将任何参数传递给构造函数)

以下两种代码样式均被视为有效的 JS 语法,并且结果是相同的!

IIFE 函数的括号可以省略

对于我来说,IIFE(立即调用功能表达式)的语法总是有点奇怪。

那些括号我到底应该怎么用?

事实证明,这些额外的括号仅仅是为了告诉 JavaScript 解析器即将发布的代码是函数表达式,而不是函数。可以想象,知道了这一点,有很多方法可以跳过那些多余的括号,并且仍然可以制作出有效的IIFE。

void运算符告诉解析器代码是函数表达式。因此,我们可以跳过函数定义的括号。你猜怎么着?我们可以使用任何一元运算符(void, ,!,-等)来让它奏效!

太酷了!

但是,如果您是一个敏锐的观察者,您可能会想,

一元运算符不会影响IIFE返回的任何结果吗?

好吧,这会影响结果。但是,好消息是,如果您关心结果并说要将其存储在某个变量中,那么首先就不需要多余的括号。

确实如此!

我们添加这些括号只是为了提高人类可读性。

想要了解有关IIFE结帐的更多信息,可以去看看Chandra Gundamaraju的这篇很酷的文章

with 声明

您知道吗,JavaScript有一个with语句块?实际上它是JS中的关键字。编写with块的语法如下:

with(object)statement// for multiple statements add a blockwith(object){statementstatement...}

with把传入的对象的所有属性添加到当前作用域链中:

趣闻with听起来非常酷,对吧?它甚至比对象解构更好。好吧,不是这样的。通常不建议使用with语句,因为它已被弃用。在严格模式下完全禁止这样做。事实证明,使用块会增加该语言的性能和安全性问题!

译者注:不建议使用with,因为with语句将对象的成员添加到当前作用域,从而无法说出块内的变量实际指的是什么。由于难以调试和读取这些类型的功能,因此许多人认为这是一种不好的做法,可以参考以下信息了解更多:-https://eslint.org/docs/rules/no-withhttps://eslint.org/docs/rules/no-new-func#disallow-function-constructor-no-new-func

Function 构造函数

函数声明不是定义新函数的唯一方法;您可以使用Function()构造函数以及new运算符动态地定义函数。

最后一个构造函数参数是函数的字符串化代码以及之前的其他参数为函数参数。

趣闻Function构造函数是 JavaScript 中所有构造函数的母亲。甚至Object的构造函数都是Function构造函数。Function自己的构造函数也是Function本身。因此,调用object.constructor.constructor ...足够的次数最终将返回JavaScript中任何对象上的Function构造函数。

函数属性

众所周知,函数是JavaScript中的「一等公民」。因此,没有人阻止我们向函数添加自定义属性。这在JS中是完全正确的事情。但是,它很少使用。

那么我们什么时候想要这样做呢?

嗯,有一些很好的用例。例如:

可配置函数

假设我们有一个叫做greet的函数。我们希望我们的函数根据不同的语言环境打印不同的问候消息。此语言环境也应该是可配置的。我们可以在某个地方维护全局语言环境变量,也可以使用如下所示的函数属性来实现函数:

带有静态变量的函数

另一个类似的例子。假设您要实现一个数字生成器——该数字生成器生成一系列有序数字。通常,您将使用带有静态的counter变量的类或IIFE来跟踪上一个值。这样,我们可以限制访问counter,还可以避免因额外的变量污染全局空间。

但是,如果我们想灵活地读取甚至修改计数器而又不污染全局空间怎么办?

是的,我们仍然可以创建一个类,它带有counter变量和一些其他读取counter的方法;或者我们偷个懒,只在函数上使用属性。

这是一个很长的 list,我们刚刚写了一半。如果您想休息一下,现在是个好时机。如果您要继续看下去,好吧,我向您致敬!

让我们继续!

参数属性

我敢肯定你们大多数人都知道函数内的arguments对象。它是一个数组,类似于对象,可以在所有函数中使用。它具有在调用时传递给函数的参数列表。但是它还具有其他一些有趣的特性,

  • arguments.callee:引用当前调用的函数
  • arguments.callee.caller:引用已调用当前函数的函数

注意:尽管ES5禁止在严格模式下使用calleecaller,但在许多已编译的库中仍然很常见。因此,值得学习。

标记模板字符串

除非您与世隔绝,否则您肯定听说过模版字符串。模版字符串是ES6提供的许多不错的功能之一。但是,您知道标记模版字符串吗?

带有标记的模板字符串可以通过向模板字符串添加自定义标记,来更好地控制将模板字符串解析为字符串的过程。Tag只是一个解析器函数,它获取由字符串模板解释的所有字符串和值的数组,并返回最终字符串。

在下面的示例中,我们的自定义标签highlight解释模板字符串的值,并且还将解释后的值使用元素包装在结果字符串中以突出显示。

在许多库都可以发现一些有趣的用例是利用此功能来实现的。以下是一些很酷的例子,

Getters