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