JavaScript中的奇葩知识(28道)
source link: https://zhuanlan.zhihu.com/p/317193617
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
久经沙场的前辈们,写了无数代码,踩了无数的坑。但有些坑,可能一辈子也踩不到摸不着,因为根本不会发生在业务代码里~~
1
Function.prototype 竟然是个函数类型。而自定义函数的原型却是对象类型。
typeof Function.prototype === 'function'; // true function People() {} typeof People.prototype === 'object'; // true
所以我们设置空函数可以这么做:
// Good const noop = Function.prototype; // Bad const noop = () => {};
2
一个变量真的会不等于自身吗?
const x = NaN; x !== x // true
这是目前为止 js 语言中唯一的一个不等于自己的数据。为什么?因为 NaN
代表的是一个范围,而不是一个具体的数值。在早期的 isNaN()
函数中,即使传入字符串,也会返回 true
,这个问题已经在 es6 中修复。
isNaN('abc'); // true Number.isNaN('abc') // false
所以如果您想兼容旧浏览器,用 x !== x 来判断是不是NaN,是一个不错的方案。
3
构造函数如果 return
了新的数据
// 不返回 function People() {} const people = new People(); // People {} // 返回数字 function People() { return 1; } const people = new People(); // People {} // 返回新对象 function Animal() { return { hello: 'world', }; } const animal = new Animal(); // { hello: 'world' }
在实例化构造函数时,返回 非对象 类型将不生效
4
.call.call 到底在为谁疯狂打call?
function fn1() { console.log(1); } function fn2() { console.log(2); } fn1.call.call(fn2); // 2
所以 fn1.call.call(fn2)
等效于 fn2.call(undefined)
。而且无论您加多少个 .call
,效果也是一样的。
5
实例后的对象也能再次实例吗?
function People() {} const lili = new People(); // People {} const lucy = new tom.constructor(); // People {}
因为 lili
的 原型链
指向了 People
的原型,所以通过向上寻找特性,最终在 Peopel.prototype
上找到了构造器即 People
自身
6
setTimeout 嵌套会发生什么奇怪的事情?
console.log(0, Date.now()); setTimeout(() => { console.log(1, Date.now()); setTimeout(() => { console.log(2, Date.now()); setTimeout(() => { console.log(3, Date.now()); setTimeout(() => { console.log(4, Date.now()); setTimeout(() => { console.log(5, Date.now()); setTimeout(() => { console.log(6, Date.now()); }); }); }); }); }); });
在0-4层, setTimeout
的间隔是 1ms
,而到第 5 层时,间隔至少是 4ms
。
7
es6函数带默认参数时将生成 声明作用域
var x = 10; function fn(x = 2, y = function () { return x + 1 }) { var x = 5; return y(); } fn(); // 3
8
函数表达式 (非函数声明)中的函数名不可覆盖
const c = function CC() { CC = 123; return CC; }; c(); // Function
当然,如果设置 var CC = 123
,加声明关键词是可以覆盖的。
9
严格模式下,函数的 this
是 undefined
而不是 Window
// 非严格 function fn1() { return this; } fn1(); // Window // 严格 function fn2() { 'use strict'; return this; } fn2(); // undefined
对于模块化的经过webpack打包的代码,基本都是严格模式的代码。
10
取整操作也可以用按位操作
var x = 1.23 | 0; // 1
因为按位操作只支持32位的整型,所以小数点部分全部都被抛弃
11
indexOf()
不需要再比较数字
const arr = [1, 2, 3]; // 存在,等效于 > -1 if (~arr.indexOf(1)) { } // 不存在,等效于 === -1 !~arr.indexOf(1);
按位操作效率高点,代码也简洁一些。也可以使用es6的 includes()
。但写开源库需要考虑兼容性的道友还是用 indexOf
比较好
12
getter/setter
也可以动态设置吗?
class Hello { _name = 'lucy'; getName() { return this._name; } // 静态的getter get id() { return 1; } } const hel = new Hello(); hel.name; // undefined hel.getName(); // lucy // 动态的getter Hello.prototype.__defineGetter__('name', function() { return this._name; }); Hello.prototype.__defineSetter__('name', function(value) { this._name = value; }); hel.name; // lucy hel.getName(); // lucy hel.name = 'jimi'; hel.name; // jimi hel.getName(); // jimi
13
0.3 - 0.2 !== 0.1 // true
浮点操作不精确,老生常谈了,不过可以接受误差
0.3 - 0.2 - 0.1 <= Number.EPSILON // true
14
class
语法糖到底是怎么继承的?
function Super() { this.a = 1; } function Child() { // 属性继承 Super.call(this); this.b = 2; } // 原型继承 Child.prototype = new Super(); const child = new Child(); child.a; // 1
正式代码的原型继承,不会直接实例父类,而是实例一个空函数,避免重复声明动态属性
const extends = (Child, Super) => { const fn = function () {}; fn.prototype = Super.prototype; Child.prototype = new fn(); Child.prototype.constructor = Child; };
15
es6居然可以重复解构对象
const obj = { a: { b: 1 }, c: 2 }; const { a: { b }, a } = obj;
一行代码同时获取 a
和 a.b
。在a和b都要多次用到的情况下,普通人的逻辑就是先解构出 a
,再在下一行解构出 b
。
16
判断代码是否压缩居然也这么秀
function CustomFn() {} const isCrashed = typeof CustomFn.name === 'string' && CustomFn.name === 'CustomFn';
17
对象 ===
比较的是内存地址,而 >=
将比较转换后的值
{} === {} // false // 隐式转换 toString() {} >= {} // true
18
intanceof 的判断方式是原型是否在当前对象的原型链上面
function People() {} function Man() {} Man.prototype = new People(); Man.prototype.constructor = Man; const man = new Man(); man instanceof People; // true // 替换People的原型 People.prototype = {}; man instanceof People; // false
如果您用es6的class的话,prototype原型是不允许被重新定义的,所以不会出现上述情况
19
Object.prototype.__proto__ === null; // true
这是原型链向上查找的 最顶层
,一个 null
20
parseInt
太小的数字会产生 bug
parseInt(0.00000000454); // 4 parseInt(10.23); // 10
21
1 + null // 1 1 + undefined // NaN Number(null) // 0 Number(undefined) // NaN
22
arguments 和形参是别名关系
function test(a, b) { console.log(a, b); // 2, 3 arguments[0] = 100; arguments[1] = 200; console.log(a, b); // 100, 200 } test(2, 3);
但是您可以用 use strict
严格模式来避免这一行为,这样 arguments
就只是个副本了。
23
void 是个固执的老头
void 0 === undefined // true void 1 === undefined // true void {} === undefined // true void 'hello' === undefined // true void void 0 === undefined // true
跟谁都不沾亲~~
24
try/catch/finally 也有特定的执行顺序
function fn1() { console.log('fn1'); return 1; } function fn2() { console.log('fn2'); return 2; } function getData() { try { throw new Error(''); } catch (e) { return fn1(); } finally { return fn2(); } } console.log(getData()); // 打印顺序: 'fn1', 'fn2', 2
在 try/catch
代码块中,如果碰到 return xxyyzz;
关键词,那么 xxyyzz
会先执行并把值放在临时变量里,接着去执行 finally
代码块的内容后再返回该临时变量。如果 finally
中也有 return aabbcc
,那么会立即返回新的数据 aabbcc
。
25
是否存在这样的变量 x
,使得它等于多个数字?
const x = { value: 0, toString() { return ++this.value; } } x == 1 && x == 2 && x == 3; // true
通过隐式转换,这样不是什么难的事情。
26
clearTimeout
和 clearInterval
可以互换~~~~使用吗
var timeout = setTimeout(() => console.log(1), 1000); var interval = setInterval(() => console.log(2), 800); clearInterval(timeout); clearTimeout(interval);
答案是: YES
。大部分浏览器都支持互相清理定时器,但是建议使用对应的清理函数。
27
下面的打印顺序是?
setTimeout(() => { console.log(1); }, 0); new Promise((resolve) => { console.log(2); resolve(); }).then(() => console.log(3)); function callMe() { console.log(4); } (async () => { await callMe(); console.log(5); })();
答案是:2, 4, 3, 5, 1
主线任务:2,4
微任务:3,5宏任务:1
28
null
是 object
类型,但又不是继承于 Object
,它更像一个历史遗留的 bug
。鉴于太多人在用这个特性,修复它反而会导致成千上万的程序出错。
typeof null === 'object'; // true Object.prototype.toString.call(null); // [object Null] null instanceof Object; // false
脑袋空了,想到再加。。。
原作者姓名: 原罪
原出处:segmentfault
原文链接: Javascript 里的奇葩知识
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK