你可能不在意的 javascript

759 阅读5分钟
以下的知识点 相对来说有点散,为文档笔记!

怎么判断一个变量是 NaN

var v = NaN

isNaN(v)  // true 但是 isNaN函数会首先尝试将这个参数转换为数值,然后才会对转换后的结果是否是NaN进行判断
// 举个🌰
isNaN('string') // true oops!

// 升级不转换类型
Number.isNaN(v) // true

// 或者
Object.is(v, NaN) // true

// 利用自身的特点
v !== v // true

怎么判断变量是 +0 还是 -0

var zero

// 直接api
Object.is(zero, 0) 

// 利用自身特性
1 / zero  // zero 若是 +0 则是 Infinity 否则是 -Infinity

上面的两个判断 都提及了 Object.is() 手动实现一波

if (!Object.is) {
  Object.is = function (x, y) {
    if (x === 0) {
      return 1 / x === 1 / y;
    } else if (x !== x) {   // 判断 NaN
      return y !== y;
    }
    return x === y;
  };
}

base64 转码

JavaScript 原生提供两个 Base64 相关的方法。

  • btoa():任意值转为 Base64 编码
  • atob():Base64 编码转为原来的值
var string = 'Hello World!';
btoa(string) // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh') // "Hello World!"

如果是非 ASCII 码字符转为 Base64 编码, 则需要进行转码

btoa('你好')  // error

// 需要转码
btoa(encodeURIComponent('你好')) // "JUU0JUJEJUEwJUU1JUE1JUJE"
decodeURIComponent(atob('JUU0JUJEJUEwJUU1JUE1JUJE')) // "你好"

借用函数输出字符串

(function () { /*
  line 1
  line 2
  line 3
*/}).toString().split('\n').slice(1, -1).join('\n')

// "line 1
// line 2
// line 3"

函数的同名参数, 参数名都是a。取值的时候,以后面的a为准,即使后面的a没有值或被省略,也是以其为准

function f(a, a) {
  console.log(a);  // undefined
  
  // 为了 取到第一个的值咋办呢  使用 arguments
  console.log(arguments[0])  // 1
}

f(1)

位运算符的几个小点 交换位置 取整

// 交换
var a = 1, b = 2
a ^= b
b ^= a
a ^= b

a // 2
b // 1

// 取整 (据说是最快取整) (直接 去掉小数点后面的值)
~~1.8 // 1
~~1.2 // 1
~~-1.2 // -1
~~-1.8 // -1

//  插播一道题
// 描述: 又一个数组(arr) 有偶数个(至少两个)a, 以及其他单个数值 b, 找出b
// var arr = [1, 1, 2, 1, 1] (返回 2) 或者 arr = [2, 2, 1] (返回 1) 或者 arr =[4, 4, 4, 4, 5] (返回 5)

// 法一
// 不太优雅
function find(arr) {
  let obj = {}
  arr.forEach((item) => obj[item] ? obj[item] ++ : obj[item] = 1)
  return +Object.entries(obj).filter(([key, value]) => value == 1)[0][0]
}

// 法二
// 就两种数值
function find(arr) {
  arr.sort((a, b) => a - b);
  return arr[0] == arr[1] ? arr[arr.length - 1] : arr[0];
}

// 法三
// 优雅
function find(arr) {
  return arr.reduce((acc, cur) => acc ^ cur);
}

console.log() 的一些占位符 不同类型的数据必须使用对应的占位符

* %s 字符串
* %d 整数
* %i 整数
* %f 浮点数
* %o 对象的链接
* %c CSS 格式字符串

// 举个🌰
var s = 'string',
    int = 1,
    i = 2,
    f = 2.2,
    o = {name: 'hello'},
    c = 'color: lightblue;'

console.log('%c hello world %s, %d, %i, %f, %o',c, s, int, i, f, o)

判断一个变量是否是对象 可以使用 Object()

value === Object(value) // 若是是对象则为 true

对空数组使用pop方法,不会报错,而是返回undefined

console.log([].pop()) // undefined

字符串的一些注意点

  • 如果参数为负数,或大于等于字符串的长度,charCodeAt返回NaN
  • split方法还可以接受第二个参数,限定返回数组的最大成员数
