壹题汇总每日五题 | 2021.8.18 有疑问

154 阅读8分钟

第 2 题:['1', '2', '3'].map(parseInt) what & why ?

parseInt()函数

parseInt()函数有两个参数,解析一个字符串参数,返回一个指定基数的整数或NaN。

const intValue = parseInt(string[, radix]);

如果第一个传入string的参数不是字符串,则使用ToString转换,忽略字符串开头的空白符。
第二个参数radix是介于2-36之间的整数。默认为10(十进制)。如果第二个参数为undefinded,或0,或未指定,JS处理方法为:
string以“0x/0X”开头,radix=16。
string以“0”开头,radix=8/10。(虽然ECMAScript 5 规定使用10,但具体由实现环境决定。因此永远要明确给出radix参数的值。)
string以非“0x/0X/0”的其他值开头,radix=10。

map() 函数

map() 方法创建一个新数组,新数组是原数组中的每个元素都调用回调函数的结果。

var new_array = arr.map(function callback(currentValue[,index[, array]]) {
 // Return element for new_array
 }[, thisArg])

currentValue 是数组中正在被处理的当前元素。
index可选, 是数组中正在被处理的当前元素的索引。
array可选,是map方法被调用的数组。
还有thisArg可选, 执行 callback函数时使用的this值。

返回值

['1', '2', '3'].map(parseInt)中,因为parseInt()函数有两个参数,所以map()currentValueindex传入parseInt()。也即:

['1', '2', '3'].map((item, index) => {
	return parseInt(item, index)
})

返回值依次为:

parseInt('1', 0) // 1
parseInt('2', 1) // NaN
parseInt('3', 2) // NaN, 3 不是二进制

所以['1', '2', '3'].map(parseInt) // [1,NaN, NaN]

如果实际上想要循环访问字符串数组, 该怎么办?

['10','10','10','10','10'].map(Number);
// [10, 10, 10, 10, 10]

第 3 题:什么是防抖和节流?有什么区别?如何实现?

防抖,动作绑定事件。在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

function debounce(func,time){
    let timer = null;
    return () => {
        clearTimeout(timer);
        timer = setTimeout(() => func.apply(this,arguments),time);
    }
}

节流,动作绑定事件。规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

function throtte(func,time){
    let activeTime = 0;
    return () => {
        let currentTime = Date.now();
        if(currentTime - activeTime > time){
            func.apply(this,arguments);
            activeTime = Date.now();
        }
    }
}

拓展资料:手写一个防抖函数 debounce

第 4 题:介绍下 Set、Map、WeakSet 和 WeakMap 的区别?

Set 和 Map 主要的应用场景在于 数据重组 和 数据储存

集合(Set)

  • ES6 新增的一种新的数据结构
  • 成员唯一且无序。使用“Same-value-zero equality”算法判断两值是否不同。与精确相等运算符(===)的主要区别是认为NaN等于自身。
  • Set 本身是一种构造函数,用来生成 Set 数据结构。

Set实例属性

  • constructor: 构造函数

  • size:元素数量 Set实例方法

  • 操作方法

    • add(value):新增,相当于 array里的push
    • delete(value):存在即删除集合中value
    • has(value):判断集合中是否存在 value
    • clear():清空集合
  • 遍历方法(遍历顺序为插入顺序)

    • keys():返回一个包含集合中所有键的迭代器
    • values():返回一个包含集合中所有值得迭代器
    • entries():返回一个包含Set对象中所有元素得键值对迭代器
    • forEach(callbackFn, thisArg):用于对集合成员执行callbackFn操作,如果提供了 thisArg 参数,回调中的this会是这个参数,没有返回值 Set 可默认遍历,默认迭代器生成函数是 values() 方法,所以可以使用 map、filter 方法。所以可实现交集(Intersect)、并集(Union)、差集(Difference)。
let set1 = new Set([1, 2, 3])
let set2 = new Set([4, 3, 2])

let intersect = new Set([...set1].filter(value => set2.has(value)))
let union = new Set([...set1, ...set2])
let difference = new Set([...set1].filter(value => !set2.has(value)))

console.log(intersect)	// Set {2, 3}
console.log(union)		// Set {1, 2, 3, 4}
console.log(difference)	// Set {1}

WeakSet

