本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1、 防抖和节流
- 防抖 触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间。比如登录按钮避免用户点击太快,以致于发送了多次请求,需要防抖,只在最后一次点击后发送;掘金草稿之类的,在文本编辑器中实时保存,当无任何更改操作一秒后进行保存。
<div class="container">
<input type="text" id="searchText">
</div>
let timer = null
document.getElementById('searchText').addEventListener('input', function () {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
console.log(`发送查询请求$(this.value)`);
}, 1000)
})
- 节流
高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。控制事件发生的频率,如控制为 1s 发生一次,甚至 1 分钟发生一次。比如滑动
scroll事件,每隔一秒计算一次位置信息等。
<div class="container">
<input type="text" id="searchText">
</div>
let lastTime = null
document.getElementById('searchText').addEventListener('input', function () {
if (lastTime == null) {
console.log(`发送查询请求$(this.value)`);
lastTime = new Date().getTime()
} else {
let now = new Date().getTime()
if (now - lastTime > 2000) {
console.log(`发送查询请求$(this.value)`);
lastTime = new Date().getTime()
}
}
})
- 总结
防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零
clearTimeout。防抖可以比作等电梯,只要有一个人进来,就需要再等一会儿。业务场景有避免登录按钮多次点击的重复提交。节流:控制流量,单位时间内事件只能触发一次,与服务器端的限流 (Rate Limit) 类似。代码实现重在开锁关锁timer=timeout; timer=null。节流可以比作过红绿灯,每等一个红灯时间就可以过一批。 两者理解区别重点在于防抖是触发n秒后才执行,而节流是触发中每隔n秒只执行一次,一个是触发中,一个是触发后。
2、 理解拷贝之浅拷贝、深拷贝,怎么实现?
JS中的数据类型分为:
基本类型:string, number, boolean, null, undefined、symbol、bigint;
引用类型:Object,特殊的有Array, Function, Date, Math等。
基本数据类型: 名值存储在栈内存中;
引用数据类型: 名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值。
- 浅拷贝:简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝。
- 深拷贝:当修改A时,如果B不跟着变,说明这是深拷贝。 浅拷贝方法
- Object.assign(),拷贝的是对象的属性的引用,而不是对象本身。
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"
当object只有一层的时候,是深拷贝
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }
- 直接用=赋值
let a=[0,1,2,3,4],
b=a;
console.log(a===b); //true
a[0]=1;
console.log(b); //[1,1,2,3,4]
- 扩展运算符... 是一个 es6 / es2015特性,提供了一种非常方便的方式来执行浅拷贝,与 Object.assign ()的功能相同。(一层深拷贝,多层浅拷贝)。
let obj1 = { name: 'Kobe', address:{x:100,y:100}}
let obj2= {... obj1}
obj1.address.x = 200;
obj1.name = 'wade'
console.log('obj2',obj2) // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }
- Array.prototype.concat()
let arr = [1, 3, {
username: 'kobe'
}];
let arr2 = arr.concat();
arr2[2].username = 'wade';
console.log(arr); //[ 1, 3, { username: 'wade' } ]
- Array.prototype.slice()
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr); // [ 1, 3, { username: 'wade' } ]
深拷贝方法
- 对象只有一层的话可以使用上面的:Object.assign()函数
- 转成 JSON 再转回来
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false
- 递归拷贝
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);
- jquery jquery 提供$.extend可以用来做 Deep Copy。
var $ = require('jquery');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);
// false
3、 JS数据类型转换
js数据类型转换有两种 1、强制类型转换; 2、隐式转换
(1)强制转换
-
其他的数据类型转换为String
方式一:toString()方法
注意:null和undefined这两个值没有toString,如果调用他们的方法,会报错。var a = 123 a.toString()//"123" var b = null; b.toString()//"报错" var c = undefined c.toString()//"报错"Number 类型的 toString() 方法的基模式,可以用不同的基输出数字,例如二进制的基是 2,八进制的基是 8,十六进制的基是 16var iNum = 10; alert(iNum.toString(2)); //输出 "1010" alert(iNum.toString(8)); //输出 "12" alert(iNum.toString(16)); //输出 "A"方式二:String()函数
使用String()函数做强制类型转换时,对于Number和Boolean实际上就是调用的toString()方法,
但是对于null和undefined,就不会调用toString()方法,它会将null直接转换为"null",将undefined 直接转换为"undefined"var a = null String(a)//"null" var b = undefined String(b)//"undefined"String方法的参数如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串形式。
String({a: 1}) // "[object Object]" String([1, 2, 3]) // "1,2,3" -
其他的数据类型转换为Number
方式一:使用Number()函数
分成两种情况讨论,一种是参数是原始类型的值,另一种是参数是对象
(1)原始类型值①字符串转数字
如果是纯数字的字符串,则直接将其转换为数字
如果字符串中有非数字的内容,则转换为NaN
如果字符串是一个空串或者是一个全是空格的字符串,则转换为0
Number('324') // 324 Number('324abc') // NaN Number('') // 0②布尔值转数字:true转成1,false转成0
Number(true) // 1 Number(false) // 0③undefined转数字:转成NaN
Number(undefined) // NaN④null转数字:转成0
Number(null) // 0⑤Number() 接受数值作为参数,此时它既能识别负的十六进制,也能识别0开头的八进制,返回值永远是十进制值
Number(3.15); //3.15 Number(023); //19 Number(0x12); //18 Number(-0x12); //-18(2)对象
简单的规则是,Number方法的参数是对象时,将返回NaN,除非是包含单个数值的数组。
Number({a: 1}) // NaN Number([1, 2, 3]) // NaN Number([5]) // 5方式二:parseInt() & parseFloat()
这种方式专门用来对付字符串,parseInt()一个字符串转换为一个整数,可以将一个字符串中的有效的整数内容取出来,然后转换为Number。parseFloat()把一个字符串转换为一个浮点数。parseFloat()作用和parseInt()类似,不同的是它可以获得有效的小数。
console.log(parseInt('.21')); //NaN console.log(parseInt("10.3")); //10 console.log(parseFloat('.21')); //0.21 console.log(parseFloat('.d1')); //NaN console.log(parseFloat("10.11.33")); //10.11 console.log(parseFloat("4.3years")); //4.3 console.log(parseFloat("He40.3")); //NaNparseInt()在没有第二个参数时默认以十进制转换数值,有第二个参数时,以第二个参数为基数转换数值,如果基数有误返回NaN
console.log(parseInt("13")); //13 console.log(parseInt("11",2)); //3 console.log(parseInt("17",8)); //15 console.log(parseInt("1f",16)); //31有个题目可以参考一下
问题:["1", "2", "3"].map(parseInt) 答案是多少? 答案:[1, NaN, NaN] 为什么呢? map函数将数组的每个元素传递给指定的函数处理,并返回处理后的数组,所以['1','2','3'].map(parseInt) 就是将字符串1,2,3作为元素;0,1,2作为下标分别调用 parseInt 函数。即分别求出 parseInt('1',0), parseInt('2',1), parseInt('3',2)的结果。 parseInt('1',0)=1 , parseInt('2',1)=NaN, parseInt('3',2)=NaN.两者的区别:Number函数将字符串转为数值,要比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN。
parseInt('42ca') // 42 Number('42ca') // NaN上面代码中,parseInt逐个解析字符,而Number函数整体转换字符串的类型。
另外,对空字符串的处理也不一样Number(" "); //0 parseInt(" "); //NaN -
其他的数据类型转换为Boolean
Boolean(undefined) // false Boolean(null) // false Boolean(0) // false Boolean(NaN) // false Boolean('') // false Boolean({}) // true Boolean([]) // true Boolean(new Boolean(false)) // true
(2)隐式转换
- 自动转换为布尔值
JavaScript 遇到预期为布尔值的地方(比如if语句的条件部分),就会将非布尔值的参数自动转换为布尔值。系统内部会自动调用Boolean函数。 - 自动转换为数值
对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字。 - 自动转换为字符串
如果 + 的其中一个操作数是字符串(或者通过以上步骤最终得到字符串),则执行字符串拼接,否则执行数字加法。