1. 程式人生 > 程式設計 >JavaScript知識:建構函式也是函式

JavaScript知識:建構函式也是函式

目錄
  • 1、建構函式的定義與呼叫
  • 2、new關鍵字的用途
  • 3、建構函式的問題:浪費記憶體
  • 總結

首先要明確的是建構函式也是函式

我經常使用建構函式來建立例項物件,例如物件和陣列的例項化可以通過相應的建構函式Object()和Array()完成。

建構函式與普通函式在語法的定義上沒有任何區別,主要的區別體現在以下3點。

(1)建構函式的函式名的第一個字母通常會大寫。按照慣例,建構函式名稱的首字母都是要大寫的,非建構函式則以小寫字母開頭。這是從面嚮物件語言那裡借鑑的,有助於在ECMAScript中區分建構函式和普通函式。

(2)在函式體內部使用this關鍵字,表示要生成的物件例項,建構函式並不會顯式地返回任何值,而是預設返回“this”。在下面的程式碼段中,Person()函式並沒有使用return關鍵字返回任何資訊,但是輸出的變數person1是一個具有name、ag

e屬性值的Person例項。

<!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>

顯示效果如下

JavaScript知識:建構函式也是函式

(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.com
his.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>

解釋:將多個函式封裝成一個物件即可解決函式名可能衝突的問題。

通過這種設定全域性訪問的函式,這樣就可以被所有例項訪問到,而不用重複建立。

但是這樣也會存在一個問題,如果為一個物件新增的所有函式都處理成全域性函式,這樣並無法完成對一個自定義型別物件的屬性封裝(言外之意。指的是全域性函式只可以封裝函式,但無法封裝屬性,因為屬性封裝在函式外面,要定義為全域性變數,才可以去呼叫,這反而會汙染全域性變數),因此這不是一個好的解決辦法。

此時,便引用原型的概念,使用原型概念可以很好解決這個問題,可以很好解決這個問題。

總結

本篇文章就到這裡了,希望能給你帶來幫助,也希望您能夠多多關注我們的更多內容!