Skip to content
on this page

扫码联系

编程学习&& IT

tian

课程简介

Set 和Map

Set

Set是什么

Set 实例的方法和属性

Set构造函数的参数

Set的注意事项

Set的应用

Map

Map是什么

Map 实例的方法和属性

Map构造函数的参数

Map的注意事项

Map的应用

set

什么是Set 集合

理解Set

1.Set初识

  • 什么是Set
  • 理解Set

1.什么是Set(集合)

Set 是一系列无序、没有重复值的数据集合

js
[1, 2]; //数组是一系列有序的数据集合

2.理解 Set

js
//创建数组的两种方式
console.log(  [1, 2, 1]  );
console.log(  new Array(1, 2, 1)  );

创建Set

Set 中不能有重复的成员

js
//实例化set对象
const s = new Set();
s.add(1);
s.add(2);
s.add(2);
console.log(s);

Set 没有下标去标示每一个值,所以 Set 是无序的,也不能像数组那样通过下标去访问 Set 的成员

2.Set 实例的方法和属性

  • 方法
  • 属性

1.方法

add 添加成员

js
const s = new Set();
s.add(1).add(2).add(2);
// console.log(s);

has 是否有指定成员

js
console.log(s.has(1));  //true
// console.log(s.has(3));  false

delete 删除指定成员

js
s.delete(1);

// 使用 delete 删除不存在的成员,什么都不会发生,也不会报错
s.delete(3);
console.log(s)

clear 全部删除

js
s.clear();
console.log(s)

forEach

按照成员添加进集合的顺序遍历

  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回键值对的遍历器
  • for...of:可以直接遍历每个成员
js
s.forEach(function (item, index, set) {
    // Set 中 item = index
    console.log(item, index, set === s);
    console.log(this);
}, document); //第二个参数指定this指向


  console.log(s.keys());
  console.log(s.values());
console.log(s.entries());

for (let item of s) {
    console.log(item)
}

for (let item of s.values()) {
    console.log(item)
}

for (let item of s.entries()) {
    console.log(item[0], item[1])
}

2.属性

size 长度

js
console.log(s.size);

3.Set构造函数的参数

数组、 字符串、arguments、NodeList、Set 等

1.数组

js
//最常用
const s = new Set([1, 2, 1]);
console.log(s);

2.字符串、arguments、NodeList、Set 等

字符串

js
console.log(new Set('hi'));

arguments

js
function fn() {
    console.log(new Set(arguments));
}
fn(1, 2, 1)

NodeList

js
<p>1</p>
<p>2</p>
<p>3</p>

console.log(new Set(document.querySelectorAll('p')));

Set

js
const s = new Set([1, 2, 1]);
console.log(new Set(s) === s);  //false 引用不同
console.log(s);

4.Set 的注意事项

  • 判断重复的方式
  • 什么时候使用Set

1.判断重复的方式

Set 对重复值的判断基本遵循严格相等(===)

但是对于 NaN 的判断与 === 不同,Set 中 NaN 等于 NaN 所以会去重

js
const s = new Set([1, 2, 1]);
//const s = new Set([NaN, 2, NaN]);
console.log(s);

console.log(1 === 1); //true
console.log(NaN === NaN); //false

如果是引用数据类型

js
const s = new Set();
s.add({}).add({});
console.log({} === {});
console.log(s);

2.什么时候可以使用 Set

① 数组或字符串去重时

② 不需要通过下标访问,只需要遍历时

③ 为了使用 Set 提供的方法和属性时(add delete clear has forEach size 等)

5.set的应用

1.数组去重

js
let arr = [1, 2, 3, 4, 2, 3]
let s = new Set(arr)
console.log(s)

如何把set转换为数组

js
//使用 s.forEach丢到一个新的数组

//set和数组类似 也可以展开set
console.log(...s);
console.log([...s]); //使用展开运算符直接在[]中展开
console.log( Array.from(s) );

//如何使用set直接去重
console.log([...new Set(arr)]);
console.log( Array.from(new Set(arr)) );

