Map和Set的区别,Map和Object的区别

1,433 阅读10分钟

一、Map和Set的区别

一、Set集合

a.简述
1.SetMap 主要的应用场景在于 数据重组 和 数据储存。
2.Set 是一种叫做集合的数据结构,Map 是一种叫做字典的数据结构。

b.集合 与 字典 的区别:
1.共同点:集合、字典 可以储存不重复的值
2.不同点:集合 是以 [value, value]的形式储存元素,字典 是以 [key, value] 的形式储存
3.集合(Set) 新的数据结构,类似于数组,成员唯一(内部元素没有重复的值)。且使用键对数据排序即顺序存储。

const arr = new Set()
const data = [1,2,3,4,5,1,2,3,4]
data.forEach(item => arr.add(item))
for (let i of arr) {
    console.log(i) // 1 2 3 4 5
}

// 去重数组
const newData = [...new Set(data)] // 
console.log(newData) // [1, 2, 3, 4, 5]
const newData1 = Array.from(new Set(data))
console.log(newData1) // [1, 2, 3, 4, 5]

// 注意:向 Set 加入值的时候,不会发生类型转换 NaN !== NaN
const newSet = new Set()
const a = NaN
const b = undefined
const c = 5
const d = '5'
newSet.add(a)
newSet.add(b)
newSet.add(c)
newSet.add(d)
console.log(newSet) // {NaN, undefined, 5, '5'}

c.操作方法
1.add(value):新增,相当于 array里的push。
2.delete(value):存在即删除集合中value。
3.has(value):判断集合中是否存在 value。
4.clear():清空集合。

d.遍历方法(遍历顺序为插入顺序)
1.keys():返回一个包含集合中所有键的迭代器。
2.values():返回一个包含集合中所有值得迭代器。
3.entries():返回一个包含Set对象中所有元素得键值对迭代器
4.forEach(callbackFn, thisArg):用于对集合成员执行callbackFn操作,如果提供了 thisArg 参数,
回调中的this会是这个参数,没有返回值。
let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);
}

["red", "red"]
["green", "green"]
["blue", "blue"]
set.forEach((value, key) => console.log(key + ' : ' + value))

二、字典(Map)

a.简述
1.是一组键值对的结构,具有极快的查找速度。
const m = new Map()
const o = { p: 'xiaolin' }
m.set(o, 'tom')
m.get(o)
m.has(o)
m.delete(o)
m.has(o)
console.log(m, 'Map')

b.操作方法
1.set(key, value):向字典中添加新元素。
2.get(key):通过键查找特定的数值并返回。
3.has(key):判断字典中是否存在键key。
4.delete(key):通过键 key 从字典中移除对应的数据。
5.clear():将这个字典中的所有元素删除。

c.遍历方法:
1.Keys():将字典中包含的所有键名以迭代器形式返回。
2.values():将字典中包含的所有数值以迭代器形式返回。
3.entries():返回所有成员的迭代器。
4.forEach():遍历字典的所有成员。 
const map = new Map([
    ['F', 'no'],
    ['T',  'yes'],
]);

for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
   console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

// 等同于使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"   
map.forEach((item) => {
    console.log(item, 'item')
})

//数组对象去重
function unique(array, target) {
    let map = new Map();
    for (let item of array) {
        if (!map.has(item[target])) {
            map.set(item[target], item)
        }
    }
    const res = [...map.values()];
    return res
}

三、 总结

Set
1.成员唯一、有序且不重复。
2.[value, value],键值与键名是一致的(或者说只有键值,没有键名)。
3.可以遍历,方法有:add、delete、has。

Map1.本质上是键值对的集合,类似集合。
2.可以遍历,方法很多可以跟各种数据格式转换。

二、Map和Object的区别

一.Map和Object 的区别

a.不同点
1.Object 中, key 必须是简单数据类型(整数,字符串或者是 symbol),
而在 Map 中则可以是 JavaScript 支持的所有数据类型,也就是说可以用一个 Object 来当做一个Map元素的 key。
2.Map 元素的顺序遵循插入的顺序,而 Object 的则没有这一特性。
3.Map 继承自 Object 对象。

