Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lodash源码分析之assignValue #117

Open
HeftyKoo opened this issue Mar 9, 2020 · 2 comments
Open

lodash源码分析之assignValue #117

HeftyKoo opened this issue Mar 9, 2020 · 2 comments
Labels
internal 内部函数或方法 系列文章

Comments

@HeftyKoo
Copy link
Owner

HeftyKoo commented Mar 9, 2020

本文为读 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)

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

作者:对角另一面

@KK-AI-LL
Copy link

截至目前源码:
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的时候才去赋值。

@HeftyKoo
Copy link
Owner Author

截至目前源码:
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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
internal 内部函数或方法 系列文章
Projects
None yet
Development

No branches or pull requests

2 participants