最近在备考雅思,空下来就翻译一下技术文章,增加阅读能力。这篇文章来自
medium
,作者:Chidume Nnamdi,文章原文地址
⚠ 重要 ⚠
这篇文章描述的部分功能已经被弃用了,有些功能被EcmaScript禁用了。这些功能仍然在许多图书馆中很常见,还是值得我们学习了解的。
仅供参考。
标记模板字符串
taggingThis`Hey!!, I'm ${name} !!`
如果已经用过React样式组件化,那么就已经用过标记模板字符串。
带有模板标记的字符串可以更好的控制字符串解析。允许我们使用函数来解析模板字符串。
taggingThis`Hey!!, I'm ${name} !!`
taggingThis
是一个功能。标记函数的第一个参数包含字符串值的数组,其余参数与表达式相关。
function taggingThis(strings, ...vals) {
//...
}
taggingThis`Hey!!, I'm ${name} !!`
strings
参数将保持在[ "Hey!!, I'm ", ' !!' ]
,其余的参数传递到数组中,vals[ 'Nnamdi' ]
。
这个taggingThis
函数里面,可以操作入参和返回不同内容。
function taggingThis(strings, ...vals) {
return strings[0] + vals[0] + strings[1]
}
const name = "Nnamdi"
taggingThis `Hey!!, I'm ${name} !!` // Hey!!, I'm Nnamdi !!
逗号运算符
逗号运算符分割表达式和返回链中的最后一个表达式。
let expr = (99, 88, 77)
expr // 77
有三个表达式99,88和77。所有的表达式都被求值和最后一个被赋值给expr
。
下面这个for
循环:
for(let i = 0, ii = 1; i< 10; i++, ii--) { ... }
当想写一个简短的lambda
函数,逗号运算符会派上用场。
const lb = (a, b, arr) => (arr.push(a*b), a*b)
这里面有两个语句,第一个语句将乘法结构推入数组arr,第二个语句是a*b。第二个结果就是返回给回调者的内容。
三元运算符逗号也是很有用,和短lambda
函数语法相同,仅接受表达式,不是接受语句。
with语句
⚠注意⚠
通常不建议使用with
语句,已经被废弃了。在严格模式下被禁止了。使用with
会增加js语言性能和安全上的问题。
with
是JS中的关键字。用于扩展语句的作用于链。
下面这样使用:
with(expression)
statement
// OR
with(expression) {
statement
statement
...
}
计算表达式并围绕表达式创建作用域。with
语句的表达式和父作用域在大括号里面。
const a = 45
let b = 8
var f = 909
with(f) {
log(f, b, a) // 909 8 45
}
f
是包裹在作用域链中。f
和with
语句块外面声明的外部变量在with
语句块里面可见。
注意,let
和const
在with
语句里面声明的变量只能在语句里面可以用,外部不可以调用。
with(f) {
log(f, b, a) // 909 8 45
let withinF = 20
}
log(withinF) // undefined
调用withinF
方法,将会抛出ReferenceError
错误,因为变量withinF
只存在with
语句块里面。
尽量避免使用with
。
in
in
也是一个关键字,检查对象里面存在的属性。在for..in
循环中就使用,但没有意识到in
就是关键字。
如果对象里面存在这个属性in
将返回true,否则将返回false。
const o = {
prop: 90
}
log("prop" in o) // true
上面in
是独立使用的,并没有for...in
循环。
将检查prop
是否可作为o对象里面的属性使用。返回ture,因为已经在o
里面定义了prop
。
如果检查未定义的属性:
const o = {
prop: 90
}
log("prop1" in o) // false
打印出false。因为prop1
在o这个对象里面没有定义。
已经用过这些功能了吗?
数组构造函数
知道吗?可以不用传统的方式定义数组。
const arr = [90,88,77]
用数组构造函数
const arr = new Array(90, 88, 77)
传递给构造函数的参数排序将构成其索引的基础。
90第一个参数索引是0,88第二参数索引是1,77最后一个参数索引是2.
arr[0] // 90
arr[1] // 88
arr[2] // 77
用new Array(...)
和使用数组是一样的。
那么
const arr = new Array(90, 88, 77)
同下面是一样的
const arr = [90, 88, 77]
有人认为使用数组构造函数功能有些缺点或不一致:
Why never use new Array in Javascript
使用new
的方式创建一个数组,适用于构造函数new Array()
的参数大于或等于2的情况。
这样做
const arr = new Array(90, 88, 77)
将创建数组[90, 88, 77]
.因为new Array(..)
的参数为3,即>=2.
给new Array(..)
传递一个参数,将导致JS引擎为传递参数大小的数组分配空间。
const arr = new Array(6)
将创建一个包含6项或元素的数组,长度为6。
const arr = new Array(6)
arr // [ <6 empty items> ]
arr.length // 6
const arr = new Array(6)
同下:
const arr = [, , , , , ]
arr // [ <6 empty items> ]
arr.length // 6
如果说这是JS不一致或偏激的功能,我不会同意。这在EcmaScript规范中。
这并不矛盾。规范中都有描述。在给出结论之前,我们首先应该始终先阅读语言的规范。
Function构造函数
可以用Function构造函数定一个一个Function。
接下来讲清楚。在JS里我们定义一个函数像这样:
const mul = (a, b) => a * b
// OR
function mul(a, b) {
return a * b
}
// OR
const mul = function(a, b) {
return a * b
}
我们仍然可以像下面这样做:
const mul = new Function("a", "b", "return a * b")
传递给Function
的参数形成函数的参数和主体。mul
变量变成函数的名称。
最后的参数将变成函数的主体,那么最后参数的前面将会变成函数的参数。
在mul
中,参数a
和b
是函数将接收的参数,参数return a * b
是函数的主体。a和b相乘然后返回结果。
调用函数在括号里面传参数mul(…)
:
const mul = new Function("a", "b", "return a * b")
log(mul(7, 8)) // 56
根据MDN 文档:
直接调用构造函数可以动态创建函数,但存在安全和评估方面类似的(但重要性不高)问题。但是,与eval不同,Function构造函数创建的函数仅在全局范围内执行。
解构数组
我们可以使用元素的索引来分解数组的元素。
const arr = [99, 88, 77]
元素99, 88, 77
有索引0, 1, 2
.从数组arr中获取元素99,我们将其作为属性传递给arr,
arr[1]
就像处理对象一样:
let o = {
prop: 99
}
o[prop] // 99
可以像这样从o对象里面解构属性:
const {prop} = o
prop // 99
同样可以这样操作数组:
const arr = [99, 88, 77]
const { 1: secA } = arr
secA // 88
const { 0: firstA, 1: secA, 2: thirdA } = arr
firstA // 99
secA // 88
thirdA // 77
我们使用索引号提取元素。索引是定义数组中元素位置的属性。
const arr = [99, 88, 77]
和下面的相同:
const arr = {
0: 99,
1: 88,
2: 77,
length: 3
}
数组也是对象,这就是为什么用对象的方式去解构数组效果一样,但还有一种特殊的语法解构数组:
const [first, second, third] = arr;
尽可能的避免知道数组中的特定位置信息(开始索引,结束索引)。
Reducing Array contents using the length
property
可以操作length
属性减少数组。
数组中的length
属性是指数组中的长度。
const arr = [99, 88, 77]
arr.length // 3
减少length
属性,JS引擎将元素减少为与length
属性。
const arr = [99, 88, 77]
arr.length // 3
arr.length = 1
arr // [ 99 ]
arr的length
属性更改为1,因此从右侧减少了元素,使其长度等于设置的length属性的值。
如果增加length
属性,JS引擎将会增加元素(未定义),使数组元素长度符合length
属性值。
const arr = [99, 88, 77]
arr.length // 3
arr.length = 1
arr // [ 99 ]
arr.length = 5
arr // [ 90, <4 empty items> ]
数组中只有一个,如果增加长度到5,那么增加了4个空到数组里面,使数组长度达到5.
Arguments
用arguments
获取函数传递的参数。这样做的好处,可以使用arguments
对象传参数给函数,无需确切的定义函数参数。
function myFunc() {
arguments[0] // 34
arguments[1] // 89
}
myFunc(34,89)
arguments
对象是数组索引。也就是说,属性是数字,所以就可以通过键值索引。
arguments
对象是通过Arguments
类实例化的,该类具有一些很酷的属性。
arguments.callee.name
这是指当前被调用的函数名称。
function myFunc() {
log(arguments.callee.name) // myFunc
}
myFunc(34, 89)
arguments.callee.name
引用调用当前执行函数的函数的名称。
function myFunc() {
log(arguments.callee.name) // myFunc
log(arguments.callee.caller.name) // myFuncCaller
}
(function myFuncCallee() {
myFuncCaller(34, 89)
})()
可变参数在函数中是特别有用的。
Skip the brackets
实例化对象,可以忽略括号()
.
例如:
class D {
logger() {
log("D")
}
}
// Normally, we do this:
(new D()).logger() // D
// Also, we can skip the ():
(new D).logger() // D
// and it still works
括号是可选的,即使在内置类中也是如此。
(new Date).getDay()
(new Date).getMonth()
(new Date).getYear()
void operator
void
是JS中的关键字,是一个陈述语句,然后返回undefined
.
例如:
class D {
logger() {
return 89
}
}
const d = new D
log(void d.logger()) // undefined
logger
方法应该返回89,但void
关键字将无效并返回undefined
.
之前未定义的值可以被分配给另一个值,会伪造定义。void
操作符可以确保真正的undefined
。用于最小化目的。
Function property
可以在函数中设置属性:
function func() {
func.prop1 = "prop1"
}
func.prop2 = "prop2"
函数也是对象,所以上面的写法是有效的。当使用对象时,Function属性将是Function所有实例的静态属性。
const j1 = new func()
const j2 = new func()
j1.prop1 // prop1
j2.prop1 // prop1
j1.prop1 = "prop1 changed"
j2.prop1 // prop1 changed
当用作是函数时,将是全局属性。
function func() {
func.prop1 === undefined ? func.prop1 = "yes" : null
if(func.prop1 === "yes")
log("Prop with Yes")
if (func.prop1 == "no")
log("Prop with No")
}
func() // Prop with Yes
func.prop1 = "no"
func() // Prop with No
Inheritance via proto
_proto_
是从JavaScript中的对象继承属性的方法。
Object.prototype
的 __proto__
属性是一个访问器属性(一个getter函数和一个setter函数), 暴露了通过它访问的对象的内部[[Prototype]]
(一个对象或 [null
])。
看一个例子:
// proto.js
const log = console.log
const obj = {
method: function() {
log("method in obj")
}
}
const obj2 = {}
obj2.__proto__ = obj
obj2.method()
两个对象字面量:obj和obj2都有一个merhod属性方法。obj2是一个空对象字面量,没有属性。
接下来,访问obj2的__proto__
,并将obj赋值给它。通过Object.prototype
将obj所有的可访问的属性赋值给obj2.这就是为什么在没有定义的情况下可以调用obj2上面的method
,而且没有报错。
$ node proto
method in obj
obj2继承了obj的属性,那么在它的属性里面,method
方法将可用。
proto
用于对象,例如:object literal, Object, Array, Function, Date, RegEx, Number, Boolean, String.
Unary + Operator
一元+操作符将其操作数转换为Number
类型。
+"23" // 23
+{} // NaN
+null // 0
+undefined // NaN
+{ valueOf: () => 67 } // 67
+"nnamdi45" // NaN
当我们想快速将变量转换为Number
类型时,这非常方便。
Unary — Operator
一元运算符将其操作数转换为Number
类型,然后取反。
该运算符将一元+运算符的结果取反。首先,它将操作数转换为Number
值,然后取反该值。
-"23" // -23
字符串“23”将转换为Number
类型,得到23.然后,正数将会转换为复数形式-23.
-{} // NaN
-null // -0
-undefined // NaN
-{ valueOf: () => 67 } // -67
-"nnamdi45" // NaN
如果转换为数值的结果为NaN
,则不会取反。
取负+0产生-0,取负-0产生+0。
- +0 // -0
- -0 // 0
Exponentiation ** operator
该运算符用于查找数字的指数。数字的幂上升到一个确定的级。
在数学中,如果2的3次方(找到2的指数),则意味着将2乘以三次:
2 * 2 * 2
我们可以用**
运算符在JS中相同的操作:
2 ** 3 // 8
9 ** 3 // 729
Conclusion
😀JS中有很多很棒的功能。即将推出更多功能,将不断更新。
如果拥有或发现了JS很少使用的任何功能,可以在评论中回复。很高兴能知道这些😀。
翻译结束!