- 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) === 3sum(1)(2)(3) == 6sum(1)(2)(3)(4) == 10function sum() {}
4. 箭头函数的this指向问题
var a = 10var obj = {a: 20,say: () => {console.log(this.a)}}var anotherObj = { a: 30 }obj.say() // 10obj.say.apply(anotherObj)() // 10obj.say.bind(anotherObj)(); // 10obj.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则returncanRun = false; // 立即设置为falsesetTimeout(() => { // 将外部传入的函数的执行放在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];// 使用forEacharr.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);}
