最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JS?new操作原理及手寫函數模擬實現示例

 更新時間:2022年07月06日 11:09:36   作者:種花家的進階  
這篇文章主要為大家介紹了JS?new操作原理及手寫函數模擬實現示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

我們知道,在 ES6 之前(ES5),JavaScript 中類的表現形式就是構造函數。

JavaScript 中的構造函數是怎么樣的?

  • 構造函數也是一個普通的函數,從表現形式上看,和普通的函數沒有任何區(qū)別(除了按照慣例,構造函數名稱的首字母通常會大寫);
  • 但如果一個普通函數被 new 操作符調用了,那么這個函數就叫做構造函數

原理

如果一個函數被 new 操作符調用了,那么它會執(zhí)行如下操作:

  • 在內存中創(chuàng)建一個新對象;
  • 將這個新對象內部的 [[Prototype]] 屬性賦值為構造函數的 prototype 屬性;
  • 將構造函數內部的 this 賦值為這個新對象(即 this 指向新對象);
  • 執(zhí)行構造函數內部的代碼(給新對象添加屬性);
  • 如果構造函數返回了非空對象,則返回該對象;否則,返回剛創(chuàng)建的新對象;

手寫函數模擬 new

下面,我們就根據上面的原理,嘗試自己手寫一個函數,模擬實現 new 操作符的功能。

v1 基本實現

我們先用 ES5 的語法來進行實現:

