11

get这个方法,在lodash中是出场率很高的方法,初识起来有些疑惑,看了demo,又很直观。

get方法源码链接

下边是它的使用说明

 Gets the value at `path` of `object`. If the resolved value is
  `undefined`, the `defaultValue` is returned in its place.

根据object对象的path路径获取值。如果解析值是undefined,就返回一个默认的值(defaultValue)

 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
 *
 * _.get(object, 'a[0].b.c');
 * // => 3
 *
 * _.get(object, ['a', '0', 'b', 'c']);
 * // => 3
 *
 * _.get(object, 'a.b.c', 'default');
 * // => 'default'

暂时不考虑第第三个参数,只考虑第二个参数,该参数的含义就是路径。 指向object的路径。

这个get方法事实上等同于我们取object上的某个属性的值。object['a'][0]['b']['c'] 或者 object.a[0].b.c.

如果把复杂对象当成一个树来看,['a'][0]['b']['c']抑或是a[0].b.c都是树上的一个个树枝,树枝的终端可以另一个树枝,或者是一个具体的果实.

那该如何实现一个get方法呢。首先,第一个参数是具体的对象,第二个参数是一个路径,该路径我们当然会有一个约定俗成的格式,既足够直观理解,又便于我们格式化处理。当然还要避免错误处理,返回相应的默认值。

lodash的实现如下

function get(object, path, defaultValue) {
  var result = object == null ? undefined : baseGet(object, path);
  return result === undefined ? defaultValue : result;
}

export default get;

这里引入了一个baseGet。 get => baseGet

import castPath from './_castPath.js';
import toKey from './_toKey.js';


function baseGet(object, path) {
  path = castPath(path, object);

  var index = 0,
      length = path.length;

  while (object != null && index < length) {
    object = object[toKey(path[index++])];
  }
  return (index && index == length) ? object : undefined;
}

我们先不去看castPath的方法,就能很轻易的判断它返回的应该是一个路径相关的数组。

比如

// -var object = { 'a': [{ 'b': { 'c': 3 } }] };
['a', '0', 'b', 'c']
//

随着while遍历,object都会被重新赋值。最终取到我们目标的指

  return (index && index == length) ? object : undefined;

确认path中有确定想取的值。否则就是取不到值,undefined。

接下来我们可以考虑下castPath到底做了什么了。
/**
 * Casts `value` to a path array if it's not one.
 * value如果不是数组,就将它转换成一个路径 数组。
 * @private
 * @param {*} value The value to inspect. 被检查的值
 * @param {Object} [object] The object to query keys on. 被查询对象是否有对应的key。
 * @returns {Array} Returns the cast property path array. 返回被转换的路径数组
 */

function castPath(value, object) {
  if (isArray(value)) {
    return value;
  }
  return isKey(value, object) ? [value] : stringToPath(toString(value));
}

isKey(value, object) 应该是用来判断value是object的key,如果是,那二话不说直接返回了。

换做是我,我要如何写这个isKey,可能我不会单独写一个方法。

粗心点,我可能直接就写object[value]来判断了。

isKey比我们想的多的多。看看它是怎么写的

/** Used to match property names within property paths. */
var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
    reIsPlainProp = /^\w*$/;
//reIsDeepProp 用来匹配属性名里包含路径属性  例如什么. [] \这种
/**
 * Checks if `value` is a property name and not a property path.
 * 检查value是一个属性名,不是属性路径
 * @private
 * @param {*} value The value to check. 被检查的值
 * @param {Object} [object] The object to query keys on.
 * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
 */
function isKey(value, object) {
  if (isArray(value)) { // 数组,直接返回false
    return false;
  }
  var type = typeof value; // 对应的集中类型直接返回true,看到没,没有字符串,没有字符串。如果你考虑到
  if (type == 'number' || type == 'symbol' || type == 'boolean' ||
      value == null || isSymbol(value)) {
    return true;
  }
  return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
    (object != null && value in Object(object));
}

return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
    (object != null && value in Object(object));

先判断是字符串,而且不是那种包含.[]

value in Object(object))判断value是Object的key,返回一个bol

还剩下一个stringToPath

stringToPath(toString(value));

很显然,它的作用是将类似于"a[0]b.c"规则的字符串转换成数组路径["a", "0", "b", "c"]

var stringToPath = memoizeCapped(function(string) {
  var result = [];
  if (string.charCodeAt(0) === 46 /* . */) {
    result.push('');
  }
  //主要是下边这两行
  string.replace(rePropName, function(match, number, quote, subString) {
    result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
  });
  return result;
});

正则表达式的知识在自己补充吧。简单说就是匹配出我需要的 a,0,b,c.

至于memoize下一篇再说吧。


最普通的一个
301 声望41 粉丝

永远不要做你擅长的事。