摘要:后續我將推出進階系列,一方面是一個監督自己學習的一個過程,另一方面也會給看到的童鞋一些啟發。第二步鏈接到原型中現在把構造函數和參數都打印出來了。
原文:https://zhehuaxuan.github.io/...寫在前面的話
作者:zhehuaxuan
前端的入門相對簡單,相對于其他方向天花板可能會相對較低。但是在市場上一個優秀的前端依舊是很搶手的。能夠站在金字塔上的人往往寥寥無幾。
目前前端也已經一年半了,在公司的知識棧相對落后,就業形勢不容樂觀,所以有必要自己琢磨,往中高級前端進階。后續我將推出《JavaScript進階系列》,一方面是一個監督自己學習的一個過程,另一方面也會給看到的童鞋一些啟發。
JavaScript新建對象的過程在ES5中定義一個函數來創建對象,如下:
function Person(name){ this.name = name; } Person.prototype.getName = function(){ return name; } var person = new Person("xuan"); console.log(person.name);//輸出:xuan console.log(person.getName());//輸出:xuan
我們看到當我們新建一個對象,我們就可以訪問構造器中的指向this的屬性,還可以訪問原型中的屬性。我們不妨把JavaScript調用new的過程主要由下面四步組成:
新生成一個空對象
將空對象鏈接到原型中
綁定this
返回新對象
下面跟著我按照這個思路來創建對象:
function create(){ //Todo } person = create(Person,"xuan");//create(ObjectName,...arguments)
我們使用如上所示的函數來模擬new關鍵字。
首先第一步新建一個對象:
function create(){ var obj = new Object(); return obj; } person = create(Person,"xuan");
現在已經創建并返回一個對象,當然現在打印出來肯定是一個普通的對象,畢竟流程還沒有走完,我們接著往下看。
第二步鏈接到原型中:
function create(){ var obj = new Object(); var constructor = [].shift.call(arguments); console.log(constructor); console.log(arguments); obj.__proto__ = constructor.prototype; return obj; } person = create(Person,"xuan");
現在把構造函數和參數都打印出來了。沒問題!
第三步綁定this,如下:
function create() { let obj = new Object(); let constructor = [].shift.call(arguments) obj.__proto__ = constructor.prototype constructor.apply(obj, arguments); console.log(obj); return obj; } person = create(Person,"xuan");
打印結果實現new對象的效果。
現在改一下構造函數代碼:
function Person(name){ this.name = name; return { name:"abc" } } var person = new Person("xuan"); console.log(person); console.log(Object.prototype.toString.call(person));
效果如下:
我們執行一下我們構建的函數效果如下:
發現不一致,所以我們要處理第三步綁定this中apply函數的返回值:
function create() { let obj = new Object(); let constructor = [].shift.call(arguments) obj.__proto__ = constructor.prototype //constructor.apply(obj, arguments); let res = constructor.apply(obj, arguments); if(res){ return res; }else{ return obj; } } person = create(Person,"xuan");
效果如下:
完美!
現在我們思考一下這里的res返回值有三種情況:undefined,基本類型,對象。
如果res是undefined時,返回obj;如果res是基本類型我們也返回obj;
如果res是對象我們返回res對象;
綜合一下:
如果返回的res對象是Object類型那么返回res,否則返回obj。當然其他的判斷條件也是可以的。最后代碼優化如下:
function create() { let obj = new Object(); let constructor = [].shift.call(arguments) obj.__proto__ = constructor.prototype //constructor.apply(obj, arguments); let res = constructor.apply(obj, arguments); return res instanceof Object?res:obj; } person = create(Person,"xuan");幾個問題
現在的代碼已經完美了么?我們先來提幾個問題。
new Object()創建的對象純凈么?new Object()創建的對象純凈么?
為啥使用[].shift.call()來進行參數分割?arguments是一個數組么?
首先什么是純凈?我們定義一個對象的__proto__屬性為空的對象是一個純凈的對象。
在第二步的時候中已經改變的obj的原型鏈,所以無論它前面的原型鏈是咋樣的都無所謂,但是為了保證對象的純凈性,我們有必要引出Object.create(),該方法創建一個新對象,使用現有的對象來提供新創建的對象的__proto__。我們來看一下:
var person1 = Object.create({});
打印如下:
我們看到person1的__proto__指向了{}對象,所以我們在上述代碼中直接修改如下:
function create() { let constructor = [].shift.call(arguments); let obj = Object.create(constructor.prototype); let res = constructor.apply(obj, arguments); return res instanceof Object?res:obj; } person = create(Person,"xuan");為啥使用[].shift.call()來進行參數分割?arguments是一個數組么?
首先我們知道arguments是函數傳入的參數,那么這個參數是數組么?我們打印一下便知:
console.log(arguments); console.log(Object.prototype.toString.call(arguments)); console.log(arguments instanceof Array);
結果如下
不是數組。我們展開發現他跟數組很像,查一下資料發現這個對象是類數組。里面沒有shift函數,直接調用shift會報錯。我們使用使用Array.from(arguments)將arguments轉成數組,然后在調用shift函數也是一種思路。但是在這里我們使用apply最適合。所以下述代碼是模擬new Object()的最優代碼:
function create() { let constructor = [].shift.call(arguments); let obj = Object.create(constructor.prototype); let res = constructor.apply(obj, arguments); return res instanceof Object?res:obj; } person = create(Person,"xuan");
還有更優的實現方法,請大佬們不吝拍磚!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/102538.html
摘要:使用指定的參數調用構造函數,并將綁定到新創建的對象。由構造函數返回的對象就是表達式的結果。情況返回以外的基本類型實例中只能訪問到構造函數中的屬性,和情況完全相反,結果相當于沒有返回值。 定義 new 運算符創建一個用戶定義的對象類型的實例或具有構造函數的內置對象的實例。 ——(來自于MDN) 舉個栗子 function Car(color) { this.color = co...
摘要:之前文章詳細介紹了的使用,不了解的查看進階期。不同的引擎有不同的限制,核心限制在,有些引擎會拋出異常,有些不拋出異常但丟失多余參數。存儲的對象能動態增多和減少,并且可以存儲任何值。這邊采用方法來實現,拼成一個函數。 之前文章詳細介紹了 this 的使用,不了解的查看【進階3-1期】。 call() 和 apply() call() 方法調用一個函數, 其具有一個指定的 this 值和分...
摘要:返回的綁定函數也能使用操作符創建對象這種行為就像把原函數當成構造器,提供的值被忽略,同時調用時的參數被提供給模擬函數。 bind() bind() 方法會創建一個新函數,當這個新函數被調用時,它的 this 值是傳遞給 bind() 的第一個參數,傳入bind方法的第二個以及以后的參數加上綁定函數運行時本身的參數按照順序作為原函數的參數來調用原函數。bind返回的綁定函數也能使用 n...
摘要:今天這篇文章我們來看看一道必會面試題,即如何實現一個深拷貝。木易楊注意這里使用上面測試用例測試一下一個簡單的深拷貝就完成了,但是這個實現還存在很多問題。 引言 上篇文章詳細介紹了淺拷貝 Object.assign,并對其進行了模擬實現,在實現的過程中,介紹了很多基礎知識。今天這篇文章我們來看看一道必會面試題,即如何實現一個深拷貝。本文會詳細介紹對象、數組、循環引用、引用丟失、Symbo...
摘要:引言上一節介紹了高階函數的定義,并結合實例說明了使用高階函數和不使用高階函數的情況。我們期望函數輸出,但是實際上調用柯里化函數時,所以調用時就已經執行并輸出了,而不是理想中的返回閉包函數,所以后續調用將會報錯。引言 上一節介紹了高階函數的定義,并結合實例說明了使用高階函數和不使用高階函數的情況。后面幾部分將結合實際應用場景介紹高階函數的應用,本節先來聊聊函數柯里化,通過介紹其定義、比較常見的...
閱讀 3029·2021-11-23 09:51
閱讀 1018·2021-09-26 09:55
閱讀 3975·2021-09-22 14:58
閱讀 1507·2021-09-08 09:35
閱讀 1087·2021-08-26 14:16
閱讀 891·2019-08-23 18:17
閱讀 2078·2019-08-23 16:45
閱讀 711·2019-08-23 15:55