JS

167 阅读7分钟

原文

JS

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

  • map是键值对,键是唯一的,迭代顺序按set()方法插入的顺序
  • 键的比较基于零值相等,即Object.is(),逻辑与===相似,区别在于
    +0 === -0trueNaN === NaNfalse
    Object.is(+0,-0)falseObject.is(NaN,NaN)true
  • map可以复制或合并
// 复制
const original = new Map([
  [1, 'one'],
]);
const clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false. 浅比较 不为同一个对象的引用
// 合并
const first = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);
const second = new Map([
  [1, 'uno'],
  [2, 'dos']
]);

// 合并两个 Map 对象时,如果有重复的键值,则后面的会覆盖前面的。(与数组合并同理)
// 展开语法本质上是将 Map 对象转换成数组。
const merged = new Map([...first, ...second]);

console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
  • map的属性、方法
    map.size --- map对象的成员数量
    clear() --- 移除所有元素
    delete(key) --- 删除指定元素(根据键)
    entries() --- 返回一个新的迭代器对象(iterator)
    forEach() --- 按照插入顺序依次对 Map 中每个键/值对执行一次给定的函数
    get(key) --- 返回指定元素
    has(key) --- 判断指定元素是否存在
    keys() --- 返回key的迭代对象
    set(key,value) --- 更新键值对
    values() --- 返回value的迭代对象

  • MapObject的区别 | | Map | Object | | --- | --- | --- | |意外的键|Map默认情况不包含任何键。只包含显式插入的键。|一个Object有一个原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。| |键的类型|一个Map的键可以是任意值,包括函数、对象或任意基本类型。|一个Object的键必须是一个String或是Symbol。| |键的顺序|Map中的键是有序的。因此,当迭代的时候,一个Map对象以插入的顺序返回键值。|虽然Object的键目前是有序的,但并不总是这样,而且这个顺序是复杂的。因此,最好不要依赖属性的顺序。| |键值对个数|Map的键值对个数可以轻易地通过size属性获取。|Object的键值对个数只能手动计算。| |迭代|Map是可迭代的,所以可以直接被迭代。|Object 没有实现 迭代协议,所以使用 JavaSctipt 的for...of表达式并不能直接迭代对象。| |性能|在频繁增删键值对的场景下表现更好。|在频繁添加和删除键值对的场景下未作出优化。| |序列化和解析|没有元素的序列化和解析的支持。
    但是你可以使用携带 replacer 参数的 JSON.stringify()创建一个自己的对 Map 的序列化和解析支持。参见 Stack Overflow 上的提问:How do you JSON.stringify an ES6 Map?|原生的由 ObjectJSON 的序列化支持,使用 JSON.stringify()
    原生的由 JSON 到 Object的解析支持,使用 JSON.parse()。|

  • Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
    Set对象是值的集合,可以按照插入的顺序迭代它的元素。 Set 中的元素是唯一的。
    Set判断两值是否相等,逻辑同===,区别在于NaNNaN被视为相同的值

  • set的属性、方法
    set.size --- set对象的元素数量
    clear() --- 移除所有元素
    delete(value) --- 删除指定元素
    entries() --- 返回一个新的迭代器对象(iterator) ,形式为[value,value]
    forEach() --- 按照插入顺序依次对 Map 中每个键/值对执行一次给定的函数
    has(value) --- 判断指定元素是否存在
    add(value) --- 插入新元素
    keys() --- values的别名
    values() --- 返回value的迭代对象

数组的filter、every、flat的作用是什么

filter()  按照指定函数过滤数组,浅拷贝
every()  判断是否数组内所有元素都能通过测试函数。它返回一个布尔值。(若收到一个空数组,此方法在任何情况下都会返回 true。)
flat(depth = 1)  数组扁平化

// 手写数组扁平化
function flat(arr, depth = 1) {
    if(depth <= 0) return arr
    return arr.reduce((prev, curr) => prev.concat(Array.isArray(curr) ? flat(curr, depth - 1) : curr),[])
}

es6有哪些新特性

前端开发者不得不知的 ES6 十大特性

说一下对Promise的了解

Promise实现原理

promise和async的区别

async本质上是Generator的语法糖

// Generator函数
function* generator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = generator(); // "Generator { }"

console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3

