# proposal-logical-assignment

A proposal to combine Logical Operators and Assignment Expressions:

``````// "Or Or Equals" (or, the Mallet operator :wink:)
a ||= b;
a || (a = b);

// "And And Equals"
a &&= b;
a && (a = b);

// "QQ Equals"
a ??= b;
a ?? (a = b);
``````

Current Stage: 4

## Motivation

Convenience operators, inspired by Ruby's. We already have a dozen mathematical assignment operators, but we don't have ones for the often used logical operators.

``````function example(a) {
// Default `a` to "foo"
if (!a) {
a = 'foo';
}
}
``````

If statements work, but terseness would be nice. Especially when dealing with deep property assignment:

``````function example(opts) {
// Ok, but could trigger setter.
opts.foo = opts.foo ?? 'bar'

// No setter, but 'feels wrong' to write.
opts.baz ?? (opts.baz = 'qux');
}

example({ foo: 'foo' })
``````

With this proposal, we get terseness and we don't have to suffer from setter calls:

``````function example(opts) {
// Setters are not needlessly called.
opts.foo ??= 'bar'

// No repetition of `opts.baz`.
opts.baz ??= 'qux';
}

example({ foo: 'foo' })
``````

## Semantics

The logical assignment operators function a bit differently than their mathematical assignment friends. While math assignment operators always trigger a set operation, logical assignment embraces their short-circuiting semantics to avoid it when possible.

``````let x = 0;
const obj = {
get x() {
return x;
},

set x(value) {
console.log('setter called');
x = value;
}
};

// This always logs "setter called"
obj.x += 1;
assert.equal(obj.x, 1);

// Logical operators do not call setters unnecessarily
// This will not log.
obj.x ||= 2;
assert.equal(obj.x, 1);

// But setters are called if the operator does not short circuit
// "setter called"
obj.x &&= 3;
assert.equal(obj.x, 3);
``````

In most cases, the fact that the set operation is short-circuited has no observable impact beyond performance. But when it has side effects, it is often desirable to avoid it when appropriate. In the following example, if the `.innerHTML` setter was triggered uselessly, it could result in the loss of state (such as focus) that is not serialized in HTML:

``````document.getElementById('previewZone').innerHTML ||= '<i>Nothing to preview</i>';
``````

See discussion of short-circuit semantics in #3. It also highlights differences already present in mathematical assignment operators in code like `obj.deep[key++] += 1` vs `obj.deep[key] = obj.deep[key++] + 1`.