es6

let命令

  1. let命令与var(全局范围内有效)命令类似,但let命令申明的变量只在所在代码块有效。
  2. let命令不存在变量提升,所以会报错;
  3. 暂时性死区:let命令申明变量之前,变量不可用
  • 也说明了typeof并不是完全安全。若去掉申明语句 let则正确。
typeof x; // ReferenceError
let x;
  • y赋值给x,但是此时y并未定义,所以报错
function bar(x = y, y = 2) {
  return [x, y];
}

bar(); // 报错
function bar(x = 2, y = x) {
  return [x, y];
}
bar(); // [2, 2]
  • let申明的变量只在当前代码块有效,let申明的i,i只在本轮循环中有效,所以,每轮循环都重新定义i。若是重新定义i又怎么知道上一轮i的值?这是由于javascript引擎内部记住上一轮的值,在本轮初始化时,在上一轮的基础上计算。
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
变量不可在同一区域重复申明。

  1. 块作用域
  • 块级作用域允许任意嵌套;
  • 外层定义域无法获取内层定义域变量;
  1. 块级定义域内函数的定义相当于let申明,对作用域外没有影响。
    在浏览器的 ES6 环境中,块级作用域内声明的函数,行为类似于var声明的变量。

    ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。
// 不报错
'use strict';
if (true) {
  function f() {}
}

// 报错
'use strict';
if (true)
  function f() {}
function f() { console.log('I am outside!'); }

(function () {
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }

  f();
}());
// Uncaught TypeError: f is not a function

上面的代码在符合 ES6 的浏览器中,都会报错,因为实际运行的是下面的代码。

function f() { console.log('I am outside!'); }
(function () {
  var f = undefined;
  if (false) {
    function f() { console.log('I am inside!'); }
  }

  f();
}());
// Uncaught TypeError: f is not a function

应该写成函数表达式,而不是函数声明语句。

// 函数声明语句
{
  let a = 'secret';
  function f() {
    return a;
  }
}

// 函数表达式
{
  let a = 'secret';
  let f = function () {
    return a;
  };
}

const

  1. const申明一个只读的常量。一旦申明,值不再改变。
  2. const申明时必须赋值,否则报错。
  3. cosnt是块级作用域,没有申明提升。
  4. const存在暂时性死区。
  5. 不可重复定义。
  • 本质:const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
const a = [];
a.push('Hello'); // 可执行
a.length = 0;    // 可执行
a = ['Dave'];    // 报错

冻结对象

const foo = Object.freeze({});

// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;

es6申明变量的方法

  1. var,function()
  2. let, const
  3. import,class

顶层对象

  1. 顶层对象,在浏览器环境中是window,在node中是Global。
    es5中,顶层对象与全局变量是等价的。
  2. es6规定:ES6 为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性

Global对象

es5在各种实现里面是不统一的,所以
现在有一个提案,在语言标准的层面,引入global作为顶层对象。也就是说,在所有环境下,global都是存在的,都可以从它拿到顶层对象。

垫片库system.global模拟了这个提案,可以在所有环境拿到global

// CommonJS 的写法
require('system.global/shim')();

// ES6 模块的写法
import shim from 'system.global/shim'; shim();

上面代码可以保证各种环境里面,global对象都是存在的。

// CommonJS 的写法
var global = require('system.global')();

// ES6 模块的写法
import getGlobal from 'system.global';
const global = getGlobal();

上面代码将顶层对象放入变量global

变量的解构赋值

  1. 数组的解构赋值:从数组中提取值,赋值给变量。
    解构不成功,变量的值就等于undefined。
let [foo] = [];
let [bar, foo] = [1];

以上两种情况都属于解构不成功,foo的值都会等于undefined。

  • 数组的不完全解构,即等号左边只匹配右边数组的一部分。
  • 等号右边不是数组(不可遍历的结构),将会报错。
    因为等号右边的值,要么转为对象以后不具备 Iterator 接口(前五个表达式),要么本身就不具备 Iterator 接口(最后一个表达式)。
// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
  • set结构也可以使用数组解构赋值。
  • 实际,只要是具有iterator接口的数据结构都可以使用数组解构。
