Will (a===1 && a===2 && a===3) (strict comparison) ever be true (in JavaScript)

This paper is an extension and solution of the JS classic problem (A == 1&&a == 2&&a ==3)(loose equality)

How to use getter/setter descriptors to set (a===1 && a===2 && a=== 3) to true

We first briefly understand this JS classic problem, and then solve its extension problem.

Content Overview:

  • Review (a==1&&a==2&&a==3)(loose equality) problem
    • (a==1&&a==2&&a==3) Problem description
    • Answers and Explanations
  • (a===1&&a===2&&a===3) Extension problem
    • Answers and explanations
      • What is a property descriptor
  • Refer to the article

Review (a==1&&a==2&&a==3)(loose equality) problem

If you already know about the problem and know how to solve the JS puzzle (yes, it’s just a puzzle, I don’t want to see such a use case in production code), then you can skip to the next section and read about its extension problem. There is a discussion about this issue on Reddit, and here are some of the funniest comments I’ve seen

“If I saw this code in the code base, I would be desperate.

(a== 1&&a == 2&&a ==3) questions

Can (a == 1&&a == 2&&a ==3) be true?

The answer is yes, as shown in the following code

const a = { value : 0 };
a.valueOf = function() {
    return this.value += 1;
};

console.log(a==1 && a==2 && a==3); //true
Copy the code

In general, the purpose of asking this question in an interview is not to memorize the answer, but to see how candidates think about the question and whether they have any knowledge of Javascript’s peculiar syntax for ==.

Problem resolution

The secret is the loose equality operator ==

In JS, loose equality == converts the left and right values to the same primitive type and then compares them for equality. After the conversion (which is required for one or both sides of ==), the final equality match performs the same judgment as the === symbol. Loose equality is invertible, and for any value A and B, usually A == B has the same behavior as B == A (except for the order of transformation). You can learn more about loose match == and strict match === in depth here.

How does Javascript cast this value?

When a cast is performed to compare two values, let’s first look at the built-in conversion function.

ToPrimitive(input, PreferredType?)
Copy the code

The optional parameter PreferredType specifies the final cast type, which can be Number or String, depending on whether the result of the ToPrimitive() method returns Number or String.

The conversion of values is as follows

  1. This value is returned if Input is of primitive type
  2. If the input variable is of type Object, then input.valueof () is called. If the return result is a primitive type, the reference is returned
  3. Call input.toString() if neither. Return it if the result is primitive
  4. If none of the above is possible, a type error is thrownTypeErrorFailed to convert the input variable to the base type.

If the PreferredType is Number, the conversion algorithm will execute in the order described above, and if it is String, step 2 and Step 3 will swap the order. The PreferredType is a default value, and if not entered, Date is treated as String and other variables as Number. The default valueOf returns this, and the default toString() returns type information.

This is how the + and == operators call toPrimitive().

So in the code above, as the JS engine parses, a == 1, which is the basic type, the JS engine will try to convert a to Number, and then in the algorithm above, A.valueof is called and returns 1(increments by 1 and returns itself). The same type conversion occurs at a==2 and a==3 and increments itself.

(a === 1 && a === 2 && a === 3)(strict match) problem

Can (a === 1 && a === 2 && a ===3) also be true?

Of course you can, see the code below

var value = 0; //window.value
Object.defineProperty(window, 'a', {
    get: function() {
        returnthis.value += 1; }}); console.log(a===1 && a===2 && a===3) //true
Copy the code

Question to explain

From the solution of the classic problem, we know that primitive types in JS no longer satisfy the above condition (strict equality without conversion process), so we need some way to call a function and do what we want in this function. But executing a function usually requires () after the function name. And since this is not loose equality ==, valueOf will not be called by the JS engine. Emmm, a little tricky. Fortunately, Property functions, especially getter descriptors, provide a solution to this problem.

What are property descriptors?

There are two types of property descriptors, data descriptors and access descriptors.

  1. Data descriptor

    Enforces key-value

    Optional key values

     - configurable
     - enumable
     - writeable
    Copy the code

    example

    {
        value: 5.writable: true
    }
    Copy the code
  2. Access descriptor

    Enforce key values – get/set or both – Confiturable – Enumerable

    {
        get: function () { return 5; },
        enumerable: true
    }
    Copy the code

An example of access descriptors on MDN

 // Example of an object property added
    // with defineProperty with an accessor property descriptor

    var bValue = 38;

    Object.defineProperty(o, 'b', {
        // Using shorthand method names (ES2015 feature).
        // This is equivalent to:
        // get: function() { return bValue; },
        // set: function(newValue) { bValue = newValue; },
        get() { return bValue; },
        set(newValue) { bValue = newValue; },
        enumerable: true.configurable: true
    });
    o.b; / / 38
    // 'b' property exists in the o object and its value is 38
    // The value of o.b is now always identical to bValue,
    // unless o.b is redefined
Copy the code

In the solution to the problem, we define a property for the Object using Object.defineProperty. You can delve into the syntax and definition of Object.defineProperty here. Interestingly, get and set are methods that can be called with the “.” operator. For example, A has a property b with a getter that can be called just like any other property of the object, similar to a.B. This solves our original problem. We need to call a function without (). With the get attribute, we can call a function without adding () after the function name.

In the solution mentioned above, we define a property of A with a getter on the window object, so that A can be accessed directly in the code (global variables), and therefore the value of A can be obtained directly. If we define attribute A on another object instead of window, such as object1, we need to change the title to object1.a=== 1&&object1.a === 2&&object1.a ===3.

Github Gist

  • Problem (a==1 && a==2 && a==3)
  • Problem (a===1 && a===2 && a===3)

reference

  • Reddit Post
  • Speaking JS
  • MDN web-docs

IVWEB Technology Weekly shocked online, pay attention to the public number: IVWEB community, weekly timing push quality articles.

  • Collection of weekly articles: weekly
  • Team open source project: Feflow