pocket-lodash icon indicating copy to clipboard operation
pocket-lodash copied to clipboard

lodash源码分析之assignValue

Open HeftyKoo opened this issue 4 years ago • 2 comments

本文为读 lodash 源码的第一百一十六篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash

gitbook也会同步仓库的更新,gitbook地址:pocket-lodash

依赖

import baseAssignValue from './baseAssignValue.js'
import eq from '../eq.js'

《lodash源码分析之baseAssignValue》 《lodash源码分析之NaN不是NaN》

源码分析

assignValue 的作用是将 value 设置到 object 指定的 key 上。

源码如下:

const hasOwnProperty = Object.prototype.hasOwnProperty
function assignValue(object, key, value) {
  const objValue = object[key]

  if (!(hasOwnProperty.call(object, key) && eq(objValue, value))) {
    if (value !== 0 || (1 / value) === (1 / objValue)) {
      baseAssignValue(object, key, value)
    }
  } else if (value === undefined && !(key in object)) {
    baseAssignValue(object, key, value)
  }
}

要将 value 设置到指定的 key 上很简单,只要检测一下 object 上是否有这个 key ,如果这个 key 不存在,或者原来的值 objValuevalue 不相等时,就可以将 value 设置到 obj 上了。

const hasOwnProperty = Object.prototype.hasOwnProperty
function assignValue(object, key, value) {
  const objValue = object[key]

  if (!(hasOwnProperty.call(object, key) && eq(objValue, value))) {
    baseAssignValue(object, key, value)
  } else if (value === undefined && !(key in object)) {
    baseAssignValue(object, key, value)
  }
}

处理正负 0 问题

但是 eq 是不区分正负 0 的,也即eq(0, -0)true,而 assignValue 是要区分正负零的。

如果原来的值为 0 ,传入的值为 -0assignValue 会将 0 更新成 -0

可以看到,lodash 现有的代码是将处理正负零的逻辑放在第一个分支内。

但是假设原来的值 objValue 和 传入的值刚好是一对正负零,则 eq 得到的结果为 true,此时要进入第一个分支的判断,hasOwnProperty 要为 false,也即 objValue 是从原型链上取得的,此时应该直接调用 baseAssinValue 更新值即可。

所以在这个分支内的判断我觉得是毫无必要的。

如果 hasOwnPropertytrueeq 也为 true,则会跳过第一个分支的逻辑,此时,应该判断一下传入的 value 是否等于 0(可以为正0,也可以为负0 ,如果为 0 ,再判断 objValuevalue 是否同为正 0 或者同为负 0 ,如果不相同,那要也调用 baseAssignValue 来更新值。

源码修改如下:

function assignValue(object, key, value) {
  const objValue = object[key]

  if (
    !(hasOwnProperty.call(object, key) && eq(objValue, value)) || 
    (value === 0 && (1 / value !== 1 / objValue)) || 
    (value === undefined && !(key in object))
  ) {
    baseAssignValue(object, key, value)
  }
}

至于第三个分支,我觉得是永远不会为 true 的,其实可以去掉的。

因为第进入第三个分支,必须要 hasOwnPropertytrueeq 也为 true

hasOwnPropertytrue 时,key in object 也必定为 true ,也即 !(key in object) 必定为 false,那么 value === undefined && !(key in object) 必定为 false ,所以这个分支是永远都不会进入的。

以上是我个人的一点见解,也给 lodash 提了个 pr,如果有理解得不正确的地方,还请指正。

License

署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)

最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:

作者:对角另一面

HeftyKoo avatar Mar 09 '20 14:03 HeftyKoo

截至目前源码: function assignValue(object, key, value) { var objValue = object[key]; if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || (value === undefined && !(key in object))) { baseAssignValue(object, key, value); } } 第一、 和作者说的一样,第一个判断已经cover了+/- 0的情况。 第二、 in和hasOwnProperty的行为不一样,in会拿到原型链上的值,也就是说这里的意思是如果object本身没有key但是原型链上key有值,就不要使用undefined将原型链上的值覆盖掉,只有在value不为undefined的时候才去赋值。

KK-AI-LL avatar May 27 '21 06:05 KK-AI-LL

截至目前源码: function assignValue(object, key, value) { var objValue = object[key]; if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || (value === undefined && !(key in object))) { baseAssignValue(object, key, value); } } 第一、 和作者说的一样,第一个判断已经cover了+/- 0的情况。 第二、 in和hasOwnProperty的行为不一样,in会拿到原型链上的值,也就是说这里的意思是如果object本身没有key但是原型链上key有值,就不要使用undefined将原型链上的值覆盖掉,只有在value不为undefined的时候才去赋值。

第二点,要 !(hasOwnProperty.call(object, key) && eq(objValue, value))false,则 hasOwnProperty.call(object, key) 必定为 true,即 object 上必定有 key 这个属性,那 key in object 必定为 true

HeftyKoo avatar May 27 '21 07:05 HeftyKoo