function* fibs() {
  let a = 0;
  let b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5

上面代码中,fibs是一个 Generator 函数,原生具有 Iterator 接口。解构赋值会依次从这个接口获取值。

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

默认值可以引用解构赋值的其他变量,但该变量必须已经声明。

let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2];    // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     // ReferenceError: y is not defined
  1. 对象的解构赋值
  • 数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
  • 对象的属性名和变量名不一致可以采用这种形式:
    let { foo: foo1, bar: bar1 } = { foo: "aaa", bar: "bbb" };
    对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
  • 下面代码有三次解构赋值,分别是对loc、start、line三个属性的解构赋值。注意,最后一次对line属性的解构赋值之中,只有line是变量,loc和start都是模式,不是变量。
const node = {
  loc: {
    start: {
      line: 1,
      column: 5
    }
  }
};

let { loc, loc: { start }, loc: { start: { line }} } = node;
line // 1
loc  // Object {start: Object}
start // Object {line: 1, column: 5}
  • 对象也可以指定默认值,只有当匹配恒等于undefined时,默认值生效。
  • 数组本质是特殊的对象,可以使用对象的解构赋值。

字符串解构赋值

  1. 字符串此时被转换为一个类似数组的对象。
  2. 类似数组的对象都有一个length属性,可以解构赋值。

数值和布尔值解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

let {toString: s} = 123;
s === Number.prototype.toString // true

let {toString: s} = true;
s === Boolean.prototype.toString // true

上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError

函数的解构赋值

