JS
Map和Set的区别,Map和Object的区别
map是键值对,键是唯一的,迭代顺序按set()方法插入的顺序- 键的比较基于
零值相等,即Object.is(),逻辑与===相似,区别在于
+0 === -0为true,NaN === NaN为false
Object.is(+0,-0)为false,Object.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的迭代对象 -
Map和Object的区别 | | 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?|原生的由Object到JSON的序列化支持,使用JSON.stringify()。
原生的由JSON到Object的解析支持,使用JSON.parse()。| -
Set对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
Set对象是值的集合,可以按照插入的顺序迭代它的元素。Set中的元素是唯一的。
Set判断两值是否相等,逻辑同===,区别在于NaN和NaN被视为相同的值 -
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有哪些新特性
说一下对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定义的对象的属性也不能被修改该怎么做?
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
}
数据类型有哪些?如何判断一个数据是否是数组
基本类型:Number、String、Boolen、Null、Undefined、Symbol、BigInt
引用类型:Object、Function、Array、Date、RegExp等等
Array.isArray()、instanceof、Object.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)