按位和运算符如何在javascript中处理对象?

How does bitwise-and operator work on objects in javascript?

我正在开发一个开源项目,遇到了位运算符。我确实理解这些原则,而且JavaScript将非零整数值计算为真(如果我错了,请在这里更正我;在另一篇文章中的一个可接受的答案中找到这条语句)。

相关代码如下:

1
2
3
4
5
6
7
8
9
10
function example(){
  var args = arguments;
  var j = 1 & args[0];
  .
  .
  .
}

var test = {keyA: 1, keyB: 2};
example(test);

我的问题是:j的价值是多少?

对象的二进制等价物是什么?

据我所知,如果test中的最后一个位为0,则为j = 0;如果test中的最后一个位为1,则为j = 1

请帮帮我,伙计们。我花了最后一个小时在这里搜索任何几乎相关的帖子,大多数主题是关于数字的,一两个是关于布尔值的,就是这样。

编辑:由于上面给出的代码示例似乎不像我想的那么清楚,这里是我找到的完整功能(和工作):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function Extend() {
    var args = arguments;
    var target;
    var options;
    var propName;
    var propValue;
    var deep = 1 & args[0];
    var i = 1 + deep;
    target = args[i - 1] || {};

    for (; i < args.length; i++) {
        // Only deal with non-null/undefined values
        if (options = args[i]) {
            // Extend the base object
            for (propName in options) {
                propValue = options[propName];

                if (propValue !== undefined) {
                    propValue = options[propName];
                    target[propName] = (deep && IsPlainObject(target[propName])) ? Extend(deep, {}, propValue) : propValue;
                }
            }
        }
    }

    // Return the modified object
    return target;
}

var _someVariable = Extend({keyA: 1, keyB: 2, keyC: 10}, _someOtherVariable);

deep在决定是否进入for循环时具有一定的意义。或者没有。


deep变量的目的是检测这两个调用之间的差异:

1
Extend({keyA: {a: 1}, keyB: 2}, {keyC: 3, keyA: {b: 2}});

1
Extend(true, {keyA: {a: 1}, keyB: 2}, {keyC: 3, keyA: {b: 2}});

第一个变量将返回:

1
{keyA: {b: 2}, keyB: 2, keyC: 3}

第二个目的是返回:

1
{keyA: {a: 1, b: 2}, keyB: 2, keyC: 3}

因此,函数实际上允许第一个可选参数,这将使函数递归地应用扩展,从而得到一个深的而不是浅的扩展对象。您还可以通过分析递归调用来了解这种意图,其中第一个参数是deep,第二个参数是要扩展的对象,第三个参数是要扩展的对象。下一行还显示了这一点:

1
var i = 1 + deep;

由于i是循环的起点,所以可以看出,如果deep设置为1而不是0,则循环将从参数2开始处理,这是有意义的,因为参数0被认为是可选参数,下一个是目标对象。请注意,函数接受一个可变数量的附加参数,它将使用这些参数来扩展目标对象。在这些论点的基础上,i变量循环。

附带说明:由于一个bug,第二个版本返回与第一个版本相同的结果。要修复错误,请更换

1
2
target[propName] = (deep && IsPlainObject(target[propName]))
    ? Extend(deep, {}, propValue) : propValue;

用:

1
2
target[propName] = (deep && IsPlainObject(target[propName]))
    ? Extend(deep, target[propName], propValue) : propValue;

现在,从本质上来说:

1
var deep = 1 & args[0];

位运算符的使用必须是一种将效率规则置于清晰度之上的想法。其目的是,如果第一个参数表示可选参数,则将deep设置为1,这应该是一个布尔值,指示扩展应该发生在浅或深的地方。因为对象会使这个表达式的值为0,所以这似乎是一个很好的技巧。但这有一个问题。如果有人想这样做:

1
Extend(["3"], {myattr: 2});

我们希望使用附加的自定义属性myattr返回类似数组的对象:

1
{0:"3", length: 1, myattr: 2}

但是,当前的Extend函数会将第一个参数误解为执行深度扩展的指令。这是因为1 & ["3"]1 & 3进行评价,评价为1。因此,结果将是第二个没有任何扩展发生的参数:

1
{myattr: 2}

结论:最好避免使用这种隐式的位运算符,并这样做:

1
var deep = args.length > 0 && typeof args[0] === 'boolean' && args[0];

在公共语言中:当至少有一个参数且该参数是布尔值且其值为真时,让deep为真(1)。在所有其他情况下,将其设为假(0)。注意,不能将false作为第一个参数传递,因为它会使函数执行一个浅扩展。在这种情况下,该参数将被视为要扩展的对象,但将失败。因此,可选的第一个参数(如果提供)必须是值为true的布尔值。这对于原始的Extend函数和修正的函数都是正确的。

最后,最好向该函数添加注释,以澄清第一个可选参数的用法。


位运算符可以处理32位(非)有符号整数。如果传入的内容不是数字,则会隐式强制为数字。这是使用对象的valueOf/toString方法完成的,对于您的对象,{keyA: 1, keyB:2}将生成NaN。然后,这个数字被转换成一个(u)Int32,它给0表示NaN

检查toUint32的规格,按照ToNumber的规格进行操作。