b.Object 支持以下几种方法来创建新的实例:
const obj = {}
const obj = new Object()
const obj = Object.create(null)

c.Map 仅支持下面这一种构建方法:
const map = new Map([[1, 2], [2, 3]]); // map = {1 => 2, 2 => 3}

d.数据访问
Map 
1.map.get(1); // 2  访问元素
2.map.has(1);  判断某个元素是否在 Map 中可以使用
3.map.set(key, value);  新增一个数据
4.map.delete(1);   删除指定数据
5.map.clear();  全部删除
Object 可以通过 . 和 [ ] 来访问
// 获取数据
obj.id;
obj['id'];

// 新增/修改数据
obj['key'] = value;
obj.key = value;

// 删除数据
delete obj.id;
Object 可以使用 Object.prototype.hasOwnProperty() 
来判断某个key是否是这个对象本身的属性,从原型链继承的属性不包括在内。

f.获取size
Map 自身有 size 属性,可以自己维持 size 的变化。
Object 则需要借助 Object.keys() 来计算
console.log(Object.keys(obj).length);

g.Iterating
Map 自身支持迭代,Object 不支持。
如何确定一个类型是不是支持迭代呢? 可以使用以下方法:
console.log(typeof obj[Symbol.iterator]); // undefined
console.log(typeof map[Symbol.iterator]); // function

二.总结:

何时使用 Map ,何时使用 Object1.虽然Map 在很多时候优于 Object,但是作为 JavaScript 最基础的数据类型,还是有很多情景更适合使用 Object2.当所要存储的是简单数据类型,并且 key 都为字符串或者整数或者 Symbol 的时候,优先使用 Object ,
因为Object可以使用 字符变量 的方式创建,更加高效。
3.JSON 直接支持 Object,但不支持 Map
4.Map 是纯粹的 hash, 而 Object 还存在一些其他内在逻辑,所以在执行 delete 的时候会有性能问题。
所以写入删除密集的情况应该使用 Map5.Map 会按照插入顺序保持元素的顺序,而Object做不到。
6.Map 在存储大量元素的时候性能表现更好,特别是在代码执行时不能确定 key 的类型的情况

三、数组的filter,every,flat的作用?

filter(): 创建一个新数组,新数组中的元素是由过滤指定数组中符合某条件的元素组成。

every(): 用于检测目标数组中的所有元素是否满足某项条件,如果都满足则返回true,只要有一个不满足,则返回false;

flat():  返回一个新数组,新数据中的元素是将目标数组(多层数组)拉低一层,flat可以传参数,表示拉低几
层,默认参数为1

四、es6有哪些新特性

1.默认参数;

2.字符串模板;

3.解构赋值;

4.class类的概念

5.各类型数据的扩展

6.箭头函数

7.promise

8.模块的概念

9.letconstsymbol创建变量,块作用域的概念

10.MapSet新数据结构

五、说一下对promise的了解?

promise是es6提出的一种解决异步编程的方法,相对于原来使用回调函数的方法,它具有更合理,更强大,更简洁等优点。

promise是一个构造函数,自创建开始就会执行。含有且只能含有三种状态:pending执行中,fulfilled执行成
功,reject执行失败。状态之间的转化也只能是单向的:pending到fulfilled、pending到reject;

promise有一个方法.then,用于接受promise处理后的结果,含有两个参数,relative和reject,分别接受成功
和失败的结果,他们是内置的回调函数,返回一个promise。.then方法可以链式调用。

六、Promise的all和race有什么区别

Promise.all()方法用于将多个Promise实例包装成一个新的Promise实例。新的promise对象的状态由内部的多个
promise共同决定,只有内部promise都是fulfilled时新的promise实例才是fulfilled,其他情况都是reject
状态;

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。多个promise之中有一个实
例率先改变状态,新的promise实例的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给新的
promise实例的回调函数。

七、箭头函数和普通函数的区别

a.箭头函数this指向父级作用域的this
b.call(),apply(),bind()无法改变箭头函数中this的指向
c.不可以被当作构造函数
d.不可以使用arguments对象
e.箭头函数不支持new.target

八、let、var和const的区别?如果希望const定义的对象的属性也不能被修改该怎么做?