function useNewOperator() {
  var constructor = arguments[0]
  var args = [].slice.call(arguments, 1)
  // 1. 在內存中創(chuàng)建一個新對象
  var obj = {}
  // 2. 這個新對象內部的 [[Prototype]] 指針被賦值為構造函數(constructor)的 prototype 屬性
  obj.__proto__ = constructor.prototype
  // 3. 構造函數內部的 this 被賦值為這個新對象(即 this 指向新對象)
  // 4. 執(zhí)行構造函數內部的代碼(給新對象添加屬性)
  var res = constructor.apply(obj, args)
  // 5. 如果構造函數返回非空對象,則返回該對象;否則,返回剛才創(chuàng)建的新對象
  if (res != null && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return obj
}
// 測試
function Person(name, age) {
  this.name = name
  this.age = age
  // return undefined
  // return null
  // return {}
  // return 123
  // return ''
  // return String(123)
  // return new String(123)
  // return { name: 'wy' }
}
Person.prototype.sayName = function() {
  console.log(this.name);
}
const p1 = new Person('zhj', 20)
console.log(p1); // Person {name: 'zhj', age: 20}
p1.sayName() // zhj
const p2 = useNewOperator(Person, 'zhj', 20)
console.log(p2); // Person {name: 'zhj', age: 20}
p2.sayName() // zhj

v2 考慮參數類型

上面的基本實現能跑但還存在問題,即沒有考慮傳入第一個參數是否為函數類型,如果第一個參數傳入的不是函數,那么在執(zhí)行 constructor.apply(obj, args) 這行代碼調用 constructor() 時就會報錯了。所以我們需要加上判斷,如果第一個參數傳入的不是一個函數,就直接拋出異常:

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 = [].slice.call(arguments, 1)
  // 1. 在內存中創(chuàng)建一個新對象
  var obj = {}
  // 2. 這個新對象內部的 [[Prototype]] 指針被賦值為構造函數(constructor)的 prototype 屬性
  obj.__proto__ = constructor.prototype
  // 3. 構造函數內部的 this 被賦值為這個新對象(即 this 指向新對象)
  // 4. 執(zhí)行構造函數內部的代碼(給新對象添加屬性)
  var res = constructor.apply(obj, args)
  // 5. 如果構造函數返回非空對象,則返回該對象;否則,返回剛才創(chuàng)建的新對象
  if (res != null && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return obj
}
// 測試
function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.sayName = function() {
  console.log(this.name);
}
const obj = {}
const p2 = useNewOperator(obj, 'zhj', 20) // Uncaught TypeError: the first argument to useNewOperator function must be a function
console.log(p2);
p2.sayName()

v3 Object.prototype.__proto__ 的替代方案

前面我們在將新對象內部的 [[Prototype]] 屬性賦值為構造函數的 prototype 屬性時,是通過給 obj 上的 __proto__ 屬性賦值實現的(相當于使用了 Object.prototype.__proto__),雖然可以,但不推薦使用 Object.prototype.__proto__,更推薦使用 Object.getPrototypeOf/Reflect.getPrototypeOfObject.setPrototypeOf/Reflect.setPrototypeOf(參考鏈接:developer.mozilla.org/zh-CN/docs/… )。所以我們做如下修改:

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 = [].slice.call(arguments, 1)
  // 1. 在內存中創(chuàng)建一個新對象
  var obj = {}
  // 2. 這個新對象內部的 [[Prototype]] 指針被賦值為構造函數(constructor)的 prototype 屬性
+ Object.setPrototypeOf(obj, constructor.prototype)
  // 3. 構造函數內部的 this 被賦值為這個新對象(即 this 指向新對象)
  // 4. 執(zhí)行構造函數內部的代碼(給新對象添加屬性)
  var res = constructor.apply(obj, args)
  // 5. 如果構造函數返回非空對象,則返回該對象;否則,返回剛才創(chuàng)建的新對象
  if (res != null && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return obj
}
// 測試
function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.sayName = function() {
  console.log(this.name);
}
const p1 = new Person('zhj', 20)
console.log(p1); // Person {name: 'zhj', age: 20}
p1.sayName() // zhj
const p2 = useNewOperator(Person, 'zhj', 20)
console.log(p2); // Person {name: 'zhj', age: 20}
p2.sayName() // zhj

或者我們還可以使用 Object.create() 直接指定原型來創(chuàng)建新對象:

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 = [].slice.call(arguments, 1)
  // 1. 在內存中創(chuàng)建一個新對象
- var obj = {}
-
  // 2. 這個新對象內部的 [[Prototype]] 指針被賦值為構造函數(constructor)的 prototype 屬性
+ var obj = Object.create(constructor.prototype)
  // 3. 構造函數內部的 this 被賦值為這個新對象(即 this 指向新對象)
  // 4. 執(zhí)行構造函數內部的代碼(給新對象添加屬性)
  var res = constructor.apply(obj, args)
  // 5. 如果構造函數返回非空對象,則返回該對象;否則,返回剛才創(chuàng)建的新對象
  if (res != null && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return obj
}
// 測試
function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.sayName = function() {
  console.log(this.name);
}
const p1 = new Person('zhj', 20)
console.log(p1); // Person {name: 'zhj', age: 20}
p1.sayName() // zhj
const p2 = useNewOperator(Person, 'zhj', 20)
console.log(p2); // Person {name: 'zhj', age: 20}
p2.sayName() // zhj

v4 使用 ES6 語法實現

下面,我們再來使用 ES6 語法(剩余參數(rest parameters)、const)進行實現:

function useNewOperator(constructor, ...args) {
  if (typeof constructor !== 'function') {
    throw new TypeError('the first argument to useNewOperator function must be a function')
  }
  // 1. 在內存中創(chuàng)建一個新對象
  const obj = {}
  // 2. 這個新對象內部的 [[Prototype]] 指針被賦值為構造函數(constructor)的 prototype 屬性
  Object.setPrototypeOf(obj, constructor.prototype)
  // 或者使用 Object.create() 直接指定原型創(chuàng)建新對象
  // const obj = Object.create(constructor.prototype)
  // 3. 構造函數內部的 this 被賦值為這個新對象(即 this 指向新對象)
  // 4. 執(zhí)行構造函數內部的代碼(給新對象添加屬性)
  const res = constructor.apply(obj, args)
  // 5. 如果構造函數返回非空對象,則返回該對象;否則,返回剛才創(chuàng)建的新對象
  if (res != null && (typeof res === 'object' || typeof res === 'function')) {
    return res
  }
  return obj
}
// 測試
function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.sayName = function() {
  console.log(this.name);
}
const p1 = new Person('zhj', 20)
console.log(p1); // Person {name: 'zhj', age: 20}
p1.sayName() // zhj
const p2 = useNewOperator(Person, 'zhj', 20)
console.log(p2); // Person {name: 'zhj', age: 20}
p2.sayName() // zhj

v5 考慮 ES6 的 new.target 檢測

最后,還有一個點需要考慮,就是 ES6 新增的 new.target 屬性,在通過使用 new 操作符被調用的構造方法或函數中,new.target 會返回一個指向構造方法或函數的引用(參考鏈接:developer.mozilla.org/en-US/docs/… )。所以我們可以使用 new.target 來檢測函數或構造方法是否是通過 new 操作符被調用的。那么我們還需要在自己實現的 useNewOperator 函數中添加相應的代碼:

無注釋版本

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

以上,就是關于 new 操作背后的原理,以及手寫函數模擬實現 new 操作過程的所有內容啦,更多關于JS new操作手寫函數的資料請關注腳本之家其它相關文章!

相關文章

最新評論

铜梁县| 禄劝| 百色市| 霍林郭勒市| 长葛市| 荣昌县| 太康县| 尖扎县| 车致| 永清县| 奎屯市| 娄底市| 江口县| 承德市| 吴川市| 金阳县| 清流县| 日喀则市| 龙岩市| 略阳县| 浑源县| 太仓市| 景东| 崇阳县| 武冈市| 日照市| 天门市| 高台县| 镇雄县| 闽清县| 丹巴县| 嘉善县| 阳山县| 唐海县| 磐安县| 得荣县| 柳河县| 荆州市| 察雅县| 渑池县| 东安县|