javascript中那些奇怪的特性

程序员百科

发布时间:19-03-0809:04

JavaScript 通常被认为是最容易掌握的语言,也是最难掌握的语言。 我完全赞同。 这是因为 JavaScript 是一种非常古老且非常灵活的语言。 它充满了神秘的语法和过时的功能。 我已经使用 JavaScript 多年了,迄今为止,我仍时不时地发现一些我从未知道的隐藏语法或技巧。

比如下图,让很多前端程序员感到很无奈。

奇葩的javascript

我曾尝试过列出一些鲜为人知的 JavaScript 特性, 尽管这些功能在严格模式下是无法使用的,但它们仍然是完美的JavaScript 代码。请牢记,我并不建议使用全部的特性,这会让你的同事变得暴躁尽管这些特性看起来很酷。

注意:我这里谈到的并不包括,提升,闭包,代理,原形继承,异步,生成器等。虽然以上这些特性不易理解,但是却非常实用。

Void操作符

JavaScript 有一个一元的void操作符。你可能已经看到它被用作void(0)或void 0。它在生活中有一个目的——计算一个表达式并返回undefined。使用“0”只是一种约定。 你不必使用“0”, 它可以是任何有效的表达式void <expression>,它仍然返回undefined。

void 操作符

为什么要创建一个特殊的关键字来返回undefined而不是直接返回undefined?听起来有点多余,不是吗?有趣的事实好吧,事实证明,在ES5之前,你实际上可以在大多数浏览器中为原始未定义的类用undeunfined =“abc”分配新值。因此,定义未定义!在那些日子里,使用void是一种确保的方法,你总是返回原来的undefined。

构造函数括号是可选的

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

下面的代码样式都被认为是有效的JS语法,并且会给你完全相同的结果!

构造函数括号是可选的

可以跳过立即调用的函数表达式括号

对于我来说,立即调用的函数表达式的语法总是有点奇怪.

所有括号都有什么用?

事实证明,这些额外的括号仅仅是为了告诉JavaScript解析器,即将到来的代码是一个函数表达式,而不是一个函数。可以想象,知道了这一点,有很多方法可以跳过这些额外的括号,仍然可以生成一个有效的立即调用的函数表达式。

void操作符告诉解析器代码是函数表达式,因此,我们可以跳过函数定义的括号。

你猜怎么着?我们可以使用任何一元运算符(void, +, !, -, etc.),这仍然有效!

这太酷了!

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

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

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

确实如此!

IIFE 的返回

我们添加这些括号只是为了更好的可读性。

如果想深入了解 IIFE,请查看Chandra Gundamaraju 的这篇很酷的文章。

With语句

你知道JavaScript有with语句块吗?事实上with是JS中的一个关键词。编写with语句块的语法如下

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

with会把传入对象的所有属性添加进来, 并在其作用域内计算语句的值.

with 块例子

Fun FactWith听起来很酷, 不是吗? 甚至好过 object destructuring 语句.其实不然.with 语句一般不鼓励使用, 因为它已被废弃. 在严格模式下,它是被完全禁止使用的. 事实证明, with 带来了性能和安全问题. 真倒霉!

函数构造器

function语句不是唯一的方式来定义新的函数; 你也可以使用Function() 构造器和 new 操作符来动态地定义函数.

使用Function构造器的动态函数

最后一个构造器参数是函数体转换字符串以后的代码,它之前的参数是函数的参数.

Fun FactJavaScript中,Function构造器是所有构造器的父类. 甚至Object的构造器也是 Function 构造器. 并且Function自己的构造器是Function本身. 不过, 在JavaScript中,任何对象调用object.constructor.constructor...多次后,最终会返回一个Function构造器 .

函数属性

我们都知道函数在 JavaScript 中是一等公民对象。因此,我们可以自由地给函数对象添加自定义属性。这在 JS 中绝对没有问题,但是很少有人会这样做。

那么,什么时候我们应该这样做呢?

这里会讲到一些不错的使用场景,比如:

配置函数

假如我们有一个叫 greet的函数,我们希望它能根据不同的地域设置打印出不同的问候语,而位置是可配置的。我们当然可以配置一个全局变量来做这个事情,不过也可以像下面的代码这样,使用函数属性来实现这一功能:

有 locale 属性的 greet 函数

带有静态变量的函数

还有一种类似的用法。如果你想实现一个数字生成器,它会按顺序产生一系列的数。一般情况下,可以使用带有静态变量的IIFE,使用这个静态变量来保存产生的最后一个数。使用这种方法,我们可以限制对计数器的访问,并且可以避免全局污染。

但是如果我们想灵活地读取甚至修改计数器,同时又能保持没有全局污染,应该怎么办呢?

我们仍然可以创建一个类,使用计数器变量并通过特殊的方法来读取它;我们也可以用简单一点的办法,使用函数上的属性。

具有 counter 属性的 generateNumber 函数

