String

字符的Unicode 表示法

  1. // JavaScript允许采用\uxxxx形式表示一个字符,其中“xxxx”表示字符的码点。
  2. "\u0061"
  3. // "a"
  4. // 但是,这种表示法只限于\u0000——\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表达。
  5. "\uD842\uDFB7"
  6. // "𠮷"
  7. "\u20BB7"
  8. // " 7"
  9. // es6
  10. "\u{20BB7}"
  11. // "𠮷"
  12. "\u{41}\u{42}\u{43}"
  13. // "ABC"
  14. let hello = 123;
  15. hell\u{6F} // 123
  16. '\u{1F680}' === '\uD83D\uDE80'
  17. // true
  • codePointAt 处理返回占用资格字节存储的字符
  • fromCodePoint 处理32位UTF-16 的字符
  • 字符串可迭代
  • at 可以用来识别Unicode编号大于 0xFFFF 的字符,返回正确的字符
  • normalize 用来将字符的不同表示方法统一为同样的形式,称之为Unicode正规化

以下玩个方法都可以输入第二个参数,表示起始位置

  • includes 返回一个bool, 表示是否找到了参数字符串
  • startsWith 返回一个bool, 表示参数字符串是否正在源字符串的头部
  • endsWith 返回一个bool, 表示参数字符串是否存在源字符串的尾部 ```javascript var s = ‘Hello world!’;

s.startsWith(‘Hello’) // true s.endsWith(‘!’) // true s.includes(‘o’) // true

s.startsWith(‘world’, 6) // true s.endsWith(‘Hello’, 5) // true s.includes(‘Hello’, 6) // false

  1. - repeat <func> 返回一个新字符串, 表示将源字符串重复n
  2. es7 推出了字符串的补全方法。
  3. - padStart <func> 从源字符串的头部填充
  4. - padEnd <func> 从源字符串的尾部填充
  5. ```javascript
  6. 'x'.padStart(5, 'ab') // 'ababx'
  7. 'x'.padStart(4, 'ab') // 'abax'
  8. 'x'.padEnd(5, 'ab') // 'xabab'
  9. 'x'.padEnd(4, 'ab') // 'xaba'

模板字符串 …

Number

es6 提供了二进制(0b)和八进制(0B)数值的新的写法,

0b111110111 === 503 // true
0o767 === 503 // true

对number 新增的方法

// 用来检查一个数值是否有限
.isFinite(true) // true
.isFinite(Infinity) // false

// 用来检查一个值是否为NaN. 是原来全局isNaN的更稳妥的版本
.isNaN(NaN) // true
.isNaN(1212) // false

// 转换为number, 与全局的方法没有区别
.parseInt(`12.32`) // 12
.parseFloat(`12.23`) // 12.23

// 判断一个值是否为整数,整数和浮点数在这是一致的
.isInteger(23) // true
.isInteger(25.1) || .isInteger(`foo`) // false

// 一个极小的常量 
Number.EPSILON

// 一个数值是否为安全整数
.isSafeInteger()

Array

from

该 转换为数组,只要部署了Iterator接口的数据结构都可被转换

扩展运算符背后调用的是遍历器接口(Symbol.iterator), 如果一个对象没有部署这个接口,就无法转换。 .from 方法只要lenth属性就可以被转为数组

第二个参数类似于数组的map方法

// 转换为数组,只要部署了Iterator接口的数据结构都可被转换
Array.from('hello') // ['h', 'e', 'l', 'l', 'o']

// 扩展运算符背后调用的是遍历器接口(Symbol.iterator), 如果一个对象没有部署这个接口,就无法转换
// .from 方法只要lenth属性就可以被转为数组,
[...arguments] 

// 第二个参数类似于数组的map方法
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);

of

将一组值,转换为数组,(主要弥补的Array的不足)

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

// of
Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]

copyWithin

用于对一个数组的赋值操作
它接受三个参数。

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

    find & findIndex

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

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

