SoFunction
Updated on 2025-03-06

JS new operation principle and handwritten function simulation implementation example

introduction

We know thatES6Before(ES5),JavaScriptThe manifestation of the class in the class is the constructor.

JavaScriptWhat is the constructor in it?

  • The constructor is alsoNormal functions, from the perspective of expression, is no different from ordinary functions (except as convention, the first letter of the constructor name is usually capitalized);
  • But if a normal function is called by the new operator, then this function is calledConstructor

principle

If a function isnewThe operator is called, then it will perform the following operations:

  • Create a new object in memory;
  • Put the new object inside[[Prototype]]Assigning attribute values ​​to constructorsprototypeproperty;
  • Put the constructor insidethisAssign value to this new object (i.e.thispoint to a new object);
  • Execute code inside the constructor (add attributes to the new object);
  • If the constructor returns a non-empty object, it returns the object; otherwise, it returns the new object just created;

Handwritten function simulation new

Next, based on the above principle, we try to write a function by hand and simulate the implementation.newFunction of operator.

Basic implementation of v1

Let's use it firstES5syntax to implement:

function useNewOperator() {
  var constructor = arguments[0]
  var args = [].(arguments, 1)
  // 1. Create a new object in memory  var obj = {}
  // 2. The [[Prototype]] pointer inside this new object is assigned as the prototype property of the constructor  obj.__proto__ = 
  // 3. This inside the constructor is assigned to this new object (that is, this points to the new object)  // 4. Execute the code inside the constructor (add attributes to the new object)  var res = (obj, args)
  // 5. If the constructor returns a non-empty object, it will return the object; otherwise, return the new object just created  if (res != null && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return obj
}
// testfunction Person(name, age) {
   = name
   = age
  // return undefined
  // return null
  // return {}
  // return 123
  // return ''
  // return String(123)
  // return new String(123)
  // return { name: 'wy' }
}
 = function() {
  ();
}
const p1 = new Person('zhj', 20)
(p1); // Person {name: 'zhj', age: 20}
() // zhj
const p2 = useNewOperator(Person, 'zhj', 20)
(p2); // Person {name: 'zhj', age: 20}
() // zhj

v2 Consider parameter types

The above basic implementation can run, but there are still problems, that is, it does not consider whether the first parameter is a function type. If the first parameter is passed in is not a function, then it is executed.(obj, args)This line of code callsconstructor()There will be an error when it is. So we need to add a judgment. If the first parameter passed in is not a function, then the exception will be thrown directly:

function useNewOperator() {
  var constructor = arguments[0]
+
+  if (typeof constructor !== 'function') {
+    throw new TypeError('the first argument to useNewOperator function must be a function')
+  }
+
  var args = [].(arguments, 1)
  // 1. Create a new object in memory  var obj = {}
  // 2. The [[Prototype]] pointer inside this new object is assigned as the prototype property of the constructor  obj.__proto__ = 
  // 3. This inside the constructor is assigned to this new object (that is, this points to the new object)  // 4. Execute the code inside the constructor (add attributes to the new object)  var res = (obj, args)
  // 5. If the constructor returns a non-empty object, it will return the object; otherwise, return the new object just created  if (res != null && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return obj
}
// testfunction Person(name, age) {
   = name
   = age
}
 = function() {
  ();
}
const obj = {}
const p2 = useNewOperator(obj, 'zhj', 20) // Uncaught TypeError: the first argument to useNewOperator function must be a function
(p2);
()

Alternatives to v3 .__proto__

We were putting the new object inside[[Prototype]]Assigning attribute values ​​to constructorsprototypeWhen attributes are givenobjOn__proto__The attribute assignment is implemented (equivalent to using.__proto__), although ok, it is not recommended to use.__proto__, more recommended/and/(Reference link:/zh-CN/docs/…). So we make the following modifications:

function useNewOperator() {
  var constructor = arguments[0]
  if (typeof constructor !== 'function') {
    throw new TypeError('the first argument to useNewOperator function must be a function')
  }
  var args = [].(arguments, 1)
  // 1. Create a new object in memory  var obj = {}
  // 2. The [[Prototype]] pointer inside this new object is assigned as the prototype property of the constructor+ (obj, )
  // 3. This inside the constructor is assigned to this new object (that is, this points to the new object)  // 4. Execute the code inside the constructor (add attributes to the new object)  var res = (obj, args)
  // 5. If the constructor returns a non-empty object, it will return the object; otherwise, return the new object just created  if (res != null && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return obj
}
// testfunction Person(name, age) {
   = name
   = age
}
 = function() {
  ();
}
const p1 = new Person('zhj', 20)
(p1); // Person {name: 'zhj', age: 20}
() // zhj
const p2 = useNewOperator(Person, 'zhj', 20)
(p2); // Person {name: 'zhj', age: 20}
() // zhj

Or we can use it()Directly specify the prototype to create a new object:

function useNewOperator() {
  var constructor = arguments[0]
  if (typeof constructor !== 'function') {
    throw new TypeError('the first argument to useNewOperator function must be a function')
  }
  var args = [].(arguments, 1)
  // 1. Create a new object in memory- var obj = {}
-
  // 2. The [[Prototype]] pointer inside this new object is assigned as the prototype property of the constructor+ var obj = ()
  // 3. This inside the constructor is assigned to this new object (that is, this points to the new object)  // 4. Execute the code inside the constructor (add attributes to the new object)  var res = (obj, args)
  // 5. If the constructor returns a non-empty object, it will return the object; otherwise, return the new object just created  if (res != null && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return obj
}
// testfunction Person(name, age) {
   = name
   = age
}
 = function() {
  ();
}
const p1 = new Person('zhj', 20)
(p1); // Person {name: 'zhj', age: 20}
() // zhj
const p2 = useNewOperator(Person, 'zhj', 20)
(p2); // Person {name: 'zhj', age: 20}
() // zhj

v4 implements using ES6 syntax

Next, let's use it againES6Syntax (remaining parameters (rest parameters)、const) Implementation:

function useNewOperator(constructor, ...args) {
  if (typeof constructor !== 'function') {
    throw new TypeError('the first argument to useNewOperator function must be a function')
  }
  // 1. Create a new object in memory  const obj = {}
  // 2. The [[Prototype]] pointer inside this new object is assigned as the prototype property of the constructor  (obj, )
  // Or use () to directly specify the prototype to create a new object  // const obj = ()
  // 3. This inside the constructor is assigned to this new object (that is, this points to the new object)  // 4. Execute the code inside the constructor (add attributes to the new object)  const res = (obj, args)
  // 5. If the constructor returns a non-empty object, it will return the object; otherwise, return the new object just created  if (res != null && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return obj
}
// testfunction Person(name, age) {
   = name
   = age
}
 = function() {
  ();
}
const p1 = new Person('zhj', 20)
(p1); // Person {name: 'zhj', age: 20}
() // zhj
const p2 = useNewOperator(Person, 'zhj', 20)
(p2); // Person {name: 'zhj', age: 20}
() // zhj

v5 considers ES6 detection

Finally, there is another point to consider, that isES6Newly addedAttributes, in usenewIn the constructor or function in which the operator is called,Returns a reference to the constructor method or function (reference link:/en-US/docs/…). So we can useTo detect whether a function or constructor passesnewThe operator is called. Then we still need to implement it ourselvesuseNewOperatorAdd the corresponding code to the function:

Uncommented version

function useNewOperator(constructor, ...args) {
  if (typeof constructor !== 'function') {
    throw new TypeError('the first argument to useNewOperator function must be a function')
  }
   = constructor
  const obj = {}
  (obj, )
  // const obj = ()
  const res = (obj, args)
  if (res != null && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return obj
}

The above is aboutnewThe principle behind the operation and the simulation implementation of handwritten functionsnewAll the contents of the operation process. For more information about JS new operating handwritten functions, please follow my other related articles!