Sharing of interview questions from large factories: how to make the value of (a = = 1 & & A = = 2 & & A = = 3) true?

Keywords: Front-end Interview

When I first saw this topic, I was shocked. I analyzed that it was very unreasonable for us to program, and thought it was impossible. Variable a should be equal to the three values of 1, 2 and 3 at the same time under the same situation. This is a myth, no less than Goldbach's conjecture of 1 + 1 = 1, but everything is possible. Out of curiosity, After thinking for a long time, I decided to try a solution.

My idea comes from another similar interview question I encountered earlier:

// Set a function to output the following value
f(1) = 1;
f(1)(2) = 3;
f(1)(2)(3) = 6;

At that time, the solution was implemented using toString or valueOf. Let's first review toString and valueOf methods to facilitate us to understand this type of problem more deeply:

For example, we have an object. Without overriding the toString() method and valueOf() method, the output of the Node or browser is like this

class Person {
  constructor() {
    this.name = name;
  }
}

const best = new Person("Kobe");
console.log(best); // log: Person {name: "Kobe"}
console.log(best.toString()); // log: [object Object]
console.log(best.valueOf()); // log: Person {name: "Kobe"}
console.log(best + "GiGi"); // log: [object Object]GiGi
best Person
best.toString() [object Object]
best.valueOf() Person
best + 'GiGi' [object Object]GiGi

From the above output, we can observe a detail. toString() outputs [object Object], while valueOf() outputs the Person object itself. When it comes to best +'gigi ', it actually outputs [object] Gigi. We can preliminarily infer that it is the magic work of operator +?

In order to verify our inference in the previous step, we make a slight change and copy the valueOf method once:

class Person {
  constructor(name) {
    this.name = name;
  }
  // Duplicate valueOf method
  valueOf() {
    return this.name;
  }
}
best Person
best.toString() [object Object]
best.valueOf() Person
best + 'GiGi' KobeGiGi

This time, there is only one different result from the above, that is, the last best + 'GiGi' results have changed after copying the valueOf() method. From this, we can see that the essence of the object has not changed fundamentally, but when it is used as a direct operation, its value is obtained from the copied valueOf(), And continue to participate in subsequent operations.

Of course, don't forget that we also have a toString() method, so we also copy it to see if the results will also be affected:

class Person {
  constructor(name) {
    this.name = name;
  }
  valueOf() {
    return this.name;
  }
  toString() {
    return `Bye ${this.name}`;
  }
}
best Person
best.toString() Bye Kobe
best.valueOf() Kobe
best + 'GiGi' KobeGiGi

We found that the best + 'GiGi' still hasn't changed, and we still use the result of our last replication of valueOf()

In fact, we have rewritten the valueOf method and do not necessarily call the return value of valueOf() for calculation. Instead, the value returned by valueOf will be calculated according to this value only when it is a basic data type. If it is not a basic data type, the value returned by toString() method will be used for calculation.

class Person {
  constructor(name) {
    this.name = name;
  }
  valueOf() {
    return this.name;
  }
  toString() {
    return `Bye ${this.name}`;
  }
}
const best = new Person({ name: "Kobe" });

console.log(best); // log: Person name: {name: "Kobe"}
console.log(best.toString()); // log: Bye [object Object]
console.log(best.valueOf()); // log: Person {name: "Kobe"}
console.log(best + "GiGi"); // log: [object Object]GiGi
best Person
best.toString() Bye [object Object]
best.valueOf() {name: "Kobe"}
best + 'GiGi' Bye [object Object]GiGi

Looking at the above example, the name passed in now is an object new person ({Name: "Kobe"}), which is not the basic data type. Therefore, when performing the addition operation, take the value returned by the toString() method for calculation. Of course, if there is no valueOf() method, the toString() method will be executed.

So after laying the groundwork for so long, we have to uncover the answer. We use the above principles to solve this problem:

class A {
  constructor(value) {
    this.value = value;
  }
  toString() {
    return this.value++;
  }
}
const a = new A(1);
if (a == 1 && a == 2 && a == 3) {
  console.log("Hi Eno!");
}

It's relatively simple here. Directly rewrite the toString() method. Because there is no valueOf(), it will execute the result of toString() when it makes an operation judgment that a == 1.