合并去重

js
let arr1 = [1, 2, 3, 4]
let arr2 = [2, 3, 4, 5, 6]
let s = new Set([...arr1, ...arr2])
console.log(s)
console.log([...s])
console.log(Array.from(s))

2.字符串去重

js
'abbacbd';
const s = new Set('abbacbd');
console.log([...s].join('')); //把set展开变成数组 使用join方法

//简写成  console.log([...new Set('abbacbd')].join(''));
console.log(s);

3.存放 DOM 元素

js
<p>1</p>
<p>2</p>
<p>3</p>

console.log(document.querySelectorAll('p'));

const s = new Set(document.querySelectorAll('p'))
console.log(s);
s.forEach(function (elem) {
    // console.log(elem);
    elem.style.color = 'red';
    elem.style.backgroundColor = 'yellow';
});

WeakSet

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

WeakSet 的成员只能是对象,而不能是其他类型的值。

WeakSet 的对象都是弱引用

即WeakSet 中对对象的引用不会被考虑进垃圾回收机制,即只要没有其他的对象引用该对象,则该对象就会被回收,而不管它在不在 WeakSet

(由于这个特性,所以 WeakSet 适合临时存放一组对象和跟对象绑定的信息)

js
const ws = new WeakSet()
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set
js
let ws = new WeakSet()

const obj1 = {
    name: 'yunmu'
}
const obj2 = {
    age: 5
}

//WeakSet 有三个方法:add, delete, has
ws.add(obj1)
ws.add(obj2)
ws.delete(obj1)
console.log(ws)
console.log(ws.has(obj2))

WeakSet 没有size属性,没有办法遍历它的成员。

WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

map

1.map初识

  • 认识Map
  • Map 和对象的区别

1.认识 Map(映射)

Map 和对象都是键值对的集合

js
//普通对象 键->值,key->value
const person = {
    name: 'yunmu',
    age: 18
};

创建Map对象

js
const m = new Map();
m.set('name', 'yunmu');
m.set('age', 18);
console.log(m);

2.Map 和对象的区别

键的类型

对象一般用字符串或者 Symbols当作键

js
const obj = {
    name: 'yunmu',
    true: 'true',
    [{}]: 'object'
};
 console.log(obj);
console.log({}.toString()); //即使你想传引用类型 也会帮你转换为字符串

基本数据类型:数字、字符串、布尔值、undefined、null

引用数据类型:对象([]、{}、函数、Set、Map 等)

以上都可以作为 Map 的键

js
const m = new Map();
m.set('name', 'yunmu');
m.set(true, 'true');
m.set({}, 'object');
m.set(new Set([1, 2]), 'set');
m.set(undefined, 'undefined');
console.log(m);

键的顺序

Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。

键值对的统计

你可以通过 size 属性直接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。

键值对的遍历

Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后再进行迭代。

性能

Map 在涉及频繁增删键值对的场景下会有些性能优势。

2.Map 实例的属性和方法

  • 方法
  • 属性

1.方法

set 添加成员

js
 const m = new Map();

//使用 set 添加的新成员,键如果已经存在,后添加的键值对覆盖已有的
 m.set('age', 18).set(true, 'true').set('age', 20);
console.log(m);

get 获取指定成员

js
console.log(m.get('true')); // get 获取不存在的成员,返回 undefined
console.log(m.get(true));   

has 是否有指定成员

js
console.log(m.has('age'));
console.log(m.has('true'));

delete 删除指定成员

js
m.delete('age');

// 使用 delete 删除不存在的成员,什么都不会发生,也不会报错
m.delete('name');
console.log(m);

clear 全部删除

js
m.clear();
console.log(m);

forEach

js
m.forEach(function (value, key, map) {
    console.log(value, key, map === m);
    console.log(this);
}, document);
  • keys() 返回一个新的 Iterator 对象。它包含按照顺序插入 Map 对象中每个元素的 key 值
  • values() 方法返回一个新的 Iterator 对象。它包含按顺序插入Map对象中每个元素的 value 值
  • entries() 方法返回一个新的包含 [key, value] 对的 Iterator ? 对象,返回的迭代器的迭代顺序与 Map 对象的插入顺序相同
  • for...of 可以直接遍历每个成员
