你的浏览器不支持canvas

Love You Ten Thousand Years

编写自己的代码库

Date: Author: M/J

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自小可嗒嗒的博客

做好基本总结,对于每一个程序员来讲都是重要的。最近看到一篇文章,文中说。

前端工程师要忘记前端二字。

我深为折服,也颇为赞同。程序员所面临的无非是一个个问题,所做的也是解决一个一个难题。所谓语言、框架、工具、策略都是为了解决问题而服务的。很多人觉得JavaScript只能做一些写写脚本这一类前端的事,如果单纯这样去考虑,眼光未免太狭隘了。JavaScript大有所为,只有你想不到的,没有你做不到的,只有开拓语言的新应用范围,才能做到创造力,而不是单纯的紧跟时代所趋或追求于所谓的再创造力。

周杰伦有一次接受采访,说,我从不关心流行,我创造流行(有待考据)。盲目去追求时代热潮也是不可取的,但这并不意味你要固步自封。良好的知识储备是必要的,是一切发展的基石。所以,不要看我们是前端程序员,算法、数据库、编译原理、操作系统等都应该具备。

所以,勉励自己。

1. 数组操作

1.1. 数组元素的最大最小值

我们知道对象Math有两个最大值最小值方法Math.max()Math.min(),接受一系列数字,返回给定数字的最大值最小值。

Math.max([value1[, value2[, ...]]])

但是需要注意的几点是:

  • 不接收数组参数
  • 对于非Number类型,会进行隐式类型转换。如果其中某个参数不能转换成数字,返回NaN
  • 没有给定参数,返回-Infinity(对Math.max()而言,Math.min()则返回Infinity)

所以我们采用Math.max()的思路(以Math.max()为例),关键变成如何对数组进行处理。

1.1.1. 使用apply

我们知道,apply()接收一个数组参数。

theFunction.apply(valueForThis, arrayOfArgs)

apply()会unwrap参数的第一层[](?why)。

所以方法如下:

let arr = [6,4,1,8,1,11,23];
function getMaxOfArray(numArray){
    return Math.max.apply(null, numArray);
}
getMaxOfArray(arr); // 23

1.1.2. es6的…操作符

ES6新增一个spread operator,可以展开数组元素。

let arr = [6,4,1,8,1,11,23];
let max = Math.max(...arr); // 23

1.1.3. Array.reduce()

let arr = [6,4,1,8,1,11,23];
let max = arr.reduce((a, b) => Math.max(a, b)); //23

1.1.4. 排序

这种是最容易想到的方式了,对数组元素进行排序,最大最小的分别在数组两端。排序,我们可以用Array.prototype.sort()方法。

let arr = [6,4,1,8,1,11,23];
arr.sort((a,b) => a-b);
let max = arr[arr.length-1]; //23

参考: Math.max()


2. 对象操作

2.1. 对象的深拷贝

由于 Object 是引用类型,所以对对象的简单拷贝是引用拷贝,也就是浅拷贝。所谓深拷贝,指的就是我们将对象所在的内存空间的内容重新拷贝了一份。

拓展,有些数组方法不会对原数组造成影响的原因,就是因为在内部对数组进行了一次深拷贝处理。

2.1.1. Underscore.js 的实现

Underscore.js 库有一个 snapshot 是进行对象的深拷贝处理。代码的实现如下(自己有重构)。

function deepClone(obj) {
  var temp = {};
  //If obj is primary types
  if (obj == null || typeof(obj) != "object"){
    return obj;
  }
  //If obj is an object
  for (var key in obj) {
    if (obj.hasOwnProperty(key)){ //Make sure key is not from of Prototype
      temp[key] = deepClone(obj[key]);  //Recursion
    }
  }
  return temp;
}

但还是有点问题,如果传入的是一个 Array 类型、Date 类型和 RegExp 类型会将它们转换为简单对象(虽然它们也是对象,如数组只是属性从 0 开始逐个递增的对象而已),但我们还是不太想这样。这种情况下,我们可以对 Array 类型和 Date 类型进行一次判定。

需要额外注意的是,如果是嵌套 Array 类型,也要进行递归。

function deepClone(obj) {
  var temp;
  //If obj is primary types
  if (obj == null || typeof(obj) != "object"){
    return obj;
  }
  //If obj is Date type
  if (obj instanceof Date) {
    temp = new Date();
    temp.setTime(obj.getTime());
    return temp;
  }
  //If obj is Array type
  if (obj instanceof Array){
    temp = [];
    for (var item of obj){
      temp.push(deepClone(item)); //Considering nested array
    }
    return temp;
  }
  //Otherwise
  temp = {};
  for (var key in obj) {
    if (obj.hasOwnProperty(key)){ //Make sure key is not from of Prototype
      temp[key] = deepClone(obj[key]);  //Recursion
    }
  }
  return temp;
}

即便如此,上述代码对于对象方法还是无能为力

2.1.2. 利用 JSON 的解析和序列化

JSON 对象有两个方法: stringify()parse()。stringfy() 用于将 JavaScript 对象序列化为 JSON 字符串,而 parse() 用于将 JSON 字符串解析为原生 JavaScript 字符串。

var cloneObj = JSON.parse(JSON.stringify(obj));

但是它也有缺点,会忽略掉值为 undefined 以及函数表达式。

第三方库的实现

Underscore – _.clone() jQuery – &.extend() 调用 $.extend(true, {}, obj);就可以进行深复制 lodash – _.clone()_.cloneDeep(),这个库效果要更好。

参考:


对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。