a.不存在变量提升
b.块级作用域
c.暂时性死区
d.不可重复声明
e.不会挂在window对象下面
f.const声明之后必须马上赋值,否则会报错
g.const声明的简单类型不可更改,复杂类型内部数据可以更改 

用数据劫持,Object.defineProperty和Proxy都可以实现const定义的对象的属性也不能被修改。
const a = { b:3 }

Object.defineProperty(a,'b',{
    writable:false,
})

a.b = 9
console.log(a) // {b:3}

九、堆和栈的区别?

堆和栈都是操作系统的内存,堆是动态分配内存空间,内存大小不一样,也不会自动释放,栈是自动分配内存空间,
大小固定,会自动释放;js声明的变量,基本数据类型的分配到栈内存,复杂数据类型在堆内存。

10、instanceof的实现原理?

instanceof原理
1.获取实例对象的隐式原型
2.获取构造函数的prototype属性
3.while 循环 -> 在原型链上不断向上查找
4.在原型链上不断查找 构造函数的显式原型
5.直到implicitPrototype = null 都没有找到,返回false
6.构造函数的prototype属性出现在实例对象的原型链上返回true

定义:instanceof运算符用于检测构造函数的prototype属性是否出现某个实例对象的原型链上
function instance_of(Obj, Constructor) {
    let implicitPrototype = Obj.__proto__ // 获取实例对象的隐式原型
    let displayPrototype = Constructor.prototype // 获取构造函数的prototype属性
    // while 循环 -> 在原型链上不断向上查找
    while(true) {
        // 直到implicitPrototype = null 都没有找到,返回false
        if (implicitPrototype === null) {
            return false
            // 构造函数的prototype属性出现在实例对象的原型链上返回true
        } else if (implicitPrototype === displayPrototype) {
            return true
        }
        // 在原型链上不断查找 构造函数的显式原型
        implicitPrototype = implicitPrototype.__proto__
    }
}

11、new的实现原理

1.实例化对象 
2.返回值的问题:构造函数中如果有值返回,那实例化后的对象就是这个返回值
const TMap = function(options) {
    this.name = options.name
    this.address = options.address
    // return {
    //     name: 'xiaolin1',
    //     address:'shenzhen'
    // }
}
const map = new TMap({
    name: 'xiaolin',
    address:'shenzhen'
})
console.log(map, 'map')

const ObejectFactory = (...args) => {
    // 1.创建空对象
    const obj = {}
    // 2.获取构造函数
    const Constructor = [].shift.call(args)
    // 3.对象的__proto__指向Constructor.prototype
    obj.__proto__ = Constructor.prototype
    // 4.用apply的方式把构造函数Constructor的this指向obj执行Constructor
    const ret = Constructor.apply(obj,args)
    // 5.根据ret的执行结果判断返回构造函数的返回对象还是新创建的空对象
    return typeof ret === 'object' ? ret : obj
}
console.log(ObejectFactory(TMap, {name: '11', address: '22'}), '22')

12、数据类型有哪些?如何判断一个数据是否是数组

基本数据类型:stringnumberBooleannull,underfined,symbol
引用数据类型:objectfunction,array,正则等等)

如何判断一个变量是不是数组:
1.array.constructor === Array
2.array instanceof Array
3.Array.isArray(array)
4.Object.prototype.toString.call(array) === [object Array]

13、闭包

闭包:有权访问另一个函数作用域中的变量的函数
 场景:
    a.事件函数的封装 
    b.用闭包模拟私有方法
    c.在循环中给页面元素绑定事件响应函数
 特点:
    1.创建私有变量
    2.延长变量的生命周期
存在什么问题:闭包本身会造成内部变量常驻内存
function makeCounter() {
    let num = 0;
    function changeBy(val) {
          num += val
    }
    return  {
        add: function() {
           changeBy(1)
        },
        val: function() {
            return num
        }
    }
}

const res = makeCounter()
res.add()
res.add()
console.log(res.val())

14、JQuery实现链式调用的原理是什么

每个方法都return this

15、分别介绍一下原型、原型链、作用域和作用域链的含义和使用场景