js
for (let [key, value] of map) {
    console.log(key, value)
}

for (let key of map.keys()) {
    console.log(key)
}

for (let value of map.values()) {
    console.log(value)
}

for (let [key, value] of map.entries()) {
    console.log(key, value)
}

2.属性

size

​ 普通对象没有类似的属性

js
console.log(m.size);

3.Map构造函数的参数

1.数组

js
console.log(new Map(['name', 'tian', 'age', 18]));//错误

//只能传二维数组,而且必须体现出键和值
console.log(
    new Map([
        ['name', 'tian'],
        ['age', 18]
    ])
);

2.Set、Map 等

Set

Set 中也必须体现出键和值

js
const s = new Set([
    ['name', 'tian'],
    ['age', 18]
]);
console.log(new Map(s));
console.log(s);

Map

复制了一个新的 Map

js
const m1 = new Map([
    ['name', 'tian'],
    ['age', 18]
]);
console.log(m1);

const m2 = new Map(m1);
console.log(m2, m2 === m1); 

4.Map的注意事项

  • 判断键名是否相同的方式
  • 什么时候使用Map

1.判断键名是否相同的方式

基本遵循严格相等(===)

例外就是 NaN,Map 中 NaN 也是等于 NaN

js
console.log(NaN === NaN);  //false

const m = new Map();
m.set(NaN, 1).set(NaN, 2)
console.log(m);

2.什么时候使用 Map

如果只是需要 key -> value 的结构,或者需要字符串以外的值做键,使用 Map 更合适

js
//丰富方法和属性
forEach
size

//只有模拟现实世界的实体时,才使用对象
const person = {};

5.Map 的应用

让三个p标签的文字颜色改变

html
</head>
<body>
    <p>1</p>
    <p>2</p>
    <p>3</p>

    <script>
        let [p1, p2, p3] = document.querySelectorAll("p");

        console.log(p1, p2, p3);

        //  const m = new Map();
        //  m.set(p1, "red");
        //  m.set(p2, "green");
        //  m.set(p3, "orange");

        const m = new Map([
            [p1, "red"],
            [p2, "green"],
            [p3, "orange"],
        ]);

        console.log(m);

        m.forEach(function (color, elem) {
            console.log(value, item);

            elem.style.color = color;
        });
    </script>

让效果更复杂

js
const m = new Map([
    [
        p1,
        {
            color: 'red',
            backgroundColor: 'yellow',
            fontSize: '40px'
        }
    ],
    [
        p2,
        {
            color: 'green',
            backgroundColor: 'pink',
            fontSize: '40px'
        }
    ],
    [
        p3,
        {
            color: 'blue',
            backgroundColor: 'orange',
            fontSize: '40px'
        }
    ]
]);

m.forEach( (propObj,elem) => {

    for(let k in propObj){
        elem.style[k] = propObj[k];
    }

})

WeekMap

WeakMap结构与Map结构类似,也是用于生成键值对的集合。

js
// WeakMap 可以使用 set 方法添加成员
const wm1 = new WeakMap()
const key = {
    foo: 1
}
wm1.set(key, 2)
wm1.get(key) // 2

// WeakMap 也可以接受一个二维数组,
// 作为构造函数的参数
const k1 = [1, 2, 3]
const k2 = [4, 5, 6]
const wm2 = new WeakMap([
    [k1, 'foo'],
    [k2, 'bar']
])
wm2.get(k2) // "bar"

//weak不支持size属性 不支持遍历  clear
//所以 WeakMap只有四个方法可用:get()、set()、has()、delete()。

WeakMap与Map的区别有两点。

  • WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
  • WeakMap的键名所指向的对象,不计入垃圾回收机制
