java里面,new 運算符是用來實例化一個類,從而在內存中分配一個實例對象。 但在 javascript 中,原型語言沒類,只有對象與原型鏈繼承
JavaScript 中 new 表達式的作用是生成一個對象。
new 運算符創建一個用戶定義的對象類型的實例或具有構造函數的內置對象的實例。
new 關鍵字會進行如下的操作:
-
創建一個空對象(即{});
-
鏈接該對象(即設置該對象的構造函數)到另一個對象 ;
-
將步驟1新創建的對象作為this的上下文 ;
-
如果該函數沒有返回對象,則返回this。
根據這個步驟,手工實現new
function create(){ //創建一個空對象 let obj = new Object(); //獲取構造函數 // let args = [].slice.call(arguments); let Fun = args.shift(); let Constructor = [].shift.call(arguments); //鏈接到原型 obj.__proto__ = Constructor.prototype; //綁定this值,使用apply,將構造函數中的this指向新對象,這樣新對象就可以訪問構造函數中的屬性和方法 let result = Constructor.apply(obj,arguments); //返回新對象 return typeof result === "object" ? result : obj;//如果返回值是一個對象就返回該對象,否則返回構造函數的一個實例對象 }
在javascript中, 通過new可以產生原對象的一個實例對象,而這個實例對象繼承了原對象的屬性和方法。因此,new存在的意義在於它實現了javascript中的繼承
在《JavaScript模式》這本書中,new的過程說的比較直白,當我們new一個構造器,主要有三步:
• 創建一個空對象,將它的引用賦給 this,繼承函數的原型。
• 通過 this 將屬性和方法添加至這個對象
• 最后返回 this 指向的新對象,也就是實例(如果沒有手動返回其他的對象)
當我們new一個構造函數,得到的實例繼承了構造器的構造屬性(this.name這些)以及原型上的屬性。
// ES5構造函數 let Parent = function (name, age) { //1.創建一個新對象,賦予this,這一步是隱性的, // let this = {}; //2.給this指向的對象賦予構造屬性 this.name = name; this.age = age; //3.如果沒有手動返回對象,則默認返回this指向的這個對象,也是隱性的 // return this; //this的創建與返回是隱性的 }; const child = new Parent();
但上述這個解釋我覺得不夠完美,它只描述了構造器屬性是如何塞給實例,沒說原型上的屬性是如何給實例繼承的。
我在winter大神的重學前端專欄中,看到了比較符合我心意的,同時也是符合原理的描述:
• 以構造器的prototype屬性為原型,創建新對象;
• 將this(也就是上一句中的新對象)和調用參數傳給構造器,執行;
• 如果構造器沒有手動返回對象,則返回第一步創建的新對象,如果有,則舍棄掉第一步創建的新對象,返回手動return的對象。
new過程中會新建對象,此對象會繼承構造器的原型與原型上的屬性,最后它會被作為實例返回這樣一個過程。
以直觀的方式來理解的話,關鍵詞 new 之后所寫的是類名。不過正如此前說明,JavaScript 中沒有類的概念,所以,根據 JavaScript 的語法規則,new 之后所寫的是函數名。在 new 之后寫函數名的話,就會把該函數作為構造函數來進行調用。
在 JavaScript 的語言特性中沒有“類”的概念,為了便於理解,將用類 這個詞來稱呼那些可以被視作“類”的概念。來稱呼那些實際上將會調 用構造函數的 Function 對象。此外,在強調對象是通過調用構造函數而生成的時候,會將這些被生成的 對象稱作對象實例以示區別。
雖然在 JavaScript 中沒有類的概念,但將 new 之后所寫的標識符(函數名)看作是類名, 也並沒有什么概念上的問題。也就是說,完全可以認為,上文中代碼 new Object() 的作用是生成一個 Object 類的實例。
對類的功能的整理
接口 |
說明 |
---|---|
函數或是構造函數的調用 |
- |
類的屬性 |
相當於Java 中的static 方法或是static 域 |
prototype 對象的屬性 |
相當於Java 中的實例方法 |
實例屬性 |
相當於Java 中的實例域 |
類的屬性是一個類自身的屬性,例如,String 類的屬性是 String 類的對象自身的屬性。如果是函數的話,則可以像 String.fromCharCode(0x41) 這樣來使用。如果用更加直觀一些的說法來講,這就相當於 Java 或 C++ 中的 static 方法。
prototype 對象的屬性和實例屬性,都是以對象實例的形式來進行訪問的。以 String 類為例,可以以 str.trim() 或是 str.length 的方式,來使用引用了 String 對象(對象實例)的變量 str。
prototype 對象的屬性與實例屬性之間的不同點在於是否進行了繼承。例如,String 對象的 trim 方法,其實是 String.prototype 對象的屬性。這種以實例來繼承屬性的方式被稱為原型繼承。
實現一個簡單的new方法
//自己定義的new方法 let newMethod = function (Parent, ...rest) { // 1.以構造器的prototype屬性為原型,創建新對象; let child = Object.create(Parent.prototype); // 2.將this和調用參數傳給構造器執行 let result = Parent.apply(child, rest); // 3.如果構造器沒有手動返回對象,則返回第一步的對象 return typeof result === 'object' ? result : child; };
參考文章:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new
JavaScript深入之new的模擬實現 https://github.com/mqyqingfeng/Blog/issues/13
js new一個對象的過程,實現一個簡單的new方法 https://www.cnblogs.com/echolun/p/10903290.html
js手動實現new方法 https://www.jianshu.com/p/9cee6a703e01
轉載本站文章《JavaScript new 關鍵詞解析及原生實現 new 》,
請注明出處:https://www.zhoulujun.cn/html/webfront/ECMAScript/js/2020_0630_8498.html