一、是什么
在JavaScript
中,new
操作符用於創建一個給定構造函數的實例對象
例子
1 function Person(name, age){ 2 this.name = name; 3 this.age = age; 4 } 5 Person.prototype.sayName = function () { 6 console.log(this.name) 7 } 8 const person1 = new Person('Tom', 20) 9 console.log(person1) // Person {name: "Tom", age: 20} 10 t.sayName() // 'Tom'
從上面可以看到:
new
通過構造函數Person
創建出來的實例可以訪問到構造函數中的屬性new
通過構造函數Person
創建出來的實例可以訪問到構造函數原型鏈中的屬性(即實例與構造函數通過原型鏈連接了起來)
現在在構建函數中顯式加上返回值,並且這個返回值是一個原始類型
1 function Test(name) { 2 this.name = name 3 return 1 4 } 5 const t = new Test('xxx') 6 console.log(t.name) // 'xxx'
可以發現,構造函數中返回一個原始值,然而這個返回值並沒有作用
下面在構造函數中返回一個對象
function Test(name) { this.name = name console.log(this) // Test { name: 'xxx' } return { age: 26 } } const t = new Test('xxx') console.log(t) // { age: 26 } console.log(t.name) // 'undefined'
從上面可以發現,構造函數如果返回值為一個對象,那么這個返回值會被正常使用
二、流程
從上面介紹中,我們可以看到new
關鍵字主要做了以下的工作:
-
創建一個新的對象
obj
-
將對象與構建函數通過原型鏈連接起來
-
將構建函數中的
this
綁定到新建的對象obj
上 -
根據構建函數返回類型作判斷,如果是原始值則被忽略,如果是返回對象,需要正常處理
舉個例子:
1 function Person(name, age){ 2 this.name = name; 3 this.age = age; 4 } 5 const person1 = new Person('Tom', 20) 6 console.log(person1) // Person {name: "Tom", age: 20} 7 t.sayName() // 'Tom'
流程圖如下:
三、手寫new操作符
現在我們已經清楚地掌握了new
的執行過程
那么我們就動手來實現一下new
1 function mynew(Func, ...args) { 2 // 1.創建一個新對象 3 const obj = {} 4 // 2.新對象原型指向構造函數原型對象 5 obj.__proto__ = Func.prototype 6 // 3.將構建函數的this指向新對象 7 let result = Func.apply(obj, args) 8 // 4.根據返回值判斷 9 return result instanceof Object ? result : obj 10 }
測試一下
function mynew(func, ...args) { const obj = {} obj.__proto__ = func.prototype let result = func.apply(obj, args) return result instanceof Object ? result : obj } function Person(name, age) { this.name = name; this.age = age; } Person.prototype.say = function () { console.log(this.name) } let p = mynew(Person, "huihui", 123) console.log(p) // Person {name: "huihui", age: 123} p.say() // huihui
可以發現,代碼雖然很短,但是能夠模擬實現new