> 文档中心 > ES6新特性(1)

ES6新特性(1)

目录

ECMAScript6新特性

命令行工具

Nodejs环境安装

 Babel转码器

Let命令

let不存在变量提升

let不允许重复声明

const 命令

变量的解构赋值

数组的解构赋值

对象的解构赋值

字符串的解构赋值

变量的解构赋值_用途

交换变量的值

函数返回多个值

提取 JSON 数据

字符串扩展

字符串遍历器接口

模板字符串

字符串新增方法

includes(), startsWith(), endsWith()

padStart(),padEnd()

trimStart(),trimEnd()

at()

数值的扩展

Number.isFinite(), Number.isNaN()

Number.parseInt(), Number.parseFloat()

Number.isInteger()

Math函数扩展

Math.trunc()

Math.sign()

Math.cbrt()

对数方法

双曲函数方法

数组扩展_扩展运算符

Array.from()

Array.of()

数组实例的 copyWithin()

数组实例的 find() 和 findIndex()

数组实例的 fill()

数组实例的 includes()

对象的扩展

属性的简洁表示法

属性名表达式

方法的 name 属性

对象的扩展运算符

对象的新增方法-Object.is()

对象的新增方法-Object.assign()

运算符的扩展

指数运算符

链判断运算符

Null 判断运算符

函数的扩展

函数参数的默认值

rest 参数

name 属性

函数的 length 属性

箭头函数

Symbol

实例属性description

属性名的遍历

Symbol.for()


ECMAScript6新特性

命令行工具

常用命令行工具有两种

  1. CMD 命令行工具
  2. PowerShell 命令行工具

CMD 命令行 1 打开命令行窗口 1 win :左下角开始,找到运行,点击,输入 cmd ,回车 2 win win+r 快速打开命令行窗口 3 mac command + 空格,输入 terminal 2 选择盘符:盘符名加冒号 E: 3 查看盘符及目录下文件与文件夹: win:dir mac:ls 4 清空命令行信息: win:cls mac:clear 5 进入文件夹或目录: cd 文件夹名称 6 返回到上一级目录: cd ../ 7 快速补全目录或文件夹名称: tab 8 创建文件夹: mkdir 文件夹名称 9 查看历史输入过的命令:上下按键

PowerShell 1 打开方式

  1. 1 在开始位置搜索 PowerShell 打开
  2. 2 在对应目录按住 shift +右键,打开

其他保持一致

Nodejs环境安装

Nodejs 官网 https://nodejs.org/en/ npm 镜像 由于服务器在国外,所以下载速度比较慢,我们可以用国内的镜像 阿里镜像地址 https://npmmirror.com/ 在命令行运行如下命令即可 npm install -g cnpm -- registry = https://registry.npmmirror.com 看到如下信息,代表安装成功

 

 Babel转码器

        Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代 码,从而在老版本的浏览器执行。这意味着,你可以用 ES6 的方式 编写程序,又不用担心现有环境是否支持

浏览器支持性查看 https://caniuse.com/

转码示例 原始代码用了箭头函数, Babel 将其转为普通函数,就能在不支持 箭头函数的 JavaScript 环境执行了

// 转码前input.map(item => item + 1);// 转码后input.map(function (item) {  return item + 1;});

Babel 安装流程 第一步:安装 Babel(在具体的 写代码的文件夹,即根目录 中安装)

npm install --save-dev @babel/core

第二步:配置文件 .babelrc Babel 的配置文件是 .babelrc ,存放在项目的 根目录 下(在根目录下建一个 .babelrc 文件)。使用 Babel 的第一步,就是配置这个文件。 该文件用来设置转码规则和插件,基本格式如下

{  "presets": [],  "plugins": []}

第三步:转码规则 presets字段设定转码规则,官方提供以下的规则集,你可以根据需要在 根目录 下安装

npm install --save-dev @babel/preset-env

第四步:将规则加入 .babelrc(覆盖之前的)

{    "presets": [      "@babel/env"   ],    "plugins": []}

 最后一步

Babel 命令行转码 Babel 提供命令行工具 @babel/cli ,用于命令行转码 安装 Babel命令转码工具: npm install -- save - dev @babel/cli

 npm install --save-dev @babel/cli

 基本用法如下