WeakSet 对象允许将弱引用对象储存在一个集合中。【什么意思?】

  • WeakSet 只能储存对象引用,不能存放值,而 Set 对象都可以

  • WeakSet 对象中储存的对象值都是被弱引用的,即垃圾回收机制不考虑 WeakSet 对该对象的应用。WeakSet 对象里有多少个成员元素,取决于垃圾回收机制有没有运行。ES6 规定 WeakSet 不可遍历,也没有办法拿到它包含的所有元素。 属性:

  • constructor:构造函数,任何一个具有 Iterable 接口的对象,都可以作参数 方法:

  • add(value):在WeakSet 对象中添加一个元素value

  • has(value):判断 WeakSet 对象中是否包含value

  • delete(value):删除元素 value

  • clear():清空所有元素,注意该方法已废弃

字典(Map)

  • 集合、字典 都可以储存不重复的值
  • 集合 以 [value, value]的形式储存,字典 以 [key, value] 的形式储存 任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。
const set = new Set([
  ['foo', 1],
  ['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1

const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3

只有对同一个对象的引用,Map 结构才将其视为同一个键。【那应该怎么写?】

const map = new Map();

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

属性:

  • constructor:构造函数

  • size:返回字典中所包含的元素个数 方法:

  • 操作方法:

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

    • Keys():将字典中包含的所有键名以迭代器形式返回
    • values():将字典中包含的所有数值以迭代器形式返回
    • entries():返回所有成员的迭代器
    • forEach():遍历字典的所有成员 Map默认遍历器接口(Symbol.iterator属性)是entries方法。

WeakMap

键是弱引用对象,而值可以是任意的。

  • 键值依然是正常引用的。

  • key 是不可枚举的。键是弱引用对象,没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的)。 属性:

  • constructor:构造函数

方法:

  • has(key):判断是否有 key 关联对象
  • get(key):返回key关联对象(没有则则返回 undefined)
  • set(key):设置一组key关联对象
  • delete(key):移除 key 的关联对象

总结

  • Set

    • 成员唯一、无序且不重复
    • [value, value],只有键值,没有键名
    • 可以遍历,方法有:add、delete、has
  • WeakSet

    • 成员都是对象
    • 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏
    • 不能遍历,方法有add、delete、has
  • Map

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

    • 只接受对象作为键名(null除外),不接受其他类型的值作为键名
    • 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
    • 不能遍历,方法有get、set、has、delete

第 7 题:ES5/ES6 的继承除了写法以外还有什么区别?

ES6子类可以通过__proto__寻址到父类;ES5通过__proto__只能寻址到Function.prototype。

//ES6:
class Super {};
class Sub extends Super {};

const sub = new Sub();
Sub.__proto__ === Super;
//ES5:
function Super(){};
function Sub(){};

Sub.prototype = new Super();
Sub.prototype.constructor = Sub;

var sub = new Sub();
Sub.__proto__ === Function.prototype;

第 21 题:Object.prototype.toString.call() instanceof 以及Array.isArray()3 个判断数组的方法,请分别介绍它们之间的区别和优劣

Object.prototype.toString.call()

原理:每一个继承自 Object 的对象都有 toString 方法,如果方法没有重写,会返回 [Object type],其中 type 为对象的类型。只有Object类型的对象才适用,其他类型直接调用toString方法,返回内容的字符串。故而要用call或apply方法改变调用的toString方法。【call 方法的第一个参数会被当作 this,所以 arr.toString() 与 Object.prototype.toString.call(arr) 并没有改变 this,而是改变了调用的函数。什么意思?】

const an = ['Hello','An'];
an.toString(); // "Hello,An"
Object.prototype.toString.call(an); // "[object Array]"
  • 可以判断所有基本数据类型,即使是null和undefined。【疑问:原始值也继承自Object吗?是指原始值包装函数吗?】
  • 常用于判断浏览器内置对象。
  • 不能校验自定义类型。
function Animal (){}
let a = new Animal()
Object.prototype.toString.call(a)
"[object Object]"
Object.prototype.toString.call('An') // "[object String]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call({name: 'An'}) // "[object Object]"

instanceof

原理:判断对象的原型链中是不是能找到类型的 prototype

  • 不能判断原始类型。【原始类型/基本数据类型还是原始值?】
  • 所有对象类型【即引用类型?】 instanceof Object 都是 true。
  • 对象的原型可以随意修改,instanceof判断并不准确。

Array.isArray()

  • 当检测Array实例时,Array.isArray 优于 instanceof ,因为 Array.isArray 可以检测出 iframes
  • Array.isArray()是ES5新增的方法,当不存在 Array.isArray() ,可以用 Object.prototype.toString.call() 实现。

附:typeof()

  • 只能检测基本数据类型,包括boolean、undefined、string、number、symbol。检测null只能返回object。
  • 引用类型除了function都不能区分。