JavaScript知識:建構函式也是函式
目錄
- 1、建構函式的定義與呼叫
- 2、new關鍵字的用途
- 3、建構函式的問題:浪費記憶體
- 總結
首先要明確的是建構函式也是函式
我經常使用建構函式來建立例項物件,例如物件和陣列的例項化可以通過相應的建構函式Object()和Array()完成。
建構函式與普通函式在語法的定義上沒有任何區別,主要的區別體現在以下3點。
(1)建構函式的函式名的第一個字母通常會大寫。按照慣例,建構函式名稱的首字母都是要大寫的,非建構函式則以小寫字母開頭。這是從面嚮物件語言那裡借鑑的,有助於在ECMAScript中區分建構函式和普通函式。
(2)在函式體內部使用this關鍵字,表示要生成的物件例項,建構函式並不會顯式地返回任何值,而是預設返回“this”。在下面的程式碼段中,Person()函式並沒有使用return關鍵字返回任何資訊,但是輸出的變數person1是一個具有name、ag
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>Document</title> </head> <body> <script> //1.定義建構函式 function Person(name,age){ this.age = age; this.name = name; this.sayName = function(){ console.log(this.name); } } // 2.生成例項物件 var person1 = new Person('小蘇','18'); // 3.列印例項物件 console.log(person1); </script> </body> </html>
顯示效果如下
(3)作為建構函式呼叫時,必須與new操作符配合使用。這一點非常重要,任何函式只要使用new操作符呼叫就是建構函式,而不使用new操作符呼叫的函式就是普通函式。
1、建構函式的定義與呼叫
建構函式又稱為自定義函式,以函式的形式為自己的物件型別定義屬性和方法。
舉例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,age){ this.age = age; this.name = name; thttp://www.cppcns.comhis.sayName = function(){ console.log(this.name); } } // 2.生成例項物件 var person1 = new Person('小蘇','18'); // 3.呼叫方法 person1.sayName(); </script> </body> </html>
2、new關鍵字的用途
建構函式在執行時,會執行以下4步:
- 通過new關鍵字(操作符)建立例項物件,會在記憶體中建立一個新的地址。
- 為建構函式中的this確定指向,指向例項的本身。
- 執行建構函式程式碼,為例項新增屬性。
- 返回這個新建立的物件。
以前面生成person1例項的程式碼為例。
第一步:為person1RYxaUqzLf例項在記憶體中建立一個新的地址。
第二步:確定person1例項的this指向,指向person本身。
第三步:為person1例項新增name、age和sayName屬性,其中sayName屬性值是一個函式。
第四步:返回這個person1例項。
舉個例項
var person1 = new Person("Bob",18);
這行程式碼,等價於
// 演示 new 的功能 function Person(name,age) { // 1.建立一個新物件 var instance = new Object(); // 2.將函式內部的 this 指向了這個新物件 this = instance; // 3.執行建構函式內部的程式碼 this.name = name; this.age = age; this.sayName = function () { console.log(this.name); }; // 4.將新物件作為返回值 return instance; }
這就意味著,將instance 賦值給 person1,即person1 = instance。實際上又是這樣的:
// 演示 new 的功能 function Person(name,age) { // 1.建立一個新物件 var person1 = new Object(); // 2.將函式內部的 this 指向了這個新物件 this = person1; // 3.執行建構函式內部的程式碼 this.name = name; this.age = age; this.sayName = function () { console.log(this.name); }; // 4.將新物件作為返回值 return person1; }
3、建構函式的問題:浪費記憶體
基本事實:不同例項中的sayName屬性是不同的。舉個例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>Document</title> </head> <body> <script> // 1.自定義建構函式 function Person(name,age) { this.name = name; this.age = age; // this 內部的 type 屬性值是不變的 this.type = "human"; // 每個物件的 sayName 方法也是一樣的 this.sayName = function () { console.log(this.name); }; } var person1 = new Person('Bob',18); var person2 = new Person('Mike',20); // 2,判斷各自的方法是否是同一個函式 console.log(person1.sayName === person2.sayName); // false </script> </body> </html>
解釋:通過console.log(person1.sayName === person2.sayName)方法可判斷兩個函式是否是是同一個,顯然這不是同一個函式,那麼兩個函式就佔用了兩個記憶體空間,就會造成記憶體的浪費。
那如何解決函式佔用記憶體的辦法呢?答案是將物件內部函式提取出來作為公共函式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>Document</title> </head> <body> <script> // 解決方法1: 將公共的函式提取到建構函式之外 function sayName() { console.log(this.name); } function sayAge() { console.log(this.age); } //1.自定義物件 function Person(name,age) { this.name = name; this.age = age; // this 內部的 type 屬性值是不變的 this.type = "human"; // 每個物件的 sayName 方法也是一樣的 this.sayName = sayName; this.sayAge = sayAge; } //2.例項化物件 var person1 = new Person('Bob',20); console.log(person1.sayName === person2.sayName); // true </script> </body> </html>
此時,也存在一個問題,如果有多個公共函式,需要在外部建立多個函式,可能會造成命名衝突
解決辦法,將多個公共的函式封裝到一個物件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Document</title>
</head>
<body>
<http://www.cppcns.comscript>
// 3.將多個公共的函式封裝到一個物件
var fns = {
sayName : function () {
console.log(this.name);
},sayAge : function () {
console.log(this.age);
}
};
// 1.自定義物件
function Person(name,age) {
this.name = name;
this.age = age;
this.type = "human";
this.sayName = fns.sayName;
this.sayAge = fns.sayAge;
}
// 2.生成物件例項
var person1 = new Person("Bob",18);
var person2 = new Person("Mike",20);
// person1.sayName();
console.log(person1.sayName === person2.sayName);
console.log(person1.sayAge === person2.sayAge);
</script>
</body>
</html>
解釋:將多個函式封裝成一個物件即可解決函式名可能衝突的問題。
通過這種設定全域性訪問的函式,這樣就可以被所有例項訪問到,而不用重複建立。
但是這樣也會存在一個問題,如果為一個物件新增的所有函式都處理成全域性函式,這樣並無法完成對一個自定義型別物件的屬性封裝(言外之意。指的是全域性函式只可以封裝函式,但無法封裝屬性,因為屬性封裝在函式外面,要定義為全域性變數,才可以去呼叫,這反而會汙染全域性變數),因此這不是一個好的解決辦法。
此時,便引用原型的概念,使用原型概念可以很好解決這個問題,可以很好解決這個問題。
總結
本篇文章就到這裡了,希望能給你帶來幫助,也希望您能夠多多關注我們的更多內容!