'abc'.charCodeAt(-1) // NaN
'abc'.charCodeAt(4) // NaN

'a|b|c'.split('|', 0) // []
'a|b|c'.split('|', 1) // ["a"]
'a|b|c'.split('|', 2) // ["a", "b"]
'a|b|c'.split('|', 3) // ["a", "b", "c"]
'a|b|c'.split('|', 4) // ["a", "b", "c"]

Date 的一些可以注意的点

  • 无论有没有参数,直接调用Date总是返回当前时间
  • Date.prototype.toLocaleDateString():本地日期 "2020/9/22"
  • d2.setDate(-1)表示设为上个月的倒数第二天,即12月30日
Date()    // "Wed Sep 23 2020 "
Date('s') // "Wed Sep 23 2020 "
Date(NaN) // "Wed Sep 23 2020 "

// 返回的是数字的年月日
new Date().toLocaleDateString() // "2020/9/23"

// setDate 的具体表现
var d = new Date ();
d // Wed Sep 23 2020 
d.setDate(-1) // 1598779043121
d // Sun Aug 30 2020 

JSON.parse方法可以接受一个处理函数,作为第二个参数

function f(key, value) {
  if (key === 'a') {
    return value + 10;
  }
  return value;
}

JSON.parse('{"a": 1, "b": 2}', f)
// {a: 11, b: 2}

call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象,apply参数为空、null和undefined,则默认传入全局对象

var n = 123
function a() {
  console.log(this.n);
}

a.call() // 123
a.call(null) // 123
a.call(undefined) // 123

另类的对象拷贝

function copyObject(orig) {
  return Object.create(
    Object.getPrototypeOf(orig),
    Object.getOwnPropertyDescriptors(orig)
  );
}

异步操作的流程控制

// 串行执行

var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];

function async(arg, callback) {
  console.log('参数为 ' + arg +' , 1秒后返回结果');
  setTimeout(function () { callback(arg * 2); }, 1000);
}

function final(value) {
  console.log('完成: ', value);
}

function series(item) {
  if(item) {
    async( item, function(result) {
      results.push(result);
      return series(items.shift());
    });
  } else {
    return final(results[results.length - 1]);
  }
}

series(items.shift());
// 并行执行
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];

function async(arg, callback) {
  console.log('参数为 ' + arg +' , 1秒后返回结果');
  setTimeout(function () { callback(arg * 2); }, 1000);
}

function final(value) {
  console.log('完成: ', value);
}

items.forEach(function(item) {
  async(item, function(result){
    results.push(result);
    if(results.length === items.length) {
      final(results[results.length - 1]);
    }
  })
});
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
var running = 0;
var limit = 2;

function async(arg, callback) {
  console.log('参数为 ' + arg +' , 1秒后返回结果');
  setTimeout(function () { callback(arg * 2); }, 1000);
}

function final(value) {
  console.log('完成: ', value);
}

function launcher() {
  while(running < limit && items.length) {
    var item = items.shift();
    async(item, function(result) {
      results.push(result);
      running--;
      if(items.length) {
        launcher();
      } else if(!running) {
        final(results);
      }
    });
    running++;
  }
}

launcher();

定时器的一些点

  • setTimeout的第二个参数如果省略,则默认为0。
  • setTimeout的参数。从第三个参数起,将在1000毫秒之后回调函数执行时,作为回调函数的参数
  • 生效后setInterval不会产生累积效应,即不会一下子执行多次,而是只会执行一次
setTimeout(f)  // 等同于 setTimeout(f, 0)

function f(a, b, c, d) { 
	console.log(a, b, c, d)   // 1, 2, 3 ,4  
}
setTimeout(f, 1000, 1, 2, 3, 4) 

// 对应上方第三点
setInterval(function () {
  console.log(2);
}, 1000);

sleep(3000);

function sleep(ms) {
  var start = Date.now();
  while ((Date.now() - start) < ms) {
  }
}

最后

以上的知识点来自 网道 的 从入门篇部分到异步操作部分 的梳理,常看常新。