# 转码结果输出到控制台输出  npx babel example.js# 转码结果写入一个文件# --out-file 或 -o 参数指定输出文件  npx babel example.js --out-file compiled.js# 或者$ npx babel example.js -o compiled.js# 整个目录转码# --out-dir 或 -d 参数指定输出目录$ npx babel src --out-dir lib# 或者$ npx babel src -d lib

Let命令

ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

{  let a = 10;  var b = 1;}a // ReferenceError: a is not defined.b // 1

上面代码在代码块之中,分别用let和var声明了两个变量。然后在代码块之外调用这两个变量,结果let声明的变量报错,var声明的变量返回了正确的值。这表明,let声明的变量只在它所在的代码块有效。

for循环的计数器,就很合适使用let命令。

for (let i = 0; i < 10; i++) {  // ...}console.log(i);// ReferenceError: i is not defined

上面代码中,计数器i只在for循环体内有效,在循环体外引用就会报错。

下面的代码如果使用var,最后输出的是10。

var a = [];for (var i = 0; i < 10; i++) {  a[i] = function () {    console.log(i);  };}a[6](); // 10

上面代码中,变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。

如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。

var a = [];for (let i = 0; i < 10; i++) {  a[i] = function () {    console.log(i);  };}a[6](); // 6

上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

for (let i = 0; i < 3; i++) {  let i = 'abc';  console.log(i);}// abc// abc// abc

上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域

let不存在变量提升

var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。

为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

// var 的情况console.log(foo); // 输出undefinedvar foo = 2;// let 的情况console.log(bar); // 报错ReferenceErrorlet bar = 2;

let不允许重复声明

let不允许在相同作用域内,重复声明同一个变量。

// 报错function func() {  let a = 10;  var a = 1;}// 报错function func() {  let a = 10;  let a = 1;}
   /**  * 1. var关键字的作用域:var关键字是函数级别作用域  * 2. let关键字是块级作用域,理解为{}级别的作用域  * 3. let不存在变量提升  * 4. let不允许重复声明  */ // var name = "itbaizhan"; // console.log(name); // if(true){ //     var age = 20; // } // console.log(age); // 20 // function fn(){ //     var sex = "男"; // } // fn(); // console.log(sex); // 报错 // let name = "itbaizhan"; // console.log(name); // if(true){ //     let age = 20; // } // console.log(age); // 报错 // for(let i = 0;i<5;i++){ //     console.log(i); // }  // var arr = []; // for(var i = 0;i<10;i++){ // 并不是创建了10个i,而是只创建了一个i,i一直在被重新赋值 //     arr[i] = function(){ //  console.log(i); //     } // } // // 相当于每一个arr= [10个数据],每个数据都是一个函数 // arr[6](); // 10 // var arr = []; // for(let i = 0;i<10;i++){  // 每次循环独立的创建一个i,有10个i //     arr[i] = function(){ //  console.log(i); //     } // } // arr[3](); // 6 // console.log(names); // let names = "itbaizhan"; // let age = 20; // let age = 30; // console.log(age);    

const 命令

const声明一个只读的常量。一旦声明,常量的值就不能改变。

const PI = 3.1415;PI // 3.1415PI = 3;// TypeError: Assignment to constant variable.

上面代码表明改变常量的值会报错。

const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

const foo;// SyntaxError: Missing initializer in const declaration

上面代码表示,对于const来说,只声明不赋值,就会报错。

const的作用域与let命令相同:只在声明所在的块级作用域内有效。

if (true) {  const MAX = 5;}MAX // Uncaught ReferenceError: MAX is not defined

const命令声明的常量也是不提升

if (true) {  console.log(MAX); // ReferenceError  const MAX = 5;}

const声明的常量,也与let一样不可重复声明。

var message = "Hello!";let age = 25;// 以下两行都会报错const message = "Goodbye!";const age = 30;

变量的解构赋值

数组的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

以前,为变量赋值,只能直接指定值。

let a = 1;let b = 2;let c = 3;

ES6 允许写成下面这样。

let [a, b, c] = [1, 2, 3];

上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。

本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。

let [foo, [[bar], baz]] = [1, [[2], 3]];foo // 1bar // 2baz // 3let [ , , third] = ["foo", "bar", "baz"];third // "baz"let [x, , y] = [1, 2, 3];x // 1y // 3let [head, ...tail] = [1, 2, 3, 4];head // 1tail // [2, 3, 4]let [x, y, ...z] = ['a'];x // "a"y // undefinedz // []

如果解构不成功,变量的值就等于undefined。

解构赋值允许指定默认值。

let [foo = true] = [];foo // truelet [x, y = 'b'] = ['a']; // x='a', y='b'let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。

对象的解构赋值

解构不仅可以用于数组,还可以用于对象。

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };foo // "aaa"bar // "bbb"

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
 

let { bar, foo } = { foo: 'aaa', bar: 'bbb' };foo // "aaa"bar // "bbb"let { baz } = { foo: 'aaa', bar: 'bbb' };baz // undefined

上面代码的第一个例子,等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响。第二个例子的变量没有对应的同名属性,导致取不到值,最后等于undefined。

对象的解构赋值,可以很方便地 将现有对象的方法,赋值到某个变量

let { random,floor } = Math; let { log } = console;

指定默认值

var {x = 3} = {};

注意事项,如果要将一个 已经声明的变量 用于解构赋值,必须非常小心

let hello = "Hello";let { hello } = {hello:"hello"}; // 报错let hello = "Hello";({ hello } = {hello:"hello"}); // 正确

字符串的解构赋值

字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象

const [a, b, c, d, e] = 'hello';a // "h"b // "e"c // "l"d // "l"e // "o"

 类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

变量的解构赋值_用途

交换变量的值

let x = 1;let y = 2;[x, y] = [y, x];

交换变量 x y 的值,这样的写法不仅简洁,而且易读,语义非常清晰

从函数返回多个值

函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。

// 返回一个数组function example() {    return [1, 2, 3];}let [a, b, c] = example();// 返回一个对象function example() {    return {        foo: 1,        bar: 2   };}let { foo, bar } = example();

提取 JSON 数据

解构赋值对提取 JSON 对象中的数据,尤其有用

let jsonData = {    id: 42,     status: "OK",    data: ["iwen", "itbaizhan"] }; let { id, status, data } = jsonData;

字符串扩展

字符串 Unicode 表示法 ES6 加强了对 Unicode 的支持,允许采用 \uxxxx 形式表示一个字符,其中 xxxx 表示字符的 Unicode 码点。

"\u0061"// "a"

字符串遍历器接口

for...of 循环遍历

for (let i of 'itbaizhan') {  console.log(i);//打印每个字符}
for (let codePoint of 'foo') {  console.log(codePoint)}// "f"// "o"// "o"

模板字符串

模板字符串( template string )是增强版的字符串,用反引号(` )标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

let url = "www.itbaizhan.com"let h1 = "itbaizhan"let h2 = `itbaizhan`

字符串新增方法

includes(), startsWith(), endsWith()

传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
let s = 'Hello world!';s.startsWith('Hello') // trues.endsWith('!') // trues.includes('o') // true

这三个方法都支持第二个参数,表示开始搜索的位置。

let s = 'Hello world!';s.startsWith('world', 6) // trues.endsWith('Hello', 5) // 注意 trues.includes('Hello', 6) // false

实例方法:repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。(包含自身)

'x'.repeat(3) // "xxx"'hello'.repeat(2) // "hellohello"'na'.repeat(0) // ""

padStart(),padEnd()

ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。

'x'.padStart(5, 'ab') // 'ababx''x'.padStart(4, 'ab') // 'abax''x'.padEnd(5, 'ab') // 'xabab''x'.padEnd(4, 'ab') // 'xaba'

trimStart(),trimEnd()

ES2019 对字符串实例新增了trimStart()和trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

const s = '  abc  ';s.trim() // "abc"s.trimStart() // "abc  "s.trimEnd() // "  abc"

at()

at() 方法接受一个整数作为参数,返回参数指定位置的字符,支持负索引(即倒数的位置)。

const str = 'hello';str.at(1) // "e"str.at(-1) // "o"如果参数位置超出了字符串范围, at() 返回 undefined

数值的扩展

Number.isFinite(), Number.isNaN()

ES6 在Number对象上,新提供了Number.isFinite()和Number.isNaN()两个方法。

Number.isFinite()用来检查一个数值是否为有限的(finite),即不是Infinity。

Number.isFinite(15); // trueNumber.isFinite(0.8); // trueNumber.isFinite(NaN); // falseNumber.isFinite(Infinity); // falseNumber.isFinite(-Infinity); // falseNumber.isFinite('foo'); // falseNumber.isFinite('15'); // falseNumber.isFinite(true); // false

注意,如果参数类型不是数值,Number.isFinite一律返回false。

Number.isNaN()用来检查一个值是否为NaN。

Number.isNaN(NaN) // trueNumber.isNaN(15) // falseNumber.isNaN('15') // falseNumber.isNaN(true) // falseNumber.isNaN(9/NaN) // trueNumber.isNaN('true' / 0) // trueNumber.isNaN('true' / 'true') // true

如果参数类型不是NaN,Number.isNaN一律返回false

Number.parseInt(), Number.parseFloat()

ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。

// ES5的写法parseInt('12.34') // 12parseFloat('123.45#') // 123.45// ES6的写法Number.parseInt('12.34') // 12Number.parseFloat('123.45#') // 123.45   自动去调非数值

这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。

Number.parseInt === parseInt // trueNumber.parseFloat === parseFloat // true

Number.isInteger()

Number.isInteger()用来判断一个数值是否为整数。

Number.isInteger(25) // trueNumber.isInteger(25.1) // false

Math函数扩展

ES6 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用

Math.trunc()

Math.trunc 方法用于去除一个数的小数部分,返回整数部分

Math.trunc(4.1) // 4Math.trunc(4.9) // 4Math.trunc(-4.1) // -4Math.trunc(-4.9) // -4

Math.sign()

Math.sign 方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值 它会返回五种值

  • 参数为正数,返回 +1
  • 参数为负数,返回 -1
  • 参数为 0,返回 0
  • 参数为-0,返回 -0
  • 其他值,返回 NaN
Math.sign(-5) // -1Math.sign(5) // +1Math.sign(0) // +0Math.sign(-0) // -0Math.sign(NaN) // NaNMath.sign('itbaizhan')  // NaNMath.sign(undefined)  // NaN

Math.cbrt()

Math.cbrt() 方法用于计算一个数的立方根

如果一个数的立方等于 a ,那么这个数叫 a 的立方根,也称为三 次方根。也就是说,如果 x³=a ,那么 x 叫做 a 的立方根

Math.cbrt('8') // 2Math.cbrt('hello') // NaN

对数方法

ES6 新增了 4 个对数相关方法

  • 1 Math.expm1()
  • 2 Math.log1p()
  • 3 Math.log10()
  • 4 Math.log2()

双曲函数方法

ES6 新增了 6 个双曲函数方法。

  • Math.sinh(x) 返回 x 的双曲正弦(hyperbolic sine
  • Math.cosh(x) 返回 x 的双曲余弦(hyperbolic cosine
  • Math.tanh(x) 返回 x 的双曲正切(hyperbolic tangent
  • Math.asinh(x) 返回 x 的反双曲正弦(inverse hyperbolic sine
  • Math.acosh(x) 返回 x 的反双曲余弦(inverse hyperbolic cosine
  • Math.atanh(x) 返回 x 的反双曲正切(inverse hyperbolic tangent

数组扩展_扩展运算符

扩展运算符( spread )是三个点( ... )。将一个数组转为用逗号分隔的参数序列

console.log(...[1, 2, 3])// 1 2 3console.log(1, ...[2, 3, 4], 5)// 1 2 3 4 5[...document.querySelectorAll('div')]// [
,
,
]

该运算符主要用于函数调用

function push(array, items) {  array.push(...items);}function add(x, y) {  return x + y; }const numbers = [4, 38];add(...numbers) // 42

替代函数的 apply 方法 由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。

// ES5 的写法function f(x, y, z) {  // ...}var args = [0, 1, 2];f.apply(null, args);// ES6的写法function f(x, y, z) {  // ...}let args = [0, 1, 2];f(...args);

下面是扩展运算符取代apply方法的一个实际的例子,应用Math.max方法,简化求出一个数组最大元素的写法。

// ES5 的写法Math.max.apply(null, [14, 3, 77])// ES6 的写法Math.max(...[14, 3, 77])// 等同于Math.max(14, 3, 77);

扩展运算符的应用-合并数组 扩展运算符提供了数组合并的新写法。

const arr1 = ['a', 'b'];const arr2 = ['c'];const arr3 = ['d', 'e'];// ES5 的合并数组arr1.concat(arr2, arr3);// [ 'a', 'b', 'c', 'd', 'e' ]// ES6 的合并数组[...arr1, ...arr2, ...arr3]// [ 'a', 'b', 'c', 'd', 'e' ]

Array.from()

Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

下面是一个类似数组的对象,Array.from将它转为真正的数组。

let arrayLike = {    '0': 'a',    '1': 'b',    '2': 'c',    length: 3};// ES5的写法var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']// ES6的写法let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

实际应用中,常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组

    
// Array.from()把伪数组变为真的数组 function add(){ arguments = Array.from(arguments) arguments.push(50) console.log(arguments); } add(10,20,30,40); var divs = document.querySelectorAll("div"); console.log(Array.from(divs)); let arrayLike = { 0:"iwen", 1:"ime", 2:"demo", length:3 } console.log(Array.from(arrayLike)); // Array.of():把一组数变为数组 console.log(Array.of(10,20,30,40)); var arr1 = new Array(3); console.log(arr1); // [,,] var arr2 = new Array(10,20,30); console.log(arr2); // [10,20,30] console.log(Array.of(3)); // [3] console.log(Array.of(3,3,4,5)); // [3]

Array.of()

Array.of方法用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8]Array.of(3) // [3]Array.of(3).length // 1

这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。

Array() // []Array(3) // [, , ,]Array(3, 11, 8) // [3, 11, 8]

数组实例的 copyWithin()

数组实例的copyWithin()方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组

Array.prototype.copyWithin(target, start = 0, end = this.length)

它接受三个参数。

target(必需):从该位置开始替换数据。如果为负值,表示倒数。
start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。

 这三个参数都应该是数值,如果不是,会自动转为数值。

[1, 2, 3, 4, 5].copyWithin(0, 3)// [4, 5, 3, 4, 5]

数组实例的 find() 和 findIndex()

数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

[1, 4, -5, 10].find((n) => n < 0)// -5

 上面代码找出数组中第一个小于 0 的成员。

[1, 5, 10, 15].find(function(value, index, arr) {  return value > 9;}) // 10

上面代码中,find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组

数组实例的 fill()

fill方法使用给定值,填充一个数组。

['a', 'b', 'c'].fill(7)// [7, 7, 7]new Array(3).fill(7)// [7, 7, 7]

数组实例的 includes()

Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES2016 引入了该方法。

[1, 2, 3].includes(2)     // true[1, 2, 3].includes(4)     // false[1, 2, NaN].includes(NaN) // true

 数组其他方法

 // indexOf // var arr = [10,20,30]; // if(arr.indexOf(20) > -1){ //     console.log("存在"); // }else{ //     console.log("不存在"); // } // console.log(arr.includes(20)); // true // // NaN的问题 indexOf是无法验证NaN // var arr1 = [10,20,30,NaN] // console.log(arr1.indexOf(NaN)); // -1 // console.log(arr1.includes(NaN)); // true // var arr = [10,20,[30,40]]; // console.log(arr.flat()); // [10,20,30,40] // console.log(arr); // [10,20,[30,40]] // var arr1 = [10,20,[30,40,[50,60]]]; // // 可以传递参数,参数的数字代表拉平的维度 // console.log(arr1.flat(2)); // [10,20,30,40,50,60] var arr = [10,20,30,40]; console.log(arr[2]); console.log(arr.at(-3)); // 20    

对象的扩展

属性的简洁表示法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

const foo = 'bar';const baz = {foo};baz // {foo: "bar"}// 等同于const baz = {foo: foo};

上面代码中,变量foo直接写在大括号里面。这时,属性名就是变量名, 属性值就是变量值。下面是另一个例子。

function f(x, y) {  return {x, y};}// 等同于function f(x, y) {  return {x: x, y: y};}f(1, 2) // Object {x: 1, y: 2}

下面是一个实际的例子。

let birth = '2000/01/01';const Person = {  name: '张三',  //等同于birth: birth  birth,  // 等同于hello: function ()...  hello() { console.log('我的名字是', this.name); }};

属性名表达式

JavaScript 定义对象的属性,有两种方法。

// 方法一obj.foo = true;// 方法二obj['a' + 'bc'] = 123;

上面代码的方法一是直接用标识符作为属性名,方法二是用表达式作为属性名,这时要将表达式放在方括号之内。 ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。

let propKey = 'foo';let obj = {  [propKey]: true,  ['a' + 'bc']: 123};

方法的 name 属性

函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。

const person = {  sayName() {    console.log('hello!');  },};person.sayName.name   // "sayName"

上面代码中,方法的name属性返回函数名(即方法名)

对象的扩展运算符

《数组的扩展》一章中,已经介绍过扩展运算符(...)。ES2018 将这个运算符引入了对象。 对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };x // 1y // 2z // { a: 3, b: 4 }
   var job = ["itbaizhan","sxt"] var user = {     name:"iwen",     age:20,     sex:"男",     job,     sayHello(){ console.log("Hello");      } } user.sayHello() function getPosition(){     var x = 100;     var y = 100;     return{  x,  y     } } var result = getPosition() console.log(result.x); console.log(result.y); let propKey = 'itbaizhan'; var info = {     address:"地址",     [propKey]:"haha" // 属性名表达式 } console.log(info[propKey]); var hello = {a:"a",b:"b"}; console.log({...hello}); console.log({...{b:1}, a: 1});     

对象的新增方法-Object.is()

ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。 ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

Object.is('foo', 'foo')// trueObject.is({}, {})// false

不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

+0 === -0 //trueNaN === NaN // falseObject.is(+0, -0) // falseObject.is(NaN, NaN) // true

对象的新增方法-Object.assign()

Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

const target = { a: 1 };const source1 = { b: 2 };const source2 = { c: 3 };Object.assign(target, source1, source2);target // {a:1, b:2, c:3}

同名属性的替换 对于这种嵌套的对象,一旦遇到同名属性, Object.assign() 的处理方法是替换,而不是添加

const target = { a: "hello" }const source = { a: "world" }console.log(Object.assign(target, source));

数组的处理 Object.assign() 可以用来处理数组,但是会把数组视为对象

Object.assign([1, 2, 3], [4, 5])// [4, 5, 3]

Object.assign() 把数组视为属性名为 0 1 2 的对象,因此源数组的 0号属性 4 覆盖了目标数组的 0 号属性 1

 console.log(Object.is("hello","hello")); // true console.log(Object.is("hello","world")); // false console.log(NaN === NaN); // false console.log(+0 === -0);  // true console.log(Object.is(NaN,NaN)); // true console.log(Object.is(+0,-0)); // false var obj1 = {     name:"iwen",     age:10 } var obj2 = {     age:20 } var obj3 = {     sex:"男" } console.log(Object.assign(obj1,obj2,obj3)); var arr1 = [1,2,3] //  var arr2 = [4,5] console.log(Object.assign(arr1,arr2));// [4,5,3] let {keys,values,entries} = Object; // 对象的解构赋值 var user = {     username:"iwen",     password:"123" } for(let item of keys(user)){     console.log(item); // username   password } for(let item of values(user)){     console.log(item); // iwen  123 } for(let item of entries(user)){     console.log(item); //  ['username', 'iwen']  ['password', '123'] }    

运算符的扩展

指数运算符

ES2016 新增了一个指数运算符( **

2 ** 2 // 42 ** 3 // 8

这个运算符的一个特点是右结合,而不是常见的左结合。多个指数运算符连用时,是从最右边开始计算的

// 相当于 2 ** (3 ** 2)2 ** 3 ** 2// 512

指数运算符可以与等号结合,形成一个新的赋值运算符( **=

let a = 1.5; a **= 2;// 等同于 a = a * a;let b = 4; b **= 3;// 等同于 b = b * b * b;

链判断运算符

编程实务中,如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。比如,读取 message.body.user.firstName 这个属性,安全的写法是写成下面这样

// 错误的写法const  firstName =message.body.user.firstName || 'default';// 正确的写法const firstName = (message  && message.body  && message.body.user  && message.body.user.firstName) ||'default';

这样的层层判断非常麻烦,因此 ES2020 引入了 链判断运算符 ”(optional chaining operator ?. ,简化上面的写法

const firstName = message?.body?.user?.firstName || 'default';

Null 判断运算符

读取参数的时候,如果某个参数的值是 null undefined ,有时候需要为它们指定默认值。常见做法是通过 || 运算符指定默认值。

function add(x,y){    x = x || 100;    y = y || 100;    console.log(x+y);}add(0,0); // 200

上面的三行代码都通过 || 运算符指定默认值,但是这样写是错的。开发者的原意是,只要属性的值为 null undefined ,默认值就会生效,但是属性的值如果为空字符串或 false 0 ,默认值也会生效 为了避免这种情况,ES2020 引入了一个新的 Null 判断运算符 ?? 。 它的行为类似 || ,但是只有运算符左侧的值为 null undefined 时,才 会返回右侧的值。

function add(x,y){    x = x ?? 100;    y = y ?? 100;    console.log(x+y);}add(0,0);

逻辑赋值运算符 ES2021 引入了三个新的逻辑赋值运算符( logical assignment operators),将逻辑运算符与赋值运算符进行结合

// 或赋值运算符x ||= y// 等同于x || (x = y)// 与赋值运算符x &&= y// 等同于x && (x = y)// Null 赋值运算符x ??= y// 等同于x ?? (x = y)

它们的一个用途是,为变量或属性设置默认值

// 老的写法user.id = user.id || 1;// 新的写法user.id ||= 1;

user.id 属性如果不存在,则设为 1 ,新的写法比老的写法更紧凑一些

函数的扩展

函数参数的默认值

ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。

function log(x, y) {  y = y || 'World';  console.log(x, y);}log('Hello') // Hello Worldlog('Hello', 'China') // Hello Chinalog('Hello', '') // Hello World

上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。

为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值。

if (typeof y === 'undefined') {  y = 'World';}

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x, y = 'World') {  console.log(x, y);}log('Hello') // Hello Worldlog('Hello', 'China') // Hello Chinalog('Hello', '') // Hello

可以看到,ES6 的写法比 ES5 简洁许多,而且非常自然。下面是另一个例子

function Point(x = 0, y = 0) {  this.x = x;  this.y = y;}const p = new Point();p // { x: 0, y: 0 }

rest 参数

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function add(...values) {  let sum = 0;  for (var val of values) {    sum += val;  }  return sum;}add(2, 5, 3) // 10

上面代码的add函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数

注意, rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错

function add(a, ...b,c) {} // 报错

函数的 length 属性,不包括 rest 参数

function add(a, ...b) {}console.log(add.length); // 1

严格模式 ES5 开始,函数内部可以设定为严格模式

function doSomething(a, b) {  'use strict';  // code}

ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

// 报错function doSomething(a, b = a) {  'use strict';  // code}

name 属性

函数的name属性,返回该函数的函数名。

function foo() {}foo.name // "foo"

参数默认值的位置  

function add(x = 1, y) {    console.log(x+y);}add(10) // NaN

函数的 length 属性

指定了默认值以后,函数的 length 属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后, length 属性将失真

function fn1(x){}console.log(fn1.length); // 1function fn2(x=1){}console.log(fn2.length); // 0

应用 利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误

function missingParameter() {    throw new Error('Missing parameter');}function add(x = missingParameter()) {    console.log(x);}add() // Missing parameter

箭头函数

ES6 允许使用“箭头”(=>)定义函数

var f = v => v;// 等同于var f = function (v) {  return v;};

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分

var f = () => 5;// 等同于var f = function () { return 5 };var sum = (num1, num2) => num1 + num2;// 等同于var sum = function(num1, num2) {  return num1 + num2;};

如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回

var sum = (num1, num2) => { return num1 + num2; }

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错

// 报错let getTempItem = id => { id: id, name: "Temp" };// 不报错let getTempItem = id => ({ id: id, name: "Temp" });

箭头函数的一个用处是简化回调函数。

// 正常函数写法[1,2,3].map(function (x) {  return x * x;});// 箭头函数写法[1,2,3].map(x => x * x);

使用注意点 箭头函数有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的

function foo() {  setTimeout(() => {    console.log('id:', this.id);  }, 100);}var id = 21;foo.call({ id: 42 });// id: 42

 箭头函数里面根本没有自己的 this ,而是引用外层的 this

var name = "itbaizhan"var user = {    name:"iwen",    getName(){        setTimeout(() =>{            console.log(this.name); // iwen       })   }}user.getName()

Symbol

基础知识   ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法 (mixin 模式),新方法的名字就有可能与现有方法产生冲突。如 果有一种机制,保证每个属性的名字都是独一无二的就好了,这样 就从根本上防止属性名的冲突。这就是 ES6 引入 Symbol 的原因。   ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值。它属于 JavaScript 语言的数据类型之一,其他数据类型是: undefined null 、布尔值( Boolean )、字符串( String )、数值 Number )、对象( Object

let s = Symbol();typeof s// "symbol"

注意, Symbol 函数前不能使用 new 命令,否则会报错。这是因为 生成的 Symbol 是一个原始类型的值,不是对象。也就是说, 由于 Symbol 值不是对象,所以不能添加属性。基本上,它是 一种类似于字符串的数据类型。

Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分

 let s1 = Symbol('itbaizhan'); let s2 = Symbol('sxt'); s1 // Symbol(itbaizhan) s2 // Symbol(sxt)

s1 s2 是两个 Symbol 值。如果不加参数,它们在控制台的输出都是 Symbol() ,不利于区分。有了参数以后,就等于为它们加上了描述,输出的时候就能够分清,到底是哪一个值

注意, Symbol 函数的参数只是表示对当前 Symbol 值的描述,因 此相同参数的 Symbol 函数的返回值是不相等的。

// 没有参数的情况let s1 = Symbol();let s2 = Symbol();s1 === s2 // false// 有参数的情况let s1 = Symbol('itbaizhan');let s2 = Symbol('itbaizhan');s1 === s2 // false

s1 s2 都是 Symbol 函数的返回值,而且参数相同,但是它们是不相等的

实例属性description

ES2019 提供了一个实例属性 description ,直接返回 Symbol 的描述

const sym = Symbol('itbaizhan');sym.description // "itbaizhan"

作为属性名的 Symbol 由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖

let mySymbol = Symbol("name");var user = {    name:"iwen"}var info = {   [mySymbol]:"itbaizhan", }var newUser = Object.assign(user,info)console.log(newUser[mySymbol]);

属性名的遍历

Symbol 作为属性名,遍历对象的时候,该属性不会出现在 for...in for...of 循环中,也不会被 Object.keys() 返回但是,它也不是私有属性,有一个 Object.getOwnPropertySymbols() 方法,可 以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol

let mySymbol = Symbol("age");var user = {    name:"iwen",   [mySymbol]:20}for(let item in user){    console.log(item); // name}const objectSymbols = Object.getOwnPropertySymbols(user);console.log(objectSymbols); // [Symbol(age)]

Symbol.for()

有时,我们希望重新使用同一个 Symbol 值, Symbol.for() 方法可以做到这一点。

let s1 = Symbol.for('itbaizhan');let s2 = Symbol.for('itbaizhan');console.log(s1 === s2); // true
 var user = {     name:"iwen" } /*     Symbol:  1. 新的原始数据类型:字符串和数字  2. 他不能使用new关键字去生成  3. 他是独一无二的  4. Symbol可以接受一个参数,参数的目的是为了方便识别不同的Symbol  5. Symbol的参数只是描述,哪怕描述内容相同,两个值也是不同的  6. 可以使用description获取一个Symbol的描述  7. Symbol作为对象的属性名,可以保证不重复  8. Symbol作为对象的属性名,读取的时候要用数组的方式  9. Symbol作为对象的属性名,读取的时候只能使用getOwnPropertySymbols()  10. 如果要创建重复的Symbol的值,需要使用Symbol.for() */ var s = Symbol(); console.log(typeof s); // symbol // number string object var s1 = Symbol("itbaizhan"); var s2 = Symbol("sxt"); console.log(s1,s2); var s3 = Symbol("iwen"); var s4 = Symbol("iwen"); console.log(s3 === s4); // false console.log(s1.description); // itbaizhan var info2Symbol = Symbol("name") var info1 = {     name:"iwen" } var info2 = {     [info2Symbol]:"ime" } var newInfo = Object.assign(info1,info2); // {name:'ime', [info2Symbol]:"ime"} console.log(newInfo.name); // iwen // 用数组的方式读取Symbol属性 console.log(newInfo[info2Symbol]); // ime var info3Symbol = Symbol("job") var info3 = {     name:"iwen",     age:20,     [info3Symbol]:"itbaizhan" } for(let key in info3){     console.log(key); } console.log(Object.getOwnPropertySymbols(info3)); // Symbol("job") var itSymbol1 = Symbol.for("itbaizhan") var itSymbol2 = Symbol.for("itbaizhan") console.log(itSymbol1 === itSymbol2); // true    

神唱ktv下载