咱們常常會使用new
運算符建立一個數組(Array
)、對象(Object
)等,那麼new
究竟是什麼?前端
new 運算符建立一個用戶定義的對象類型的實例或具備構造函數的內置對象的實例。web
根據new
的定義,咱們很容易地得出new
的做用就是建立一個對象,其實並非這樣,new
是用來繼承的,咱們經過這樣的實例理解new
繼承數組
function person(name, age){
this.name = name;
this.age = age;
}
person.prototype.job = 'web前端開發工程師';
person.prototype.salary = function(){
return `${this.name}的薪資是20k`;
}
const Person = new person('小明','22');
console.log(Person); // person對象
console.log('name:', Person.name); // 小明
console.log('age:', Person.age); // 22
console.log('job:', Person.job); // web前端開發工程師
console.log('salary:', Person.salary()); // 小明的薪資是20k
複製代碼
咱們使用new
關鍵字建立了person
對象,並賦值給Person
,使Person
具備了person
中的屬性和方法,從而得出結論:new
關鍵字用來繼承。bash
上述的代碼中,person具備的特色app
person
)中的屬性person.prototype
)的方法如何實現訪問構造函數中的屬性 對於這個問題,咱們能夠利用call
和apply
實現繼承,假設首先有一個父類函數函數
function parent() {
// 此時this指向Child
this.name = '小明';
this.age = 22;
}
複製代碼
接下來,有一個子類方法要繼承上面的父類優化
function child() {
parent.call(this); //call的做用:對象的冒充,可實現繼承
}
複製代碼
咱們看看發生了什麼ui
const Child = new child();
console.log(Child); //child: {name: '小明', age: 22}
console.log(Child.name); // '小明'
console.log(Child.age); // 22
複製代碼
這樣,child
擁有了parent
的屬性,Child
也擁有了parent
的屬性,那麼咱們就實現了訪問構造函數中的屬性,也進一步說明了new
是用來繼承的this
person
構造函數的方法是在原型鏈中,那麼咱們就能夠直接使用賦值的方法實現訪問構造函數中的方法,像這樣:let object = {};
object.__proto__ = Person.__proto__;
console.log(object, 'object');
複製代碼
它的輸出:spa
咱們能夠看到在object的原型鏈上擁有了person
構造函數中的方法。
解決了這兩個難題,咱們就能夠本身寫一個方法來模擬實現new
運算符。
第一步
const Person = NEW(person, '小明', '22');
複製代碼
第二步
function NEW() {
let obj = {};
construct = [].shift.call(arguments); // 獲得構造函數 Array.protptype原型上的方法對傳入參數進行操做
obj.__proto__ = construct.prototype; //訪問方法
construct.apply(obj, arguments); // 讓obj擁有Construct中的屬性
return obj;
}
複製代碼
插播,這裏咱們沒有使用上面的call
方法,而是使用了apply
,這是由於下面的方法咱們須要讓obj
擁有構造函數中的屬性,這個屬性它是以類數組形式接收,因此咱們要使用apply
方法傳遞數組參數。咱們看一下結果
console.log(Person); // person對象
console.log('name:', Person.name); // 小明
console.log('age:', Person.age); // 22
console.log('job:', Person.job); // web前端開發工程師
console.log('salary:', Person.salary()); // 小明的薪資是20k
複製代碼
到這裏,看似是已經實現了new
的方法,可是,設想一下,若是有人把構造函數寫成了這樣,咱們看到name
和age
爲undefined
function person(name, age){
return {
name: '小明',
age: 22
}
}
console.log('name:', Person.name); // undefined
console.log('age:', Person.age); // undefined
複製代碼
那麼,針對這個問題,要如何改進代碼?
function NEW() {
let obj = {};
construct = [].shift.call(arguments); // 獲得構造函數 Array.protptype原型上的方法對傳入參數進行操做
obj.__proto__ = construct.prototype; //訪問方法
const result = construct.apply(obj, arguments); // 讓obj擁有Construct中的屬性
return typeof result === 'object' ? result : obj;
}
複製代碼
又若是有人把代碼寫成了這樣呢
function person(name, age){
return 1;
}
console.log('name:', Person.name); // undefined
console.log('age:', Person.age); // undefined
複製代碼
能夠看到,return
數字的時候是沒有問題的。若是返回一個null
呢
function person(name, age){
return null; // null是一個對象
}
複製代碼
這個時候就會報錯
應該如何解決這個問題,null
是一個對象,也就是說類型檢測它會返回object
,因此執行了三目運算中正確的條件,因此咱們用||
便可解決問題:
function NEW() {
let obj = {};
construct = [].shift.call(arguments); // 獲得構造函數 Array.protptype原型上的方法對傳入參數進行操做
obj.__proto__ = construct.prototype; //訪問方法
const result = construct.apply(obj, arguments); // 讓obj擁有Construct中的屬性
return typeof result === 'object' ? result || obj : obj;
}
複製代碼
這個時候,咱們要思考這個代碼還有優化的餘地麼?答案顯然是確定的
function NEW() {
construct = [].shift.call(arguments); // 獲得構造函數 Array.protptype原型上的方法對傳入參數進行操做
let obj = Object.create(construct.prototype);
// obj.__proto__ = construct.prototype;//訪問方法
const result = construct.apply(obj, arguments); // 讓obj擁有Construct中的屬性
return typeof result === 'object' ? result || obj : obj;
}
const Person = NEW(person, '小明', '22');
console.log(Person); // person對象
console.log('name:', Person.name); // 小明
console.log('age:', Person.age); // 22
console.log('job:', Person.job); // web前端開發工程師
console.log('salary:', Person.salary()); // 小明的薪資是20k
複製代碼
上面的方法中,運用了Object.create()方法建立對象,它建立的對象能夠直接使得新對象擁有原型上面的方法,因此可用其替換掉賦值訪問原型的辦法
上述文章若有錯誤之處,還請你們指正出來,咱們共同進步~
最後,分享一下個人我的公衆號「web前端日記」~