js
const map = new WeakMap()
map.set(1, 2)
// TypeError: 1 is not an object!
map.set(Symbol(), 2)
// TypeError: Invalid value used as weak map key
map.set(null, 2)
// TypeError: Invalid value used as weak map key



let wm = new WeakMap(), element = document.querySelector(".element");
wm.set(element, "data");

let value = wm.get(elemet)

应用场景

想要给网页的dom元素上添加数据时可用 WeakMap

用 WeakMap 好处是当该 DOM 元素被清除,对应的 WeakMap记录就会自动被移除

js
const wm = new WeakMap();
const ele = document.getElementById('example');
wm.set(el, 'some information');
wm.get(el)

Symbol

1.Symbol初识

1为什么用Symbol

ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

2. Symbol是什么

定义:Symbol实际上是ES6引入的一种原始数据类型

3. Symbol的使用

js
let s = Symbol();
console.log(s);   // Symbol() 
typeof s;        // "symbol"

变量s就是一个独一无二的值。typeof的结果说明s是 Symbol 数据类型。

既然是独一无二的,那么两个Symbol()就一定是不相等的:

js
let s1 = Symbol()
let s2 = Symbol()
console.log(s1)
console.log(s2)
console.log(s1 === s2) // false

4 Symbol函数前不能用new

Symbol函数不是一个构造函数,前面不能用new操作符。所以Symbol类型的值也不是一个对象,不能添加任何属性,它只是一个类似于字符型的数据类型。

如果强行在Symbol函数前加上new操作符,会报错,如下:

js
let sym = new Symbol();
// Uncaught TypeError: Symbol is not a constructor(…)

2.Symbol函数的参数

1 字符串作为参数

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

js
let s1 = Symbol('s1');
let s2 = Symbol('s2');
console.log(s1);  // Symbol(s1)
console.log(s2);  // Symbol(s2)
s1 === s2;  //  false

let s3 = Symbol('s2');
s2 === s3;  //  false


console.log(s1.description);//当前的描述

//给Symbol函数加了参数之后,控制台输出的时候可以区分到底是哪一个值;

//Symbol函数的参数只是对当前Symbol值的描述,因此相同参数的Symbol函数返回值是不相等的;

2 对象作为参数

如果Symbol函数的参数是一个对象,就会调用该对象的toString方法,将其转化为一个字符串,然后

才生成一个Symbol值。所以,Symbol函数的参数只能是字符串。

js
let sym = Symbol({});
console.log(sym);  // Symbol([object Object])

3.Symbol 方法

Symbol.for()

Symbol.for() 接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。

js
let s1 = Symbol.for('foo');
console.log(s1);
let s2 = Symbol.for('foo');
console.log(s1 == s2) //true
//注意
//Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。
//它们的区别是,前者会被登记在全局环境中供搜索,后者不会。
//Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果	不存在才会新建一个值。

Symbol.keyFor()

Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key。

js
const s1 = Symbol('foo');
console.log(Symbol.keyFor(s1)); // undefined

const s2 = Symbol.for('foo');
console.log(Symbol.keyFor(s2)); // foo

4.Symbol的应用

Symbol就是为对象的属性名而生

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

js
const person = {
    黛玉: {
        marry: false,
        hobby: "无病呻吟",
    },

    宝玉: {
        marry: true,
        hobby: "吟诗作赋",
    },

    宝玉: {
        marry: false,
        hobby: "纨绔子弟",
    },

    //只会保留最后一个宝玉
};
console.log(person);

如果使用Symbol,同名的学生信息就不会被覆盖:

js
const p1 = Symbol("宝玉");
const p2 = Symbol("宝玉");

const person = {
    黛玉: {
        marry: false,
        hobby: "无病呻吟",
    },

    [p1]: {
        marry: true,
        hobby: "吟诗作赋",
    },

    [p2]: {
        marry: false,
        hobby: "纨绔子弟",
    },
};

console.log(person);
console.log(person[p1]);
console.log(person[p2]);

消除魔术字符串

魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,改由含义清晰的变量代替。