class A {
  constructor(value) {
    this.value = value;
  }
  valueOf() {
    return this.value++;
  }
}
const a = new A(1);
if (a == 1 && a == 2 && a == 3) {
  console.log("Hi Eno!");
}

Of course, you can also replace toString with valueOf, and the effect is the same:

class A {
  constructor(value) {
    this.value = value;
  }
  valueOf() {
    return this.value++;
  }
}

const a = new A(1);
console.log(a);
if (a == 1 && a == 2 && a == 3) {
  console.log("Hi Eno!");
}

Therefore, when an object performs operations (such as addition, subtraction, multiplication and division, and judging equality), it often has the problem of calling valueOf() or toString. A function is usually hidden behind the variable of this object.

Of course, the principle of the following problem is actually the same. Attach the solution:

// Set a function to output the following value
f(1) = 1;
f(1)(2) = 3;
f(1)(2)(3) = 6;

function f() {
  let args = [...arguments];
  let add = function() {
    args.push(...arguments);
    return add;
  };
  add.toString = function() {
    return args.reduce((a, b) => {
      return a + b;
    });
  };
  return add;
}
console.log(f(1)(2)(3)); // 6

Of course, it's not over yet. Here are some special solutions. In fact, when using an object, if the object is an array, the above logic will still hold, but toString() will become an implicit call to the join() method. In other words, if the object is an array, if you don't rewrite other toString() methods, The default implementation is to call the return value of the join () method of the array as the return value of toString (), so a new solution is added to this problem, that is, without copying toString (), copy the join () method and turn it into a shift() method, which can delete the first element of several groups from it and return the value of the first element.

class A extends Array {
  join = this.shift;
}
const a = new A(1, 2, 3);
if (a == 1 && a == 2 && a == 3) {
  console.log("Hi Eno!");
}

Our exploration is not over yet. Careful students will find that our topic is how to make the value of (a = = 1 & & A = = 2 & & A = = 3) true, but the above discussion is about the case of loose equality = =. Will the above results be different in the case of strict equality = =?

The answer is different. You can try to change the above relaxed conditions to strict debugging and try again to know the result.

class A extends Array {
  join = this.shift;
}
const a = new A(1, 2, 3);
// ==After changing to = = =:
if (a === 1 && a === 2 && a === 3) {
  console.log("Hi Eno!"); // Hi Eno! It never happened again
}

So how to solve the situation at this time? We can consider using Object.defineProperty to solve this problem. This method, which is well known because of Vue, is also an old knowledge point in the interview. We can use it to hijack the a variable. When we get its value, let it increase, and then the problem can be solved easily:

var value = 1;
Object.defineProperty(window, "a", {
  get() {
    return this.value++;
  }
});

if (a === 1 && a === 2 && a === 3) {
  console.log("Hi Eno!");
}

Above, we hijack a on the global window. Every time a makes a judgment, it will trigger the get attribute to obtain the value, and each time it obtains the value, it will trigger the function to increase automatically once, and the judgment will increase automatically three times, so the formula will be established in the end.

Of course, there are other methods. Here is another example, for example, using hidden characters to deceive the interviewer:

var aᅠ = 1;
var a = 2;
var ᅠa = 3;
if (aᅠ == 1 && a == 2 && ᅠa == 3) {
  console.log("Hi Eno!");
}

The above solution is very confusing. If you are not careful, you will think it is three identical a values. In fact, it is essentially to define three different a values. There are hidden characters before and after a, so when debugging, please copy and paste the above code for debugging. If you play by hand in Chrome, you can use special means to put one or two red dots behind a, and when you enter, The debugging tool will hide these traces, so as to hide them and show the interviewer who hasn't responded for a moment.

Finally, I wish you all a good job in the new year. The above code will not be used in practice, but it may be enlightening to explore the infinity of JS. It is also suggested that the interviewer should not use this kind of interview questions to embarrass the interviewer~

If the articles and notes can give you a little help or inspiration, please don't be stingy with your praise and collection. Your is certainly the biggest driving force for me to move forward 😁

Note links are attached to read more high-quality articles in previous periods. You can check them step by step. You can praise and encourage me if you like:

Posted by callesson on Thu, 28 Oct 2021 15:29:30 -0700