在 JavaScript 中,new 運算子創建一個用戶定義的物件型別的實體或具有建構式的內置物件的實體,創建一個物件很簡單,為什么我們還要多此一舉使用 new 運算子呢?它到底有什么樣的魔力?
認識 new 運算子
通過下面的例子理解 new 運算子:
function Person (name) {
this.name = name
}
Person.prototype.getName = function () {
console.log(this.name)
}
var joe = new Person('joe')
joe.sayHello = function () {
console.log('Hello!')
}
joe.getName() // joe
joe.sayHello() // Hello!
Person.sayHello() // Uncaught TypeError: Person.sayHello is not a function
Person 是一個普通的函式,當它與 new 運算子一起使用時,Person 就是一個建構式,通過 new Person('joe') 得到的新物件 joe 繼承了 Person 的屬性,同時,this 也指向 joe 實體,為 joe 添加的屬性 sayHello 不會影響 Person,即 joe 是區別與 Person 的一個新物件,
因此,通過 new 創建的實體物件和建構式之間建立了一條原型鏈,并通過原型鏈賦予實體物件繼承屬性的能力,
new 的原理和實作
通過上面的分析,new 運算子內部做了如下四個操作:
- 創建一個空的簡單 JavaScript 物件(即{});
- 鏈接新物件(即設定該新物件的建構式)到函式物件;
- 將新創建的物件作為 this 的背景關系;
- 如果該函式沒有回傳物件,回傳新創建的物件,
new 的實作如下:
function newOperator (ctor, ...args) {
var obj = {};
obj.__proto__ = ctor.prototype
var res = ctor.apply(obj, args)
return res || obj;
}
優化一下代碼:
function newOperator (ctor, ...args) {
var o = Object.create(ctor.prototype) // 合并第一和第二步:創建一個空的簡單 JavaScript 物件(即{}),鏈接新物件(即設定該新物件的建構式)到函式物件
return fn.apply(o, args) || o
}
使用 newOperator 函式測驗上面 Person 的例子:
function Person(name) {
this.name = name
}
Person.prototype.getName = function () {
console.log(this.name)
}
var joe = newOperator(Person, 'joe')
joe.sayHello = function () {
console.log('Hello!')
}
joe.getName() // joe
joe.sayHello() // Hello!
Person.sayHello() // Uncaught TypeError: Person.sayHello is not a function
結果是一致的,
更好的檢查方式是:
function Person(name) {
this.name = name
}
console.log(new Person('joe')) // @1
console.log(newOperator(Person, 'joe')) // @2
@1 和 @2 在控制臺的顯示資訊是一模一樣的,
判斷是否使用 new 關鍵字
在 JavaScript 中,一個實體物件的創建必須使用 new 關鍵字,但是限于 JavaScript 的語法特征,實際上建構式同樣可以像普通函式那樣直接執行,那么建構式內部如何判斷是否使用了 new 關鍵字?
使用 instanceof 檢測
通過理解 new 運算子的原理,可知,在執行 new 操作時,建構式的 prototype 賦值給了實體的 proto 屬性,在 JavaScript 中 instanceof 可以用來檢測物件的原型鏈,如:a instanceof A 用來檢測 a 是否是 A 的實體(即 a 的原型鏈中存在原型物件 A),
function Person () {
if (this instanceof Person) {
console.log('new 呼叫')
} else {
console.log('普通函式呼叫')
}
}
const foo = new Person() // new 呼叫
const bar = Person() // 普通函式呼叫
使用 new.target 屬性
在 ES6 中引入了 new.target 屬性,new.target 屬性允許你檢測函式或構造方法是否是通過 new 運算子被呼叫的,在通過 new 運算子被初始化的函式或構造方法中,new.target 回傳一個指向構造方法或函式的參考,在普通的函式呼叫中,new.target 的值是 undefined,
function Person () {
console.log(new.target)
}
const foo = new Person() // ? Person () { console.log(new.target) }
const bar = Person() // undefined
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/111868.html
標籤:JavaScript
