Skip to content

Latest commit

 

History

History
1148 lines (779 loc) · 23 KB

03.md

File metadata and controls

1148 lines (779 loc) · 23 KB

三、改进的对象属性

创建对象文字从未如此简单。让我们首先看一下创建对象文字的 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是目标,有o2o3两个来源:

代码清单 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);

当我们运行这个时,我们看到新对象objo1具有相同的值。

代码清单 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) – 1Math.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 位整数xy相乘,并返回结果的低 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开始的位置。该副本取自第二个和第三个参数startend的索引位置。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])

此方法确定数组是否包含某个元素,根据情况返回truefalse

代码清单 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 所示。在处理类型化数组时,您将处理两个主要类:ArrayBufferDataView。我们将在后面详细讨论每一个问题。你可能会问,“我为什么要使用类型化数组?”它们是 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 应用编程接口

在更新版本的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;

| | 注意:此示例仅适用于浏览器。 |

websocket

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...
  });

| | 注意:此示例仅适用于浏览器。 |