Make writing a habit together! This is my first day to participate in the “Gold Digging Day New Plan · April More text challenge”, click to see the details of the activity.


Let’s start with an interview question that you can stop and think about for yourself.

Interview question: Define add so that the following expression can successfully evaluate the result.

add[1] [2] [3] + 4 / / 10
add[10] [20] + 30 / / 60
add[1000] [2000] [3000] + 4000 / / 10000
Copy the code

At first glance, this looks like the creelization of add(1), (2), (3), and I’m sure you’re familiar with the creelization of functions. But this one is different, so how do you deal with it?

Now I’m going to take you through this problem with my own thought process.

Getter method?

Analyzing the expression structure, we can see that the form A [1] has several possibilities: from arrays, from ordinary objects, both of which involve values. A getter is a method that binds an object property to a function that will be called when the property is queried.

The GET syntax binds an object property to the function that will be called when the property is queried. – the MDN getter

So is it feasible? First look at the use of getters:

// Define an attribute of obj, Max, such that it always returns the maximum value in obj's arR attribute
const obj = {
  arr: [3.2.4.1].get max() {
    return Math.max.apply(null.this.arr)
  }
}
Copy the code

As you can see, getter methods can only get properties with fixed names (in this case, Max), whereas we need numbers with indefinite parameter names. Can you pass a number as a parameter through a getter method? Getter method does not take arguments:

The GET syntax must take no arguments. – the MDN

You can now rule out the use of getter methods.

The Proxy agent

To access the properties of an object, we need to work on access here, and ES6’s Poroxy proxy was born for this!

Take a look at MDN’s definition of Proxy:

Proxy objects are used to create a Proxy for an object to intercept and customize basic operations (such as property lookup, assignment, enumeration, function calls, and so on).

Property lookup, yeah, that’s what we need. We think of the form add[1] as getting an attribute, except that the attribute is a number. Object attributes can be found in two ways: obj.attr (attr must be a variable, not a literal) and obj[key] (key is a variable or literal). Add [1] matches the second method.

Here I just briefly describe the basic use of Proxy, please refer to the MDN documentation or chapter 9 of the Little Red Book for details.

For example, if the proxy proxies obJ, then the proxy becomes an intermediary, indirectly accessing OBJ through the proxy. Why add an intermediate carrier, can’t you access OBJ directly? This is because we can set up the proxy to do some interception operations, such as illegal access interception, to do some processing on the return result.

Proxy Syntax:

const proxy = new Proxy(target, handler)
Copy the code
  • target: Target object of the proxy
  • handler: An object with functions (methods) as attributes that define the behavior triggered by the agent when various operations are performed. It could be aget()Method, which defines the behavior that can be captured, intercepted, or further processed when obtaining/accessing/finding properties of the target objectgetCapture device.

Get capture parameters:

const handler = {
  get(trapTarget, property, receiver) {
      // trapTarget: Indicates the target object target
      // property: The property to access. In this case, it is all numbers
      // receiver: proxy itself, also known as proxy}}Copy the code

Proxy Examples of Proxy:

// Set a proxy that returns twice the value of foo every time it accesses the property foo
const target = { foo: 100 }
const handler = {
  get(trapTarget, property, receiver) {
    return 2 * trapTarget.foo
  }
}
const proxy = new Proxy(target, handler)
console.log(proxy.foo) / / 200
Copy the code

Ok, add that you didn’t know Proxy before, so now you can probably understand its basic usage (at least enough to understand the following solution for this problem).

Final solution

With that analysis and the basics, it’s time to solve the problem. (Please refer to the notes and key explanations below.)

// The proxied target object has an attribute sum, which is used for summing
const target = { sum: 0 }

// The handler argument defines a GET trap
const handler = {
  get(trapTarget, property, receiver) {
    // The + operation in the original expression triggers the agent's toPrimitive implicit conversion... (1)
    // Exists specifically in the proxy object as symbol.toprimitive
    if (property === Symbol.toPrimitive) {
      // The last time it triggered an addition, the result of the summation is returned
      // We need to store the result temporarily because we need to empty 0 for the next expression evaluation... (2)
      let temp = trapTarget.sum
      // Clear the sum property
      trapTarget.sum = 0
      // Symbol. ToPrimitive is an internal function of an object... (3)
      // We need to execute this function internally, so we have an arrow function that returns the cumulative sum
      return () = > temp
    }
    // Access attributes are numbers, will be converted to strings, so go back to numbers... (4)
    trapTarget.sum += Number(property)
    // Return the agent itself for the next access to add[1][2][3] continuous access
    return receiver
  }
}
const add = new Proxy(target, handler)
Copy the code

Key points:

(1) For add[1][2][3] + 4, the last step is the + operation, students familiar with implicit conversion must be immediately aware of the implicit conversion (unfamiliar can search within the site). The final addition must be a proxy object (receiver in step (5)) plus a number (4), and the object will trigger the toPrimitive implicit conversion, which will try to convert to a primitive type before it can add to 4.

Symbol.toPrimitive is a built-in Symbol value that exists as a function value attribute of an object and is called when an object is converted to its original value. – the MDN

Therefore, property === symbol. toPrimitive is judged in step (1). If toPrimitive is triggered, Symbol. Return the sum: traptarget.sum.

(2) The reason for clearing the sum attribute in the second step is that we always represent the same target object and the sum attribute is shared between different expressions. If we do not clear the sum attribute, the result of the next expression will be affected.

(3) Symbol.toprimitive is a function property inside the object that needs to be executed in get (to convert the object to its original type), so it needs to return a function: return () => temp, which returns sum.

(4) Object access numeric attribute (refers to through obj[key]) access, if the key is a number, will be implicitly converted to a string. Therefore, in step 4, the Number(property) needs to be converted to a Number to participate in the accumulation. Do not believe you can run the following code:

const obj = {'100': 'foo'}
console.log(obj[100]) // foo
Copy the code

The above is all the content of this topic, the more in-depth understanding and use of Proxy also please check the document to learn oh.


Code word is not easy, useful to you a thumbs-up 😀!

P.S. has digger friends have other solutions welcome to put forward! Corrections are also welcome!