哎呀!这个清单很长,我们已经快进行到一半了。如果你想休息一下,现在就是个不错的时机。如果你仍然精神振奋,我向你致敬,斗士。继续!

arguments 的属性

我相信大多数人都知道函数中的

arguments

对象。这是一个数组形式的对象,所有函数中都可用。这个对象包含了函数调用时传入的所有参数列表。但除此之外,它还有一些很有意思的属性,

arguments.callee: 引用当前调用的函数(即自身)arguments.callee.caller: 引用调用当前函数的那个函数(即调用者)

callee & caller

注意:虽然 ES5 在严格(strict)模式下禁止使用calleecaller,但他们仍然普遍存在于很多已经编译的库中。所以,学习他们并非毫无意义。

带标签的模板字符串

如果你不是生活在原始社会,那就一定听说过模板字符串。模板字符串是 ES6 中新增的众多优秀特性之一。不过,话说回来,你知道带标签的模板字符串吗?

模板字符串

带标签的模板字符串能让你更好地控制从模板解析成字符串的过程,它比普通的模板字符串只是多了一个自定义的标签。这个标签代表着一个解析函数,它会得到从模板字符串解析出来的字符串和值列表,通过逻辑处理返回最终的生成的字符串。

下面的示例中,我们定义一个标签 —— highlight,它会将模板字符串解析出来的值处理成 <mark> 元素,使之高亮。

带标签的模板字符串 - highlight

已经存在不少库在使用这个有趣的特性,比如下面这些,React 的 styled-components用于翻译和国际化的 es2015-i18n-tag用于为输出上色的chalk

Getters & Setters

在大多数情况下, javascript 对象很简单。假设我们有一个

user

对象, 我们试图访问其上的age属性,可使用

user.age

我,如果其已被定义,我们将得到age属性的值, 否则,我们将得到undefined。简单吧。

但是, 不一定非要这么简单。javascript对象有GettersSetters的概念。我们可以编写自定义Getter函数来返回所需的任意内容, 而不是直接返回对象上的值。设置值也可同样处理。

这使我们在获取或设置字段时能够拥有强大的概念, 如虚拟字段字段校验副作用

ES5 Getters & Setters

Getters & Setters并不是ES5新加的;他们一直都在那里。ES5 只是向现有功能添加了便捷的语法。要了解更多关于Getters & Setters的信息, 请参阅这篇文章

Colors,一个流行的node.js库,是使用Getter的很棒的例子。该库e扩展自String类,并在其上添加了大量Getter方法。这允许我们将任何字符串转换为其彩色化版本, 以便于日志记录, 仅需简单访问它对应的属性即可。

逗号运算符

javascript 支持逗号运算符。它允许我们在一行中编写用逗号分隔的多个表达式, 并返回最后一个表达式的结果

// 语法let result = expression1, expression2,... expressionN

在这里, 将对所有表达式进行计算, 并为结果变量赋值为expressionN的返回值。

你可能已经在 for 循环中使用过逗号运算符了

for (var a = 0, b = 10; a <= 10; a++, b--)

有时在一行中编写多个语句,它是很有所帮助

functiongetNextValue() { return counter++, console.log(counter), counter}

或编写简短的lamda函数

const getSquare = x => (console.log (x), x * x)

+ 加号运算符

你曾经需要将字符串快速转换为数字嘛?

只要在字符串前面添加+运算符前缀即可。加号运算符也适用于负号、八进制、十六进制、指数值。另外,它甚至可以将日期或Moment.js对象转换为时间戳!

加号运算符

!! 砰砰运算符

好吧, 从技术上讲, 这不是一个独立的javascript 运算符。这只是使用了两次的javascript否定运算符。

砰砰听起来太酷了!砰砰或双砰是将任意表达式转换为布尔值的巧妙技巧。

如果表达式是真值, 它返回true;。否则它返回false。

砰砰运算符

~ 波浪运算符

让我们面对现实吧--没有人关心位运算符。我们什么时候才能使用它呢!

好吧, 有一个波浪位否定运算符的日常用例。

原来, 当与数字一起使用时, 波浪运算符有效地执行了

~N => -(N+1)

。只有当

N == -1

时, 此表达式的计算结果为 "0"

我们可以通过将~放在

indexOf(...)

函数前面来执行布尔检查, 是否某个项存在于一个字符串或数组之中。

使用波浪运算符的indexOf函数

注: ES6 & ES7分别在String及Array中添见了新的.includes()方法。当然, 它是一种比波浪运算符更简洁的方式来检查某项是否存在于数组或字符串中。

带标签的语句

javascript中有

label

语句的概念。它允许我们在 javascript 中命名循环和块。然后, 我们可以使用这些标签在后续使用

break

continue

时引用此代码。

在嵌套循环中, 带标记的语句特别方便。但我们也可以使用它们简单地将代码组织到块中或创建一个可中断的块

带标签的语句

注:不同于其他语言,JavaScript并不支持goto建构。因此,我们只能在标签中使用break和continue。

返回顶部