js
function getArea(shape) {
    let area = 0;
    switch (shape) {
        case "Triangle":
            area = 1;
            break;
        case "Circle":
            area = 2;
            break;
    }
    return area;
}
console.log(getArea("Triangle"));

上面代码中,字符串Triangle和Circle就是魔术字符串。它多次出现,与代码形成“强耦合”,不利于将来的修改和维护。

使用Symbol就可以很好的解决这个问题:

js
const shapeType = {
    triangle: Symbol(),
    circle: Symbol(),
};

function getArea(shape) {
    let area = 0;
    switch (shape) {
        case shapeType.triangle:
            area = 1;
            break;
        case shapeType.circle:
            area = 2;
            break;
    }
    return area;
}
console.log(getArea(shapeType.triangle));

5.Symbol使用注意事项

1.Symbol值作为属性名的遍历

如果symbol作为了key,那么用for..in..循环,是循环不出来symbol的key的

使用for...in和for...of都无法遍历到Symbol值的属性,Symbol值作为对象的属性名,也无法通过Object.keys()、Object.getOwnPropertyNames()来获取了。

但是,不同担心,我们可以使用Object.getOwnPropertySymbols()方法获取一个对象上的Symbol属性名。

js
const p1 = Symbol("宝玉");
const p2 = Symbol("宝玉");

const person = {
    黛玉: {
        marry: false,
        hobby: "无病呻吟",
    },

    [p1]: {
        marry: true,
        hobby: "吟诗作赋",
    },

    [p2]: {
        marry: false,
        hobby: "纨绔子弟",
    },
};

console.log(person);


//只能遍历普通属性
for(let key in person){
    console.log(key);
}



//只能遍历普通属性
for(let key of Object.keys(person)){
    console.log(key);
}

console.log(Object.getOwnPropertySymbols(grade)); //  [Symbol(李四), Symbol(张三)]


//只能遍历Symbol属性名
for(let key of Object.getOwnPropertySymbols(person)){
    console.log(key);
}

//既能取到普通属性又有Symbol属性
for(let key of Reflect.ownKeys(person)){
    console.log(key);
}

2.Symbol值不可以进行运算

既然Symbol是一种数据类型,那我们一定想知道Symbol值是否能进行运算。告诉你,Symbol值是不

能进行运算的,不仅不能和Symbol值进行运算,也不能和其他类型的值进行运算,否则会报错。

Symbol值可以显式转化为字符串和布尔值,但是不能转为数值。

js
let s1 = Symbol('symbol1')
let s2 = Symbol('symbol2')
s1 + s2;   // TypeError: Cannot convert a Symbol value to a number

// 转字符串
String(s1);   // "Symbol(symbol1)"

// 转布尔值
Boolean(s1); // true

// 转数字
Number(s1);  // TypeError: Cannot convert a Symbol value to a number

课程总结

Set/Map是什么

Set是一系列无序、没有重复值的数据集合

Map 的本质是键值对的集合

Set/Map实例的方法与属性

Set

add()

has()

delete() /clear()

forEach()

size属性

Map

set() / get()

has()

delete()

clear()

forEach()

size属性

Set/Map构造函数的参数

Set:数组、字符串、arguments、NodeList、Set 等

Map:数组(二维数组)、Set、Map 等

Set/Map 对相同值/键的判断

基本可用严格相等( ===)判断

例外:对于NaN 的判断与===不同, Set/Map 中 NaN 等于NaN

什么时候使用 Set

数组或字符串去重时

不需要通过下标访问,只需要遍历时

为了使用Set 提供的方法和属性时

什么时候使用Map

只需要key - > value的结构时

需要字符串以外的值做键时

为了使用Map提供的方法和属性时

Symbol

一种原始数据类型,表示独一无二的值

Symbol就是为对象的属性名而生

主要使用字符串作为参数,表示对 Symbol 实例的描述以便区分

Symbol主要用于对象的属性名 保证同名属性不会被覆盖 以及 消除魔术字符串

Symbol属性的遍历有点特殊 需要注意