function move({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

原括号问题

不能使用原括号的解构赋值。

  1. 变量声明语句,模式不能使用圆括号。
// 全部报错
let [(a)] = [1];

let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};

let { o: ({ p: p }) } = { o: { p: 2 } };
  1. 函数参数也属于变量声明,不能使用圆括号。
// 报错
function f([(z)]) { return z; }
// 报错
function f([z,(x)]) { return x; }

3.赋值语句的模式(不能扩住模式,部分或整体)

({ p: a }) = { p: 42 };
([a]) = [5];
上面代码将整个模式放在圆括号之中,导致报错。

// 报错
[({ p: a }), { x: c }] = [{}, {}];
上面代码将一部分模式放在圆括号之中,导致报错。

可以使用圆括号的情况只有一种:

赋值语句的非模式部分,可以使用圆括号。

[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确

解构赋值的应用

  1. 交换俩数数值
  2. 函数的参数
  3. 遍历map
  4. 获取json数据
  5. 函数的默认值

字符串的扩展

字符的unicode表示法
  1. \uxxxx表示一个字符,xxxx表示unicode码点。
  2. 只限于码点在\u0000~\uffff之间的字符,超出范围的用俩个双字节表示。
  3. 直接在\u后面跟上超过0xFFFF的数值(比如\u20BB7),JavaScript 会理解成\u20BB+7。由于\u20BB是一个不可打印字符,所以只会显示一个空格,后面跟着一个7。
"\uD842\uDFB7"
// ""

"\u20BB7"
// " 7"
  1. **es6表示,只要将码点放入大括号,就能正确解读该字符。
"\u{20BB7}"
// ""

"\u{41}\u{42}\u{43}"
// "ABC"

let hello = 123;
hell\u{6F} // 123

'\u{1F680}' === '\uD83D\uDE80'
// true
codepointAt

由于字符按照utf-16来存储,每个字符固定2个字节,4个字节存储的字符js默认是俩个字符。

var s = "";

s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271

上面代码中,汉字“”(注意,这个字不是“吉祥”的“吉”)的码点是0x20BB7,UTF-16 编码为0xD842 0xDFB7(十进制为55362 57271),需要4个字节储存。对于这种4个字节的字符,JavaScript 不能正确处理,字符串长度会误判为2,而且charAt方法无法读取整个字符,charCodeAt方法只能分别返回前两个字节和后两个字节的值。
es6提供了codePointAt方法,能正确识别4个字节存储的字符,并返回码点。

let s = 'a';

s.codePointAt(0) // 134071
s.codePointAt(1) // 57271

s.codePointAt(2) // 97

codePointAt会返回10进制的码点,需要16进制可以通过.tostring(16)来转换,对于2个字节存储的字符charCodeAt和codePointAt返回结果相同。
但是codePointAt的参数任然是错误的,可以通过for-of 来循环。
判断一个字符是2字节存储还是4字节存储

function is32Bit(c) {
  return c.codePointAt(0) > 0xFFFF;
}

is32Bit("") // true
is32Bit("a") // false

String.fromCodePoint()

es5提供了方法String.fromCharCode()方法,用于从码点返回字符,但是不能识别32位的utf-16字符(unicode>0xffff)

String.fromCharCode(0x20BB7)
// "ஷ"

上面代码中,String.fromCharCode不能识别大于0xFFFF的码点,所以0x20BB7就发生了溢出,最高位2被舍弃了,最后返回码点U+0BB7对应的字符,而不是码点U+20BB7对应的字符。

es6提供了String.fromCodePoint()方法,可以识别32位的字符。
String.fromCodePoint(0x20BB7)
// ""
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'
// true
  1. 如果String.fromCodePoint()方法含有多个参数,则他们会被合并为一个字符串返回。
  2. 注意:fromCodePoint()方法定义在String对象上,而codePointAt()方法则被定义在字符串的实例对象上。

字符串遍历器接口

es6为字符串遍历提供了遍历器接口,可以通过for-of来循环遍历字符串。

for (let codePoint of 'foo') {
  console.log(codePoint)
}
// "f"
// "o"
// "o"

除了遍历字符串,这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。

let text = String.fromCodePoint(0x20BB7);

for (let i = 0; i < text.length; i++) {
  console.log(text[i]);
}
// " "
// " "

for (let i of text) {
  console.log(i);
}
// ""

上面代码中,字符串text只有一个字符,但是for循环会认为它包含两个字符(都不可打印),而for...of循环会正确识别出这一个字符。

at()方法

es5为字符串提供了CharAt()方法,用来返回指定位置的字符。不能识别超过(0xffff)的字符。
目前有一个提案,用字符串实例的at()方法来返回大于0xffff的字符。

'abc'.at(0) // "a"
''.at(0) // ""

normalize()

合成字符(在语义和视觉上等价),javascript不能识别。
es6规定normalize,将字符的不同表示方法统一为相同样式,为unicoded标准化。
4个参数:

  1. NFC:在标准等价基础上,返回多个字符合成的字符。
  2. NFD:在标准等价基础上,返回分解合成字符的简单字符。
  3. NFKC:在兼容等价合成,返回合成字符。
  4. NFKD:在兼容等价合成,返回分解合成字符的多个简单字符。
    标准等价合成:在语义和视觉上等价。
    兼容等价合成:在语义等价,视觉不等价。

    注意:normalize不能识别3个或3个以上的合成字符,只能用正则表达式,unicoded 区间判断。

用来确定字符串是否包含另一字符串

  1. javascript中的indexOf()方法
  2. es6中的方法:
  • includes():返回一个布尔值,表示是否找到参数字符串。
  • startsWith():返回一个布尔值,表示是否原字符串头部。
  • endsWith():返回一个布尔值,是否含在原字符串末尾。
    第二个参数n,表示开始搜索的位置。
    endsWith()针对前n个字符,其他表示从第n个位置开始到字符串结束。

repeat()返回一个新字符串,参数n表示将原字符串重复几次

  1. 参数是小数会向小取整;
  2. 参数是负数或者Infinity,报错;
  3. 参数在0~-1之间,会向下取整为-0,视为0;
  4. NaN等价与0;
  5. 参数是字符串会转换为数字。

padStart(),padEnd()

es2017引入字符串补全功能:如果字符串的长度小于指定最小长度,会在头部尾部补全。

  1. padStart():头部补全;
  2. padEnd():尾部补全;
  • 接受俩个参数,一个为字符串的最小长度,第二个是补全字符串;
  • 若原字符串的长度大于等于最小长度,则返回原字符串;
  • 补全字符串和原字符串的长度之和大于最小长度,则截取超出位数的补全字符串。
  • 省略第二个参数,默认使用空格补全;
    用途:1. 为数值补全指定位数;
  1. 提示字符串格式
'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"

'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

matchAll()

matchAll方法返回一个正则表达式在当前字符串的所有匹配,

模板字符串

传统的 JavaScript 语言,输出模板通常是这样写的。

$('#result').append(
  'There are ' + basket.count + ' ' +
  'items in your basket, ' +
  '' + basket.onSale +
  ' are on sale!'
);

上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。

$('#result').append(`
  There are ${basket.count} items
   in your basket, ${basket.onSale}
  are on sale!
`);

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

// 普通字符串
`In JavaScript '\n' is a line-feed.`

// 多行字符串
`In JavaScript this is
 not legal.`

console.log(`string text line 1
string text line 2`);

// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
  • 在模板字符串中使用反引号,需要转义;
  • 反引号表示字符串,会保留空格和换行,缩进,不想保留可以调用.trim();
  • 模板字符串嵌入变量,引用变量需要将变量包含在${}之中;
  • 大括号内可以放入javascript语句,运算,函数;
  • 如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的toString方法。

你可能感兴趣的:(es6)