<a name="riVSM"></a>
#### fill
该方法使用给定的值,填充一个数组
<a name="Rk8X3"></a>
#### entries & keys & values & includes
```javascript
for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

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

数组的空位

  • forEach(), filter(), every()some()都会跳过空位。
  • map()会跳过空位,但会保留这个值
  • join()toString()会将空位视为undefined,而undefinednull会被处理成空字符串 ```javascript // forEach方法 [,’a’].forEach((x,i) => console.log(i)); // 1

// filter方法 [‘a’,,’b’].filter(x => true) // [‘a’,’b’]

// every方法 [,’a’].every(x => x===’a’) // true

// some方法 [,’a’].some(x => x !== ‘a’) // false

// map方法 [,’a’].map(x => 1) // [,1]

// join方法 [,’a’,undefined,null].join(‘#’) // “#a##”

// toString方法 [,’a’,undefined,null].toString() // “,a,,”

// 在es6 Array.from([‘a’,,’b’]) // [ “a”, undefined, “b” ]

[…[‘a’,,’b’]] // [ “a”, undefined, “b” ]

[,’a’,’b’,,].copyWithin(2,0) // [,”a”,,”a”]

new Array(3).fill(‘a’) // [“a”,”a”,”a”]

let arr = [, ,]; for (let i of arr) { console.log(1); } // 1 // 1

entries()、keys()、values()、find()和findIndex()会将空位处理成undefined
```javascript
// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]

// keys()
[...[,'a'].keys()] // [0,1]

// values()
[...[,'a'].values()] // [undefined,"a"]

// find()
[,'a'].find(x => true) // undefined

// findIndex()
[,'a'].findIndex(x => true) // 0

Object

  • 对象简介表示法, {a,b}}
  • getter 和setter {get [] (){}, set [] (){}}
  • 属性表达式 { ["name"]:"张大炮"}
  • .is 比较两个值是否相等
  • assign 合并一个或多个对象到源对象,返回新的对象,浅拷贝
  • 扩展运算符 {...}
  • 方法的name 属性
    • bind方法创造的函数,name属性会返回bound加上原函数的名字
    • function 构造函数创造的函数, name属性返回 anonymouns ```javascript var person = { sayName() { console.log(this.name); }, get firstName() { return “Nicholas”; } };

person.sayName.name // “sayName” person.firstName.name // “get firstName”

(new Function()).name // “anonymous”

