- 1. ==判断规则
- 2. Object的属性key的类型
- 3. valueOf与toString的执行
- 3. 写一个函数sum满足如下情况
- 4. 箭头函数的this指向问题
- 从输入url到页面完成加载发生了什么?">5. 从输入url到页面完成加载发生了什么?
- 6. 写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?">6. 写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?
- 7. 什么是防抖和节流?有什么区别?如何实现?">7. 什么是防抖和节流?有什么区别?如何实现?
- 8. 如何判断this的指向
- 9. JSON.parse(JSON.stringify())的缺点
- 10. 对象与数组的遍历
1. ==判断规则
[ ] == ![ ] 的运算过程以及隐形转换的规则是什么
2. Object的属性key的类型
var a = {}
var b = {}
a[b] = 4
// {[object Object]: 4}
3. valueOf与toString的执行
var obj = {
toString: () => { console.log('string'); return { } },
valueOf: () => { console.log('valueOf'); return { } },
}
// obj*3 结果是什么
因为会先调用 valueof
获取值,但因返回不是基本类型,无法计算,会再去调用 toString
获取值,同样也无法计算,所以最后报错
// valueOf
// string
// Uncaught TypeError: Cannot convert object to primitive value
3. 写一个函数sum满足如下情况
sum(1)(2) === 3
sum(1)(2)(3) == 6
sum(1)(2)(3)(4) == 10
function sum() {}
4. 箭头函数的this指向问题
var a = 10
var obj = {
a: 20,
say: () => {
console.log(this.a)
}
}
var anotherObj = { a: 30 }
obj.say() // 10
obj.say.apply(anotherObj)() // 10
obj.say.bind(anotherObj)(); // 10
obj.say.bind().bind(anotherObj); // 10
箭头函数的this是指向父作用域的 this
,是在声明的时候就已确定,而不是运行的时候。同时箭头函数的 this
不能被 apply
或 bind
改变。 bind
方法只有调用一边时才有用,多次调用无效
5. 从输入url到页面完成加载发生了什么?
6. 写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?
vue和react都是采用diff算法来对比新旧虚拟节点,从而更新节点。在vue的diff函数中(建议先了解一下diff算法过程)。
在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点(这里对应的是一个key => index 的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快。
7. 什么是防抖和节流?有什么区别?如何实现?
- 防抖
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
- 思路:
每次触发事件时都取消之前的延时调用方法
function debounce(fn) {
let timeout = null; // 创建一个标记用来存放定时器的返回值
return function () {
clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn.apply(this, arguments);
}, 500);
};
}
function sayHi() {
console.log('防抖成功');
}
var inp = document.getElementById('inp');
inp.addEventListener('input', debounce(sayHi)); // 防抖
- 节流
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
- 思路:
每次触发事件时都判断当前是否有等待执行的延时函数
function throttle(fn) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
canRun = false; // 立即设置为false
setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
8. 如何判断this的指向
9. JSON.parse(JSON.stringify())的缺点
1、如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2、如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor
6、如果对象中存在循环引用的情况也无法正确实现深拷贝
10. 对象与数组的遍历
var a = { a: 1 };
var b = { b: 2 };
b.__proto__ = a;
Object.defineProperty(b, "c", {
value: 3
});
b[Symbol()] = 4;
// ["b"] 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性).
Object.keys(b);
// b : 2 a : 1 循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)
for (var i in b) {
console.log(i, ":", b[i]);
}
// ["b", "c"] 返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性).
Object.getOwnPropertyNames(obj);
// ["b", "c", Symbol()] 返回一个数组,包含对象自身的所有属性,不管属性名是Symbol或字符串,也不管是否可枚举.
Reflect.ownKeys(b);
// 数组
var arr=[1,2,3,4];
// 使用forEach
arr.forEach(function(val, index) {
console.log(val, index);
});
// 使用for..in..
// 还会找原型链上的可枚举属性
for (var i in arr){
console.log(i,":",arr[i]);
}
// 使用for-of遍历
// 不仅支持数组,还支持大多数类数组对象,例如DOM NodeList对象.
// 也支持字符串遍历,它将字符串视为一系列的Unicode字符来进行遍历.
for (var value of arr){
console.log(value);
}