// Generator.prototype.next(value)
function* gen() {
  while(true) {
    var value = yield null;
    console.log(value);
  }
}

var g = gen();
g.next(1); // "{ value: null, done: false }"
g.next(2);
// 2
// "{ value: null, done: false }"

Promise的all和race有什么区别

// 手写all
Promise.MyAll = function (promises) {
  let arr = [], count = 0
  return new Promise((resolve, reject) => {
    promises.forEach((item, i) => {
      Promise.resolve(item).then(res => {
        arr[i] = res
        count += 1
        if (count === promises.length) resolve(arr)
      }).catch(reject)
    })
  })
}
// 手写race
Promise.MyRace = function (promises) {
  return new Promise((resolve, reject) => {
    // 这里不需要使用索引,只要能循环出每一项就行
    for (const item of promises) {
      Promise.resolve(item).then(resolve, reject)
    }
  })
}
// 手写allSettled
Promise.MyAllSettled = function (promises) {
  let arr = [], count = 0
  return new Promise((resolve, reject) => {
    const processResult = (res, index, status) => {
      arr[index] = { status: status, val: res }
      count += 1
      if (count === promises.length) resolve(arr)
    }

    promises.forEach((item, i) => {
      Promise.resolve(item).then(res => {
        processResult(res, i, 'fulfilled')
      }, err => {
        processResult(err, i, 'rejected')
      })
    })
  })
}

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

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

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

var、let、const之间的区别

Object.freeze(obj) 
Object.defineProperty(object, 'property', { writable: false // 设置不允许写入 }) 
Object.seal(obj) // 不能增加、删除,只能修改
Proxy // 改写get、set

堆和栈的区别

栈 --- 自动分配相对固定大小的内存空间,并由系统自动释放
堆 --- 动态分配内存,内存大小不固定,也不会自动释放
栈存放基本数据类型,以及引用类型的指针
堆存放引用类型的数据

闭包的原理

MDN闭包一个函数以及其捆绑的周边环境状态的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。

个人理解:一个持有外部环境变量的函数就是闭包。
上级作用域内变量的生命周期,因为被下级作用域内引用而没有被释放,导致上级作用域内的变量,等到下级作用域执行完以后才正常得到释放。

闭包的用途
创建私有变量(防止全局变量污染)
延长变量的生命周期
PS --- 如果上下文不被销毁,那么存储的私有变量的值也不会被销毁,可以被其下级上下文中调取使用

闭包的缺点:由于上级作用域的变量未释放,过多使用闭包会导致内存泄漏

instanceof的实现原理

// 手写instanceof
function _instanceof(A, B) {
    if (typeof A !== 'object' || A === null || typeof B !== 'function') return false
    let proto = A.__proto__
    let prototype = B.prototype
    while(true) {
        if(proto === null) return false
        else if(proto === prototype) return true
        proto = proto.__proto__
    }
}

new的实现原理

// 手写new
function myNew(fn, ...args) {
    if(typeof fn !== 'function') throw new TypeError('this is not a function')
    let obj = {}
    obj.__proto__ = fn.prototype instanceof Object.prototype ? fn.prototype : Object.prototype
    const res = fn.call(obj, ...args)
    return res instanceof Object ? res : obj
}

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

基本类型:NumberStringBoolenNullUndefinedSymbolBigInt
引用类型:ObjectFunctionArrayDateRegExp等等

Array.isArray()、instanceofObject.prototype.toString.call()、constructor、__proto__

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

操作DOM的方法在封装的时候都返回this

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

原型
作用域

继承

// 原型链继承
Child.prototype = new Parent()
// 构造函数继承
function Child() {
    Parent.call(this)
}
// 组合式继承
2 + 1
// 原型式继承
function object(obj) {
    function F()
    F.prototype = obj
    return new F()
}
Object.create()
// 寄生式继承
function create(parent) {
    let obj = Object.create(parent)
    obj.prototype.fn = function() {}
    return obj
}
// 寄生组合式继承(class实现方案)
function inHerit(child, parent) {
    let prototype = Object.create(parent.prototype)
    prototype.constructor = child
    child.prototype = prototype
}
function Child() {
    Parent.call(this)
}
inHerit(Child, Parent)

CommonJs和ESM的区别

阮一峰ES6

手写防抖节流

防抖节流

script 标签中的 async 和 defer 属性

script