奇技淫巧笔记¶
统计信息:字数 12018 阅读25分钟
原始笔记链接:https://cloud.seatable.cn/dtable/external-links/59b453a8639945478de2/
0002 [1,2,3].map(parseInt) 结果是什么¶
结果是 [1, NaN, NaN]
数组 Map 和 parstInt 的参数的说明以及返回值
array.map((item, index, arr) => return item );
let b = parseInt(a, 10); 第一个是数字,第二个是转换后的进制
基本结果是,1转换后是1,23转换后是NaN,实际上代码不会这样写,这个属于JS的奇技淫巧
0038 什么情况下a==1 a==2 a==3¶
考察第1个是等于等于表示前转换,需要进行数据类型的转换
需要把数字的转换成字符串进行比较,所以说这里需要改写数字对象圆形上的to string方法,来重写字符串
具体是可以令a是一个对象,然后a的two string方法变成当前属性加一
或者令a是一个数组是123,然后two string方法呢是数组的shift方法,依次返回1 233个数
0050 如何实现 1.add() 或者 1.minus()¶
数字对象原本没有这两个方法,需要改写数字对象的原型,给原型上增加 add 和 minus 函数即可
实际操作中很少改动原型对象
0053 JS 语句中多个等号的执行过程¶
一个JS表达当中有多个等号那么JS,表达式应该是从右向左直行
对象的应用类型是堆内存
复杂的内容可能涉及到一些编译原理,就是从右到左编译,这个在深入了解JavaScript里边的第1本有提到过
0074 JS 中 Proxy 是什么,有什么作用¶
Proxy 是内置的一个构造函数,主要用于数据绑定和拦截,实际开发中没有用到(获取或者设置对象的属性,需要先通过代理,可以进行拦截)
https://blog.csdn.net/weixin_44691513/article/details/108060781
let proxy = new Proxy({}, {key: value});
其中第一个参数表示拦截的对象,第二个参数表示处理操作的函数
这个操作就改变了对象的读写行为,获取任何属性都返回 20,设置任何属性,都放在 attr 上面
var proxy = new Proxy({}, {
get: function(target, property) {
return 20;
},
set: function(target, attr, value) {
// 一般用于对于要赋值的数进行过滤,加工,权限设置等
target.attr = value;
}
});
0143 v8垃圾回收¶
首先js因为是单线程,垃圾回收会占用主线程,导致页面卡顿,所以需要一个算法或者说策略,而v8采用的是分代式回收,而垃圾回收在堆中分成了很多部分用作不同的作用,回收主要表现在新老生代上,新生代就活得短一点的对象,老生代就活得长一点的对象。
在新生代里有一个算法,将新生代分成了两个区,一个 from,一个TO,每次经过 Scavenge 会将 from 区中的没引用的销毁,然后活着的 to 区**调换位置**,反复如此,当经过一次 acavange 后就会晋升的老生代还有个条件就是TO区的内存超过多少了也会晋升。
而老生代,采用**标记清除和标记整理**,但标记清除会造成内存不连续,所以会有标记整理取解决掉内存碎片,就是清理掉边界碎片。
为什么TO超过25%要晋升老生代?标记清除是怎么清除的?
第一个问题是为了不影响后续FORM空间的分配,第二个问题垃圾回收会构建一个根列表,从根节点去访问那些变量,可访问到位活动,不可就是垃圾
0172 0.1+0.2为什么不等于0.3?具体项目中怎么处理?¶
因为 JS 是弱类型语言,存储使用 64 位浮点数,这个都是基于二进制存储的(浮点数),可能就是2的多少次方。小数点是基于10进制存储的。
十进制和二进制转换过程中,会出现精度丢失,然后 JS 加法的时候需要进制转换,造成了这个问题
解决的办法
1、因为前面的部分正确,只有后面末尾不正确,那么使用 toFixed 处理即可,可以解决简单的运算
+(0.1 + 0.2).toFixed(1)
2、可以使用第三方的库 number precious
3、未来 ES 新的提案中支持大数字和精确计算,参考:https://zhuanlan.zhihu.com/p/225490777
实际场景:
1、科研运算,使用 number-precious
2、金融运算(两位小数),先转换成分,然后计算,计算后再换成元
0308 0.1 + 0.2 === 0.3 ?¶
JavaScirpt 使用 Number 类型来表示数字(整数或浮点数),遵循 IEEE 754 标准,通过 64 位来表示一个数字(1 + 11 + 52)
- 1 符号位,0 表示正数,1 表示负数 s
- 11 指数位(e)
- 52 尾数,小数部分(即有效数字)
最大安全数字:Number.MAX_SAFE_INTEGER = Math.pow(2, 53) - 1,转换成整数就是 16 位,所以 0.1 === 0.1,是因为通过 toPrecision(16) 去有效位之后,两者是相等的。
在两数相加时,会先转换成二进制,0.1 和 0.2 转换成二进制的时候尾数会发生无限循环,然后进行对阶运算,JS 引擎对二进制进行截断,所以造成精度丢失。
所以总结:精度丢失可能出现在进制转换和对阶运算中
https://juejin.im/post/5b90e00e6fb9a05cf9080dff
0446 setTimeout 避免循环打印¶
setTimeout 循环打印
《你不知道的JS》第一部分第五章——闭包。经典的案例如下
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000)
}
这个会打印出10个10,如何解决?
可以使用ES6的 let 形成块级作用域,这样可以正常打印
for (var i = 0; i < 10; i++) {
(function() {
var j = i;
setTimeout(function() {
console.log(j + 1);
}, j)
})();
}
或者使用 IIEF 创建临时的作用域,然后使用中间变量 j 缓存一下
for (var i = 0; i < 10; i++) {
(function() {
var j = i;
setTimeout(function() {
console.log(j + 1);
}, j)
})();
}
如果改成一个变量,可以把变量 i 作为参数,传入到 IEFF 中立即执行(创建了临时的函数作用域实现)
for (var i = 0; i < 10; i++) {
(function(j) {
setTimeout(function() {
console.log(j + 1);
}, j)
})(i);
}
0440 安卓手机连接苹果电脑传文件¶
点击链接下载
android file transfer
然后连接安卓手机即可显示对应的文件
https://dl.google.com/dl/androidjumper/mtp/current/androidfiletransfer.dmg
0306 webGL 是什么¶
webGL:内嵌在浏览器中,编写三维图形程序
相关的第三方库:Three.js:JavaScript 3D WebGL库
https://juejin.cn/post/7245682364932554808
https://juejin.cn/post/7208849836186435640
目前项目没使用,暂时不看。
0332 ES6 中 Reflect 是什么?怎么使用¶
Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy handler (en-US) 的方法相同。
与大多数全局对象不同 Reflect
并非一个构造函数,所以不能通过 new 运算符对其进行调用,或者将 Reflect
对象作为一个函数来调用。Reflect
的所有属性和方法都是静态的(就像 Math 对象)。
实际使用不是很多。
// 返回这个对象自身的属性
Reflect.ownKeys(TABLE_ALTERNATE_HIGHLIGHT_CLASS_MAP);
// 判断这个对象是否有这个属性,返回布尔值
Reflect.has(obj, key);
// 给对象设置新的属性
Reflect.set(obj, key, value);
参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
0333 ES6 中 Proxy 是什么?怎么使用¶
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
这个语法主要在写库使用,实际产品使用场景中用的不多。
const p = new Proxy(obj, handlerFn)
实际案例:当对象中不存在属性名时,默认返回值为 37
const handler = {
get: function (obj, prop) {
return prop in obj ? obj[prop] : 37;
},
};
const p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;
console.log(p.a, p.b); // 1, undefined
console.log("c" in p, p.c); // false, 37
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
0379 删除一个文件删不掉,可能是什么原因¶
问题:删除一个文件删不掉,可能是什么原因?
可能没有删除的权限(是否有系统管理员权限,是否文件或者文件夹加密)这个查看一下文件信息的权限
可能删除后,定时任务判断没有文件,会重新创建这个文件(造成界面上文件删除不掉)常见于日志文件或者配置文件,这个需要查看创建文件的日期和文件内容,以及定时任务脚本等
0394 parseInt 函数第二个参数是什么¶
parseInt 函数¶
parseInt(number, index)
函数作用:把一个变量转换成整数
参数:第一个是传入的变量,第二个是转换的进制(可选参数),默认是10进制。'2' 转换1进制是 NaN,’3‘ 转换为2进制是 NaN
如果什么也不传,那么也返回 NaN
例题: [1,2,3].map(parseInt),结果是 [1, NaN, NaN]
0395 arguments.callee 是什么¶
这个是早期 JS 的语法,实际工程使用不多
arguments.callee 使用¶
使用转转反侧法计算两个数的最大公约数时,看到这样一个代码
function gcd(a, b) {
if (a % b === 0) {
return b;
}
return arguments.callee(b, a % b);
}
console.log(gcd(28, 12)); // 4
console.log(gcd(7890, 123456)); // 6
console.log(gcd(5, 13)); // 1 (公约数为1说明两数互质)
其中 arguments.callee 不会经常使用,这个属性未来可能废弃,查询资料如下:
Arguments 表示函数的参数。arguments 有一个属性 cellee 表示函数参数的指针(指向当前的函数)那么这样写相当于递归调用函数。这样写的好处:如果函数名变化后,函数内部的代码不需要改动(arguments.callee),主要在递归调用函数中使用。
例子:我们使用这个优化一下斐波那契函数:
原函数递归调用
function factorial(num){
if (num <= 1) {
return 1;
} else {
return num * factorial(num - 1);
}
}
使用 arguements.callee 的函数
function factorial(num){
if (num <=1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
这个方法在 eslint 中弃用,原因:访问 arguments 是个很昂贵的操作,因为它是个很大的对象,每次递归调用时都需要重新创建,影响浏览器的性能,还会影响闭包。
0397 中文输入法的键盘事件¶
中文输入法¶
手机输入法中,大部分都是 229 事件码,无法直接监听符号或者字母(后退正常),其他键已经被输入法封装了,所以Keycode无效。
PC端中如果是中文输入法,那么键盘事件监听到的字母键 keycode 也是 229,这个也需要注意。
0468 git log 改成简化版本的 git lg¶
git log 改成简化版本的 git lg
https://luolei.org/better-git-log/#comments
git log 改成简化版本的 git log 加入下面的软连接配置
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
0471 VScode 清除多余空行¶
VScode 清除多余空行¶
需要选择正则按钮,然后全局替换:使用正则表达式 ^\s*(?=\r?$)\n
^\s*(?=\r?$)\n