创建对象文字从未如此简单。让我们首先看一下创建对象文字的 ES5 语法:
代码清单 19
let first = "john";
let last = "doe";
let obj = {first:
first, last: last};
现在,让我们使用 ES6 语法来看同样的事情:
代码清单 20
let first = "john";
let last = "doe";
let obj = {first, last};
这使得在处理对象文字时代码非常简洁。让我们看看另一个你能做什么的例子:
代码清单 21
function
createDog(name, ability) {
return
{ type: "animal",
name, ability };
}
let a = createDog("wolf",
"bark");
console.log(JSON.stringify(a));
以下是新创建的对象的输出:
代码清单 22
{"type":"animal","name":"wolf","ability":"bark"}
这种简写不会彻底改变您的代码,但它可以使代码更加简洁明了。
现在可以在对象属性定义中定义计算属性名称。您可以使用如下所示的变量来实现这一点:
代码清单 23
var prop = "foo";
var o = {
[prop]:
"hey",
["b"
+ "ar"]: "there",
};
console.log(o.foo);
console.log(o.bar);
以下是输出:
代码清单 24
hey
there
您也可以通过以下方式创建您的属性名:
代码清单 25
let i = 0;
let a = {
["foo"
+ ++i]: i,
["foo"
+ ++i]: i,
["foo"
+ ++i]: i
};
console.log(a.foo1);
console.log(a.foo2);
console.log(a.foo3);
以下是输出:
代码清单 26
1
2
3
还可以将函数用于更高级的属性名,如下例所示:
代码清单 27
function foo() {
return
"firstname";
}
let obj = {
foo:
"bar",
[ "prop_"
+ foo() ]:
42
}
console.log(JSON.stringify(obj));
让我们看看输出:
代码清单 28
{"foo":"bar","prop_firstname":42}
方法属性是我们在 ES5 中已经可以做到的另一个捷径。让我们看看他们在 ES5 中的样子:
代码清单 29
let myMath = {
add:
function(a, b) { return a +
b; },
subtract:
function(a, b) { return a -
b; },
multiply:
function(a, b) { return a *
b; },
divide:
function(a, b) { return a /
b; }
}
现在让我们看看 ES6 中相同的代码会是什么样子:
代码清单 30
let myMath = {
add(a, b) { return a +
b; },
subtract(a, b) { return a -
b; },
multiply(a, b) { return a *
b; },
divide(a, b) { return a /
b; }
}
正如您在比较两者时所看到的,新的 ES6 版本更好,更简洁。
object.assign()
方法用于将属性值从一个或多个源对象复制到给定的目标对象。它将返回目标对象。
我们可以使用它来创建一个简单的对象克隆,如下所示:
代码清单 31
var obj = { firstname:
"matt", lastname: "duffield"
};
var copy = Object.assign({},
obj);
console.log(copy);
当我们运行这个程序时,我们会得到一个新的对象副本:
代码清单 32
Object {firstname: "matt", lastname:
"duffield"}
这里我们看到,我们提供了一个空的对象文字作为我们的目标对象,然后提供了另一个对象文字作为给定的源。我们也从源头获取目标值。
让我们看另一个例子。这一次,让我们提供多个来源以及一个有价值的目标。我们将看到目标将从源接收值,并返回合并的值。我们还会看到,原来的目标变量也发生了变化,o1
是目标,有o2
和o3
两个来源:
代码清单 33
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
var obj = Object.assign(o1,
o2, o3);
console.log(obj);
console.log(o1);
当我们运行这个时,我们看到新对象obj
和o1
具有相同的值。
代码清单 34
Object {a: 1, b: 2, c: 3}
Object {a: 1, b: 2, c: 3}
让我们再做一个;我们将使用访问器复制一个对象文字:
代码清单 35
var obj = {
foo:
1,
get bar() {
return
2;
}
};
var copy = Object.assign({},
obj);
console.log(copy);
运行前面的代码将产生以下结果:
代码清单 36
{foo: 1, bar: 2}
这里发生的是当Object.assign()
执行合并时,它实际上执行 getter bar()
方法,并将结果放入名为bar
的属性中。
如果您真的想复制访问器,请考虑下面的代码:
代码清单 37
function
myAssign(target, ...sources) {
sources.forEach(source => {
Object.defineProperties(target,
Object.keys(source).reduce((descriptors,
key) => {
descriptors[key] = Object.getOwnPropertyDescriptor(source,
key);
return
descriptors;
}, {}));
});
return
target;
}
var copy = myAssign({}, obj);
console.log(copy);
如果在前面的代码中看到一些您没有完全理解的新特性,不要惊慌;我们将在本书的后面讨论它们。当我们运行前面的代码时,我们将得到以下结果:
代码清单 38
{foo: 1, bar: [Getter]}
这次我们得到了我们所期望的,属性和访问器的副本。
全局对象Math
在 ES6 中有几个新方法。以下将提供他们的姓名和简短描述:
数学符号(x)
将x
的符号返回为-1 或+1。除非x
不是NaN
就是零;然后x
返回。
Math.trunc(x)
去掉x
的小数部分,返回整数部分。
数学. cbrt(x)
返回x
的立方根。
数学. expm1(x)
返回Math.exp(x) – 1
。Math.log1p()
的逆。
Math.log1p(x)
返回Math.log(1 + x)
。Math.expm1()
的逆。
Math.log2(x)
计算以 2 为底的对数。
Math.log10(x)
计算以 10 为底的对数。
Math.fround(x)
将x
舍入到最近的单精度浮点值,使用 32 位。
Math.imul(x, y)
将两个 32 位整数x
和y
相乘,并返回结果的低 32 位。这是唯一一个 32 位的基本数学运算,不能通过使用 JavaScript 运算符强制结果返回 32 位来模拟。
型号.clz32(x)
对 32 位整数x
中的前导零位进行计数。
Math.sinh(x)
计算x
的双曲正弦。
Math.cosh(x)
计算x
的双曲余弦。
Math.tanh(x)
计算x
的双曲正切。
Type.asinh(x)
计算x
的反双曲正弦。
数学. acosh(x)
计算x
的反双曲余弦。
数学. atanh(x)
计算x
的反双曲正切。
math . hypt(…值)
计算其参数平方和的平方根。
这些新方法在执行计算和复杂操作时提供了额外的便利。
Number
对象有几个新的方法和属性。以下将提供他们的姓名和简短描述:
号码。希腊语字母之第五字
用于比较浮点数和舍入误差容差。该属性表示 1 和大于 1 的最小值之间的差值,可以表示为Number
,约为 2.2e-16。
号码。MAX_SAFE_INTEGER
常量,表示 JavaScript 中的最大安全整数(2 53 - 1)。
号码。最小安全整数
常量,表示 JavaScript 中的最小安全整数(-(2 53 - 1))。
Number.isNaN()
该方法确定passed
值是否为NaN
。这是原全球isNaN()
的一个更健壮的版本。
Number.isFinite()
该方法确定passed
值是否为有限数。
Number.isInteger()
该方法确定passed
值是否为整数。
编号. issafeinteger()
此方法确定提供的值是否为安全整数。
Number.parseFloat()
此方法分析字符串参数并返回一个浮点数。该方法的行为与全局函数parseFloat()
相同,但其目的是全局的模块化。
Number.parseInt()
此方法分析字符串参数并返回指定基数或基数的整数。
string 对象有几个新方法。以下将提供名称和简短描述以及一些示例:
String.fromCodePoint()
此方法返回使用指定的代码点序列创建的字符串。这是一个静态方法。
代码清单 39
String.fromCodePoint(42); // *
String.fromCodePoint(65, 90); // AZ
string . codepointat()
此方法返回一个非负整数,即 UTF-16 编码的代码点值。
代码清单 40
'ABC'.codePointAt(1); //
66
'\uD800\uDC00'.codePointAt(0); // 65536
String.startsWith()
此方法确定一个字符串是否以另一个字符串的字符开头,根据需要返回 true 或 false。
代码清单 41
var str = 'If you dream it,
you can do it.';
console.log(str.startsWith('If you')); //
true
console.log(str.startsWith('you can do')); //
false
console.log(str.startsWith('you can do', 17));
// true
string . endswith()
此方法确定一个字符串是否以另一个字符串的字符结尾,根据需要返回 true 或 false。最后一个示例允许您指定字符串的长度,从而将实际字符串固定为新值。
代码清单 42
var str = 'If you can dream
it, you can do it.';
console.log(str.endsWith('do it.')); //
true
console.log(str.endsWith('you can')); //
false
console.log(str.endsWith('you can', 28)); //
true
String.includes()
此方法确定一个字符串是否可以在另一个字符串中找到,根据需要返回 true 或 false。
代码清单 43
var str = 'If you can dream
it, you can do it.';
console.log(str.includes('If you can')); // true
console.log(str.includes('it.')); //
true
console.log(str.includes('nonexistent')); //
false
console.log(str.includes('If you can', 1));
// false
console.log(str.includes('IF YOU')); //
false
String.normalize()
此方法返回给定字符串的 Unicode 规范化形式。如果该值不是字符串,它将首先转换为字符串。
代码清单 44
// U+1E9B: Latin
small letter long s with dot above
// U+0323: Combining
dot below
var str = '\u1E9B\u0323';
//
Canonically-composed form (NFC)
// U+1E9B: Latin
small letter long s with dot above
// U+0323:
Combining dot below
str.normalize('NFC');
// '\u1E9B\u0323'
str.normalize(); // same as above
//
Canonically-decomposed form (NFD)
// U+017F: Latin
small letter long s
// U+0323:
Combining dot below
// U+0307: Combining
dot above
str.normalize('NFD');
// '\u017F\u0323\u0307'
//
Compatibly-composed (NFKC)
// U+1E69: Latin
small letter s with dot below and dot above
str.normalize('NFKC');
// '\u1E69'
//
Compatibly-decomposed (NFKD)
// U+0073: Latin
small letter s
// U+0323:
Combining dot below
// U+0307:
Combining dot above
str.normalize('NFKD');
// '\u0073\u0323\u0307'
String.repeat()
此方法构造并返回一个新字符串,该字符串包含调用它的字符串的指定数量的副本,这些副本连接在一起。
代码清单 45
'foo'.repeat(-1); //
RangeError
'foo'.repeat(0); // ''
'foo'.repeat(1); // 'foo'
'foo'.repeat(2); // 'foofoo'
'foo'.repeat(2.5); // 'foofoo'
(count will be converted to integer)
'foo'.repeat(1/0); //
RangeError
该方法返回一个新的Iterator
对象,该对象迭代一个String
值的代码点,将每个代码点作为一个String
值返回。
代码清单 46
var string = 'A\uD835\uDC68';
var strIter = string[Symbol.iterator]();
console.log(strIter.next().value); // "A"
console.log(strIter.next().value); // "\uD835\uDC68"
Array
对象有几个新方法。以下将提供每个的名称、简短描述和示例:
数组。of()
该方法使用可变数量的参数创建一个新的Array
实例,而不考虑参数的数量或类型。
代码清单 47
Array.of(1); //
[1]
Array.of(1, 2, 3); // [1,
2, 3]
Array.of("a", 7,
12.5); //
["a", 7, 12.5]
Array.of(undefined); //
[undefined]
array . copy in(目标,开始[,结束= this.length])
该方法将数组中的数组元素序列复制到从target
开始的位置。该副本取自第二个和第三个参数start
和end
的索引位置。end
参数是可选的,默认为数组的长度。
代码清单 48
[1,
2, 3, 4, 5].copyWithin(0, 3); // [4, 5, 3, 4, 5]
[1,
2, 3, 4, 5].copyWithin(0, 3, 4); // [4, 2,
3, 4, 5]
[1,
2, 3, 4, 5].copyWithin(0, -2, -1); // [4, 2, 3,
4, 5]
Array.entries()
该方法返回一个新的Array
Iterator
对象,该对象包含数组中每个索引的键/值对。
代码清单 49
var arr = ['a', 'b', 'c'];
var eArr = arr.entries();
console.log(eArr.next().value); // [0, 'a']
console.log(eArr.next().value); // [1, 'b']
console.log(eArr.next().value); // [2, 'c']
Array.fill(值[,start = 0[,end = this.length]])
此方法用静态值填充数组从开始索引到结束索引的所有元素。
代码清单 50
[1,
2, 3].fill(4); //
[4, 4, 4]
[1,
2, 3].fill(4, 1);
// [1, 4, 4]
[1,
2, 3].fill(4, 1, 2); //
[1, 4, 3]
[1,
2, 3].fill(4, 1, 1); //
[1, 2, 3]
[1,
2, 3].fill(4, -3, -2); // [4,
2, 3]
[1,
2, 3].fill(4, NaN,
NaN); // [1, 2, 3]
Array(3).fill(4);
// [4, 4, 4]
[].fill.call({ length: 3 }, 4); // {0: 4, 1:
4, 2: 4, length: 3}
Array.find(回调[,thisArg])
如果数组中的某个元素满足所提供的测试函数,则此方法返回数组中的一个值。否则,返回undefined
。
代码清单 51
function isPrime(value,
index, array) {
for
(var i =
2; i <
value; i++) {
if
(value % i ===
0) {
return
false;
}
}
return
value > 1;
}
console.log([4,
6, 8, 12].find(isPrime)); //
undefined, not found
console.log([4,
5, 8, 12].find(isPrime)); //
5
Array.findIndex(回调[,thisArg])
如果数组中的某个元素满足提供的测试函数,则此方法返回数组中的索引。否则,返回-1
。
代码清单 52
function
isPrime(value, index, array) {
for
(var i =
2; i <
value; i++) {
if
(value % i ===
0) {
return
false;
}
}
return
value > 1;
}
console.log([4,
6, 8, 12].findIndex(isPrime)); // -1, not found
console.log([4,
6, 7, 12].findIndex(isPrime)); // 2
Array.from(arrayLike[,mapFn[,thisArg]])
该方法从类似数组或可迭代的对象创建一个新的Array
实例。这种方法并不新鲜,但有一些很酷的好处。
代码清单 53
let nodes = document.querySelectorAll(".business");
nodes.forEach((item, index, arr) => {
console.log(item.name);
});
Array.includes(searchElement[,fromIndex])
此方法确定数组是否包含某个元素,根据情况返回true
或false
。
代码清单 54
[1,
2, 3].includes(2); // true
[1,
2, 3].includes(4); // false
[1,
2, 3].includes(3, 3); // false
[1,
2, 3].includes(3, -1); // true
[1,
2, NaN].includes(NaN); // true
Array.keys()
这个方法返回一个新的Array
Iterator
,它包含数组中每个索引的键。
代码清单 55
var arr = ["a",
"b", "c"];
var iterator = arr.keys();
console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
Array.values()
该方法返回一个新的Array
Iterator
对象,该对象包含数组中每个索引的值。
代码清单 56
var arr = ['w', 'y', 'k', 'o', 'p'];
var eArr = arr.values();
// your browser
must support for..of loop
// and let-scoped
variables in for loops
for (let letter of eArr) {
console.log(letter);
}
数组@ @迭代器
@@iterator
属性的初始值与values()
属性的初始值是同一个函数对象。
代码清单 57
var arr = ['w', 'y', 'k', 'o', 'p'];
var eArr = arr[Symbol.iterator]();
console.log(eArr.next().value); // w
console.log(eArr.next().value); // y
console.log(eArr.next().value); // k
console.log(eArr.next().value); // o
console.log(eArr.next().value); // p
类型化数组在处理二进制数据时很有用。它们描述了底层二进制数据缓冲区的类似数组的视图。请注意,没有名为TypedArray
的全局属性,也没有直接可见的构造函数。相反,有许多不同的全局属性,它们的值是特定元素类型的类型化数组构造函数,如表 1 所示。在处理类型化数组时,您将处理两个主要类:ArrayBuffer
和DataView
。我们将在后面详细讨论每一个问题。你可能会问,“我为什么要使用类型化数组?”它们是 JavaScript 处理原始二进制数据的答案。这方面的典型例子可以是流式音频或视频数据。
以下是可用的类型化数组构造函数列表:
表 1:类型化数组对象
| 类型 | 字节大小 | 描述 | Web IDL 类型 | c 型 | | Int8Array | one | 8 位二进制补码有符号整数 | 字节 | int8_t | | Uint8Array | one | 8 位无符号整数 | 八位字节 | uint8_t | | 我是游泳健将 | one | 8 位无符号整数(箝位) | <八位字节 | uint8_t | | Int16Array | Two | 16 位二进制补码有符号整数 | 短的 | int16_t | | Uint16 数组 | Two | 16 位无符号整数 | 无符号短 | uint16_t | | Int32Array | four | 32 位二进制补码有符号整数 | 长的 | int32_t | | Uint32 数组 | four | 32 位无符号整数 | 无符号长 | uint32_t | | 浮动 32 数组 | four | 32 位 IEEE 浮点数 | 无限制浮动 | 漂浮物 | | Float64Array | eight | 64 位 IEEE 浮点数 | 无限制双重 | 两倍 |
让我们看看下面的例子:
代码清单 58
var buffer = new ArrayBuffer(8);
console.log(buffer.byteLength);
我们从前面的代码示例中获得以下输出:
代码清单 59
8
让我们更进一步,创建一个DataView
来操作缓冲区中的数据:
代码清单 60
var buffer = new ArrayBuffer(8);
var view = new DataView(buffer);
view.setInt8(0, 3);
console.log(view.getInt8(0));
这里,我们像以前一样创建缓冲区。接下来,我们创建一个DataView
,它接受缓冲区作为参数。我们现在能够通过设置特定的类型值来操作缓冲区。我们使用setInt8
函数将值“3”放入缓冲区的 0 字节偏移位置。如果我们运行这段代码,我们会得到如下预期的输出:
代码清单 61
3
让我们试试另一个例子:
代码清单 62
var buffer = new ArrayBuffer(8);
var view = new DataView(buffer);
view.setInt32(0, 3);
console.log(view.getInt8(0));
console.log(view.getInt8(1));
console.log(view.getInt8(2));
console.log(view.getInt8(3));
console.log(view.getInt32(0));
在本例中,我们使用setInt32
将该值设置到我们的ArrayBuffer
中。由于setInt32
需要 4 个字节(32 位= 4 个字节),而setInt8
需要 1 个字节(8 位= 1 个字节),所以我们的getInt8
调用直到我们的偏移量为 4 个字节时才会找到该数字。
如果我们运行这段代码,我们会得到如下预期的输出:
代码清单 63
0
0
0
3
3
我们可以像创建常规数组一样创建和访问具有更具体名称的类型化数组视图类,而不是创建通用的DataView
。这些数组列于表 1 中。
考虑以下示例:
代码清单 64
var buffer = new ArrayBuffer(8);
// 32-bit View
var bigView = new Int32Array(buffer);
bigView[0] = 98;
bigView[1] = 128;
for(let value of
bigView)
{
console.log(value);
// 98
// 128
}
// 16-bit View
var mediumView = new Int16Array(buffer);
for(let value of
mediumView)
{
console.log(value);
// 98
// 0
// 128
// 0
}
// 8-bit View
var smallView = new Int8Array(buffer);
for(let value of
smallView)
{
console.log(value);
// 98
// 0
// 0
// 0
// -128
// 0
// 0
// 0
}
// 8-bit Unsigned View
var unsignedView = new Uint8Array(buffer);
for(let value of
unsignedView)
{
console.log(value);
// 98
// 0
// 0
// 0
// 128
// 0
// 0
// 0
}
从前面的代码中可以看出,我们能够像处理常规数组一样处理这些类型化数组。这里最大的区别是这些是真正的“类型化”数组。
以下是浏览器支持的类型化数组应用编程接口的一小部分:
- 文件应用编程接口
- XML HttpRequest
- 获取应用编程接口
- 帆布
- websocket
让我们看看如何在代码中使用这些 API。
File
API 允许您访问本地文件。下面的代码演示了如何在ArrayBuffer
中获取提交的本地文件的字节:
代码清单 65
let fileInput = document.getElementById('fileInput');
let file =
fileInput.files[0];
let reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload
= function
() {
let arrayBuffer = reader.result;
// process the buffer...
};
在更新版本的XMLHttpRequest
应用编程接口中,您可以将结果作为ArrayBuffer
交付:
代码清单 66
let xhr = new XMLHttpRequest();
xhr.open('GET', someUrl);
xhr.responseType
= 'arraybuffer';
xhr.onload
= function
() {
let arrayBuffer = xhr.response;
// process the buffer...
};
xhr.send();
像XMLHttpRequest
应用编程接口一样,Fetch
应用编程接口允许您请求资源。然而,大多数人会认为这是对XMLHttpRequest
在原料药设计方面的改进。它还利用了 Promises,我们将在本书的后面部分讨论它。下面的例子演示了如何下载网址指向的内容作为ArrayBuffer
:
代码清单 67
fetch(url)
.then(request => request.arrayBuffer())
.then(arrayBuffer => /* process buffer */);
canvas
的二维上下文允许您检索位图数据作为Uint8ClampedArray
的实例:
代码清单 68
let canvas = document.getElementById('my_canvas');
let context =
canvas.getContext('2d');
let imageData =
context.getImageData(0, 0, canvas.width, canvas.height);
let uint8ClampedArray =
imageData.data;
WebSockets
让你通过ArrayBuffers
收发二进制数据:
代码清单 69
let socket = new WebSocket('ws://127.0.0.1:8081');
socket.binaryType
= 'arraybuffer';
// Wait until socket is open
socket.addEventListener('open', function
(event) {
// Send binary data
let typedArray = new
Uint8Array(4);
socket.send(typedArray.buffer);
});
// Receive binary data
socket.addEventListener('message', function
(event) {
let arrayBuffer = event.data;
// process the buffer...
});