pocket-lodash
pocket-lodash copied to clipboard
lodash源码分析之assignValue
本文为读 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 不存在,或者原来的值 objValue 和 value 不相等时,就可以将 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 ,传入的值为 -0 ,assignValue 会将 0 更新成 -0 。
可以看到,lodash 现有的代码是将处理正负零的逻辑放在第一个分支内。
但是假设原来的值 objValue 和 传入的值刚好是一对正负零,则 eq 得到的结果为 true,此时要进入第一个分支的判断,hasOwnProperty 要为 false,也即 objValue 是从原型链上取得的,此时应该直接调用 baseAssinValue 更新值即可。
所以在这个分支内的判断我觉得是毫无必要的。
如果 hasOwnProperty 为 true ,eq 也为 true,则会跳过第一个分支的逻辑,此时,应该判断一下传入的 value 是否等于 0(可以为正0,也可以为负0 ,如果为 0 ,再判断 objValue 和 value 是否同为正 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 的,其实可以去掉的。
因为第进入第三个分支,必须要 hasOwnProperty 为 true ,eq 也为 true 。
当 hasOwnProperty 为 true 时,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)
最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见: 
作者:对角另一面
截至目前源码: 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的时候才去赋值。
截至目前源码: 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 了