文中的 我 指原文作者
javaScript 通常被认为是最容易入门的语言,也是最难掌握的语言,我完全同意。这是因为 JavaScript 是一种非常古老且非常灵活的语言,它有着了神秘的语法和过时的特性。我已经使用 JavaScript 很多年了,直到现在,我偶尔会发现一些隐藏的语法或技巧,这些是我以前不知道的。虽然这些特性可能不太为人所知,但它们仍然是众所周知的。
注意:这里没有包括变量的提升、闭包、代理、原型继承、异步等待、生成器等。
void操作符
JavaScript 有一个一元 void 操作符。你可能见过它被用作 void(0) 或 void 0。void的作用便是返回undefined,在它右边的操作数会正常计算,但是无论结果是什么,void都会返回undefined。使用“0”只是一种惯例。不必使用' 0 ',它可以是任何有效的表达式,如void <表达式>,它仍然返回未定义的。
// void operator void 0 // returns undefined void (0) // returns undefined void 'abc' // returns undefined void {} // returns undefined void (1 === 1) // returns undefined void (1 !== 1) // returns undefined void anyfunction() // returns undefined
为什么要创建一个特殊的关键字来返回undefined而不是仅仅返回undefined? 听起来有点多余,不是吗?🚩 有趣的事实
实际上,在ES5之前,在大多数浏览器中,可以为原始的 undefined = "abc"分配一个新值。所以,在那些日子里,使用void是一种确保始终返回 undefine 的原始值的方法。
构造函数括号是可选的
是的,我们在调用构造函数时在类名后添加的括号——完全可选!😮(前提是不需要将任何参数传递给构造函数)
下面的两种代码风格都被认为是有效的 JS 语法而且结果都是一样!
// Constructor with brackets const date = new Date() const month = new Date().getMonth() const myInstance = new MyClass() // Constructor without brackets const date = new Date const month = (new Date).getMonth() const myInstance = new MyClass
可以省略 IIFE(立即执行函数)的括号
IIFE (立即调用的函数表达式)的语法对我来说总是有点奇怪,为什么要有那么多的括号?
实际上,这些额外的括号只是为了告诉JavaScript解析器,即将解析的的代码是一个函数表达式,而不是函数。知道了这一点,你可以想象,有很多方法可以省略那些额外的括号,仍然可以使用 IIFE 有效的工作。
// IIFE (function () { console.log('正常形式的 IIFE 调用') })() // 清爽的 IIEF void function() { console.log('酷酷的 IIFE 调用') }()
void
操作符告诉解析器代码是函数表达式。因此,我们可以跳过函数定义周围的括号。你猜怎么着? 我们可以使用任何一元运算符(void, +, !, -等等)
,这仍然有效!
这是不是比原始的写法简单而且 B 格多了呢?
但是,如果你是一个敏锐的观察者,你可能会想,一元运算符不会影响 IIFE 返回的结果吗?
它会影响结果。但好消息是,如果你只要的返回的结果并将其存储在某个变量中,那么就不需要额外的括号。
with 的声明
JavaScript 有一个with语句块, with 实际上是JS中的关键字。 with 块的语法如下:
with 语句可以方便地用来引用某个特定对象中已有的属性,但是不能用来给对象添加属性。要给对象创建新的属性,必须明确地引用该对象。
🚩 有趣的事实with 块看起来很酷,对吧? 它甚至比对象销毁更好。好吧,其实不是。
通常不鼓励使用 with 语句,因为它已被弃用,在严格模式下完全禁止。事实证明,使用 with 块会增加语言中的一些性能和安全性问题。
函数构造器
函数语句不是定义新函数的唯一方法,可以使用function()构造函数和new 操作符动态定义函数。
🚩 有趣的事实Function 构造函数是 JavaScript 中所有构造函数的母亲。甚至 Object 的构造函数也是 Function 构造函数。而 Function 自己的构造函数也是 Function 本身。 因此,调用 object.constructor.constructor ...足够多次将最终在 JavaScript 中的任何对象上返回Function构造函数。
函数属性
我们都知道函数是JavaScript中的第一类对象。因此,没有人阻止我们向函数添加自定义属性。在 JS 中这样做是有效的,然而,它很少被使用。
那么我们什么时候要这样做?
这里有一些很好的用例。例如,
可配置函数
假设我们有一个函数叫做 greet。我们希望函数根据不同的地区打印不同的问候消息,这个区域设置也应该是可配置的。我们可以在某个地方维护全局 locale 变量,也可以使用如下所示的函数属性实现该函数
function greet () { if (greet.locale === 'ch') { console.log('中国,你好') } else if (greet.locale === 'jp') { console.log('扣你机哇!') } else { console.log('Hello World') } } greet() // Hello World greet.locale = 'ch'; greet() // 中国,你好
具有静态变量的函数
另一个类似的例子。比方说,希望实现一个生成有序数字序列的数字生成器。通常您将使用带有静态计数器变量的 Class 或 IIFE 来跟踪最后一个值。这样我们就限制了对计数器的访问,同时也避免了使用额外的变量污染全局空间。
但是,如果我们希望能够灵活地读取甚至修改计数器,而又不污染全局空间,该怎么办呢?
我们仍然可以创建一个类,有一个计数器变量和一些额外的方法来读取它;或者我们可以偷懒,使用函数自定义的属性。
Arguments 的属性
我相信你们大多数人都知道函数中的arguments对象。它是一个类似数组的对象,可以在所有函数中使用。它具有在调用函数时传递给函数的参数列表。但它也有一些其他有趣的性质:
- arguments.callee: 当前调用的函数
- arguments.callee.caller:调用当前函数的函数
注意:虽然ES5禁止在严格模式下使用callee & caller,但在许多编译后的库中仍然很常见。所以,学习它们是值得的。
标记模板字符串
模板字符串文字是ES6中许多很酷的附加内容之一,但是,知道标记模板字符串是比较少的?
标记模板字符串允许你通过向模板字符串添加自定义标记
来更好地将模板文字解析为字符串。Tag
只是一个解析器函数,它获取字符串模板解释的所有字符串和值的数组,标记函数应返回最终字符串。
在下面的例子中,我们的自定义标记 —— 高亮显示,解释模板文本的值,并将解释后的值包装在结果字符串中,使用<mark>元素进行高亮显示。
Getters & Setters
在大多数情况下,JavaScript对象是简单的。假设我们有一个 user 对象,我们试图使用user访问它的age属性。使用 user.age 得到年龄属性的值,如果没有定义,我们得到未定义的值。
但是,并不一定要这么简单。JavaScript 对象具有 getter 和 setter 的概念。我们可以编写自定义Getter函数来返回我们想要的任何东西,而不是直接返回对象上的值,设置一个值也是一样。
这使我们可以在获取或设置字段时拥有强大的能力,如 virtual fields
,field validations
,side-effects
。
逗号操作符
JavaScript有一个逗号操作符。它允许我们在一行中用逗号分隔多个表达式,并返回上一个表达式的结果。
在这里,所有表达式都将被求值,结果变量将被赋值给expressionN返回的值。
我们经常在for循环中使用了逗号操作符
for (var a = 0, b = 10; a <= 10; a++, b--)
有时在一行中编写多个语句
function getNextValue() { return counter++, console.log(counter), counter }
或者
const getSquare = x => (console.log (x), x * x)
+ 加号操作符号
想要快速地将字符串转换为数字吗?
只需在字符串前面加上+运算符。加号运算符也适用于负数、八进制、十六进制、指数值。更重要的是,它甚至可以转换 Date 或者 Moment.js对象的时间戳!
!! 操作符
从技术上讲,它不是一个单独的JavaScript操作符。它只是两次使用的 JavaScript 反运算符。
如果表达式为true值,则返回true;否则返回false。
~ 非操作符
让我们面对它——没有人关心位操作符。我们什么时候才能使用它!
这里有一个日常用例是关于波浪号或位非运算符的。
事实证明,当与数字一起使用时,非运算符是有效的 ~N => -(N+1)这个表达式只有在 N为-1时才计算为 “0”。
我们可以通过将~放在theindexOf(…)函数前面来利用这一点,如果项目存在于字符串或数组中,则执行布尔检查。
注意:ES6和ES7分别在字符串和数组中添加了一个新的 .include()方法。当然,这是一种比波浪号操作符更清晰的方法来检查一个项目是否存在于数组或字符串中。
标签 的使用
break和continue语句都可以与lebel语句联合使用,从而返回代码中特定的位置。用于嵌套循环,减少循环次数。