JavaScript系列:揭開new運算符的神祕面紗

咱們常常會使用new運算符建立一個數組(Array)、對象(Object)等,那麼new究竟是什麼?前端

new的定義

new 運算符建立一個用戶定義的對象類型的實例或具備構造函數的內置對象的實例。web

new的做用

根據new的定義,咱們很容易地得出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)的方法
  • 如何實現訪問構造函數中的屬性 對於這個問題,咱們能夠利用callapply實現繼承,假設首先有一個父類函數函數

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運算符。

  • 模擬實現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的方法,可是,設想一下,若是有人把構造函數寫成了這樣,咱們看到nameageundefined

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前端日記」~

相關文章
相關標籤/搜索