Javascript 里的奇葩知识

久经沙场的前辈们,写了无数代码,踩了无数的坑。但有些坑,可能一生也踩不到摸不着,由于根本不会发生在业务代码里~~javascript

1

Function.prototype 居然是个函数类型。而自定义函数的原型倒是对象类型。java

typeof Function.prototype === 'function';  // true

function People() {}
typeof People.prototype === 'object';      // true

因此咱们设置空函数能够这么作:webpack

// Good 
const noop = Function.prototype;

// Bad
const noop = () => {};

2

一个变量真的会不等于自身吗?es6

const x = NaN;
x !== x  // true

这是目前为止js语言中惟一的一个不等于本身的数据。为何?由于NaN表明的是一个范围,而不是一个具体的数值。
在早期的 isNaN() 函数中,即便传入字符串,也会返回true,这个问题已经在es6中修复。web

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?oop

function fn1() {
  console.log(1);
}

function fn2() {
  console.log(2);
}

fn1.call.call(fn2); // 2

因此 fn1.call.call(fn2) 等效于 fn2.call(undefined)。并且不管您加多少个 .call,效果也是同样的。this

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

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

脑壳空了,收集中,可遇不可求。。。

相关文章
相关标签/搜索