var doSomething = function() { // … }; doSomething.bind().name // “bound doSomething”

const key1 = Symbol(‘description’); const key2 = Symbol(); let obj = { key1 {}, key2 {}, }; obj[key1].name // “[description]” obj[key2].name // “”


<a name="W1gW3"></a>
## function
<a name="xWzt9"></a>
#### 默认参数
**只有undefined有默认参数的效果** 
```javascript
// 之前的写法
function log(x, y) {
  y = y || 'World';
  console.log(x, y);
}

function log(x, y = "world") {
  console.log(x, y);
}

结构赋值

function foo({x, y = 5}) {
  console.log(x, y);
}

foo({}) // undefined, 5
foo({x: 1}) // 1, 5
foo({x: 1, y: 2}) // 1, 2
foo() // TypeError: Cannot read property 'x' of undefined

参数默认值的位置

function f(x = 1, y) {
  return [x, y];
}

f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 报错
f(undefined, 1) // [1, 1]

函数的length属性

在之前函数的length会返回该函数的参数个数
指定了默认值的参数后,该参数不会被length属性计算,length将失真

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

rest参数

function add(...values) {
  let sum = 0;

  for (var val of values) {
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

严格模式

在es5开始,函数内部可以设定为严格模式
在es6,函数参数只要使用了默认值,结构赋值,扩展运算符,函数内部不能显式的指定为严格模式。
因为,函数内部的严格模式,同时适用于函数体代码和函数参数代码,函数执行的时候,先执行函数参数代码,然后再执行函数体代码。这样就有一个不合理的地方,只有从函数体代码之中,才能知道参数代码是否应该以严格模式执行,但是参数代码却应该先于函数体代码执行,虽然可以先解析函数体代码,再执行参数代码,但是这样无疑就增加了复杂性。因此,标准索性禁止了这种用法.

箭头函数

const fn = v => v;

// 需要返回对象时,用括号包括,数组也是
var getTempItem = id => ({ id: id, name: "Temp" });
  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
  • 不可以使用yield命令,因此箭头函数不能用作Generator函数
    绑定this
    箭头函数可以绑定this对象,大大减少了显式绑定this对象的写法
    ES7提出了“函数绑定”(function bind)运算符,用来取代callapplybind调用
    由于双冒号运算符返回的还是原对象,因此可以采用链式写法 ```javascript // rust? foo::bar; // 等同于 bar.bind(foo);

foo::bar(…arguments); // 等同于 bar.apply(foo, arguments);

const hasOwnProperty = Object.prototype.hasOwnProperty; function hasOwn(obj, key) { return obj::hasOwnProperty(key); }

// 默认绑定对象 var method = obj::obj.foo; // 等同于 var method = ::obj.foo;

let log = ::console.log; // 等同于 var log = console.log.bind(console);

<a name="Li518"></a>
#### 尾调用
一个函数式编程的一个重要概念,指的是某个函数的最后一步是调用另一个函数(不是指在函数体的最后一行,是最后一步)
<a name="Ch1TO"></a>
#### 尾递归
函数调用自身,称为递归,如果在尾调用自身,就称为尾递归
<a name="XlYfd"></a>
#### 函数参数的尾逗号
没啥用,给版本管理系统看的
<a name="g16OP"></a>
## Symbol
一个新的原始数据类型,表示独一无二的指。
<a name="Qv3UB"></a>
#### 迭代
symblo 作为属性名,该属性不会出现在 `for...in` 、 `for...of` 、 `Object.keys` 、 `Object.getOwnPropertyNames` 、 `JSON.stringify` 返回,但是不是私有属性,和neumble无关,可以被 `Objct.getOwnPropertySymblos` 方法获取指定对象所有Symbol属性名。可以被 `Reflect.ownKeys` 方法返回所有类型的键名,常规的和Symbol键名
<a name="HDWQw"></a>
#### for
使用.for生成的symbol值,会被登记在全局环境中供搜索<br />for不会每次调用返回一个新的symbol值,会先检查key是否存在,如果不存在才会新建一个值。
```javascript
Symbol.for("bar") === Symbol.for("bar")
// true

Symbol("bar") === Symbol("bar")
// false

keyFor

返回一个已登记的Symbol类型的值
不会被登记,不会被搜索

let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

let foo = Symbol.for("foo1");
let f1 = Symbol.keyFor(foo);

console.log(f1 === "foo1");

内置的属性

  • hasInstance 指定一个方法。在使用instanceof 运算符时会被调用
  • isConcatSpeaedable 指定一个bool, 表示该对象使用 .concat 时,是否可以展开
  • species 指定一个方法,该对象所为构造函数创造实例,会被调用
  • match 指定一个函数,当执行 str.match(obj) 时,如果该属性存在,会被调用,返回该方法的的返回值
  • replace 指定一个方法, 当 string.replace 对调用时,会返回该方法的返回值
  • search 指定一个方法,当该对象被 string.search 调用时,会返回该方法的返回值
  • split 指定一个方法, 当 string.split 被调用时,返回该方法的返回值
  • iterator 一个对象的属性,指定该对象的默认遍历器方法
  • toStringTag 指定一个方法,在该对象调用 toString 如果这个属性存在,会出现在tostring的返回值中表示对象的类型。 ```javascript ({[Symbol.toStringTag]: ‘Foo’}.toString()) // “[object Foo]”

class Collection { get Symbol.toStringTag { return ‘你好’; } } var x = new Collection(); Object.prototype.toString.call(x) // “[object 你好]”


- toPrmitive 指定一个方法,该对象被转为原始类型的值时,被调用,返回该对象对应的原始类型值
```javascript
// Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。

Number // 该场合需要转成数值
String // 该场合需要转成字符串
Default// 该场合可以转成数值,也可以转成字符串
  • unscopables 指定一个对象,该对象指定了使用with关键字时,那些属性会被with环境排除

Set & Map

Set

es6 提供新的数据结构Set,类似于数组,但是成员的值都是唯一的,所以具有唯一性
在Set加入值的时候,set不会发生类型转换。判断两个值是否相同使用的算法叫做 Same-value-equality ,类似于精确相等运算符

const seto = new Set([
  { a: 10 },
  { a: 10 }
]);
console.log(seto);

const setal = new Set([
  [1, 2],
  [1, 2]
])
console.log(setal);

let f1 = Symbol("foo"), f2 = Symbol("foo"), f3 = Symbol("foo");
const sym = new Set([
  f1, f2, f3
])
sym.forEach((a, b) => {
  console.log(a)
})

Set(2) {{…}, {…}}
Set(2) {Array(2), Array(2)}
3~ Symbol(foo)

属性、方法、可迭代

Set.prototype.constructor // 构造函数,默认就是Set函数。
Set.prototype.size // 返回Set实例的成员总数

add(value)// 添加某个值,返回Set结构本身。
delete(value)// 删除某个值,返回一个布尔值,表示删除是否成功。
has(value)// 返回一个布尔值,表示该值是否为Set的成员。
clear() // 清除所有成员,没有返回值

keys() // 返回键名的遍历器
values() // 返回键值的遍历器
entries() // 返回键值对的遍历器
forEach() // 使用回调函数遍历每个成员

// 扩展运算符也可以用在这
let set = new Set(['red', 'green', 'blue']);
let arr = [...set]; // ['red', 'green', 'blue']

weakSet

与set结构类似, 也是不重复值的集合
weakset的成员只能时对象,不能是其他类型的值,只能是数组类数组,对象
weakset中的对象都是弱引用,既垃圾回收机制不考虑weakset对该对象的引用,
所以,无法引用weakSet成员, 不可被遍历

方法

WeakSet.prototype.add(value):向WeakSet实例添加一个新成员。
WeakSet.prototype.delete(value):清除WeakSet实例的指定成员。
WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在WeakSet实例之中

Map

由于object本质上是键值对的集合,一般都是字符串当作键,所以map出现了🎈🎈🎈
map的键不在局限与字符串,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。

let items = [
  ['name', '张三'],
  ['title', 'Author']
];
let map = new Map();
items.forEach(([key, value]) => map.set(key, value)); // 执行

var map = new Map([
  ['name', '张三'],
  ['title', 'Author']
]);

map.size // 2
map.has('name') // true
map.get('name') // "张三"

如果对一个键多次赋值,后面的值可以覆盖前面的值

let map = new Map();
map.set("a", "abc");
map.set("a", "cba");
map.get("a") // cba

只有对于同一个对象的引用,map结构才将其视为同一个键, map的键实际上是跟内存地址绑定的,只要内存地址不一样时,就为两个键,解决了同名属性碰撞(clash)的问题。

let map = new Map();

map.set(['a'], 666);
map.get(['a']) // undefined

let k1 = ['a'];
let k2 = ['a'];

map
.set(k1, 111)
.set(k2, 222);
map.get(k2) // 222
map.get(k1) // 111

如果Map的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map将其视为一个键,包括0-0。另外,虽然NaN不严格相等于自身,但Map将其视为同一个键

方法、属性

.size // 当前键值对的数量


// set方法设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键
// 由于返回的是Map本身,所以可以采用链式写法
 .set (key,val).set("edition", 6).set(262, "standard") 

// 读取一个key对应的兼职,找不到就返回undefined
.get(key)

// 返回一个bool, 表示某个键是否存在
.has(key)

// 删除一个一个键, 成功->true;失败->false
.delete(key)

// 清除所有成员,没有返回值
.clear()


//迭代. Map的遍历顺序就是插入顺序
keys() // 返回键名的遍历器。
values() // 返回键值的遍历器。
entries() // 返回所有成员的遍历器。
forEach() // 遍历Map的所有成员。

各种转换

map转为数组
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
// 或者使用Array.from

数组转Map
new Map([[true, 7], [{foo: 3}, ['abc']]])
// Map {true => 7, Object {foo: 3} => ['abc']}

Map o object
const obj = { foo: 3 };
const map = new Map(Object.entries({ foo: 3 })) // 转为Map
const obj1 = Object.fromEntries(map) // 转为object

console.log(obj1); // {foo: 3}

obj.foo = 100;

console.log(obj, map); // {foo: 100} Map(1) {"foo" => 3}

map.set("foo", 9090)

console.log(obj, map); // {foo: 100} Map(1) {"foo" => 9090}

console.log(obj1); // {foo: 3}

const set = new Set([ obj, obj1 ])
console.log(set); // size -> 2


const set = new WeakMap([
  [obj, "obj"],
  [obj1, "obj1"]
])
console.log(set); //  WeakMap {{…} => "obj1", {…} => "obj"}

// 深拷贝

WeakMap

区别就是只接收对象作为键名。(null)除外。
弱引用,不会计入垃圾回收机制。场景就是,键对应的对象,在后面可能会消失,有助于防止内存泄漏

区别

没有遍历操作 key(), values(), entries()
没有 size 属性
没有 clear() 方法,无法被清空

Iterator

遍历器(Iterator),就是一种机制,一种接口,为各种不同的数据结构提供统一的访问机制。任何数据机构只要实现, Iterator接口,就可以完成遍历操作
本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口,就等于部署一种线性转换。

不可直接遍历对象,因为对象没有实现Inerator接口。因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定

es6规定,默认的Iterator接口部署在数据结构的 Symbol.iterator 属性,一个数据结构只要Symbol.iterator 属性,就可以认为是可遍历的。
一个数据结构只要实现了 Iterator 接口,就称这种数据结构式可遍历的。

在es6中,三类数据结构原生的实现了该接口,
数组、某些类数组的对象、Set、Map、String

作用

  1. 为各种数据结构,提供一个统一的,简便的访问接口
  2. 使得数据几口的成员能够按照某种次序排列,
  3. es6创造了一种新的遍历命名 for...of 遍历

    过程

  4. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

  5. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  6. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
  7. 不断调用指针对象的next方法,直到它指向数据结构的结束位置 ```javascript let it = makeIterator([‘a’, ‘b’]);

it.next() // { value: “a”, done: false } it.next() // { value: “b”, done: false } it.next() // { value: undefined, done: true }

function makeIterator(array) { let nextIndex = 0; return { next: function() { return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {value: undefined, done: true}; } }; }

由于Iterator只是把接口的规格加到数据结构上,所以遍历器于所遍历对象是分开的。<br />可以模拟出对象的遍历器迭代的数据结构,比如下面的无限循环
```javascript
let it = idMaker();

it.next().value // '0'
it.next().value // '1'
it.next().value // '2'
// ...

function idMaker() {
  let index = 0;

  return {
    next: function() {
      return {value: index++, done: false};
    }
  };
}

以下场景会默认调用 Iterator 接口

// 解构赋值
let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
// x='a'; y='b'
let [first, ...rest] = set;
// first='a'; rest=['b','c'];

// 扩展运算符
let  str = 'hello';
[...str] //  ['h','e','l','l','o']

let arr = ['b', 'c'];
['a', ...arr, 'd'] // ['a', 'b', 'c', 'd']

// yieId*
let generator = function* () {
  yield 1;
  yield* [2,3,4];
  yield 5;
};

var iterator = generator();

iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }

// orther
for...of
Array.from()
Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))
Promise.all()
Promise.race()

for…in

缺点:

  • 数组的键名是数字,但是for…in循环是以字符串作为键名“0”、“1”、“2”等等。
  • for…in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
  • 某些情况下,for…in循环会以任意顺序遍历键名。

    for…of

    一个数据结构只要部署了Symbol.iterator属性,就被视为具有iterator接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法
    优点:

  • 有着同for…in一样的简洁语法,但是没有for…in那些缺点。

  • 不同用于forEach方法,它可以与break、continue和return配合使用。
  • 提供了遍历所有数据结构的统一操作接口。