首頁  >  文章  >  web前端  >  什麼是構造函數?詳解JavaScript中的建構函數

什麼是構造函數?詳解JavaScript中的建構函數

青灯夜游
青灯夜游轉載
2022-08-04 15:22:392845瀏覽

作為原型和原型鏈的基礎,先了解清楚建構函數以及它的執行過程才能更好地幫助我們學習原型和原型鏈的知識。這篇文章帶大家詳細了解JavaScript中的建構函數,介紹一下怎麼利用建構函數來建立一個js對象,希望對大家有幫助!

什麼是構造函數?詳解JavaScript中的建構函數

1、什麼是建構子?

一個普通的函數被用來建立一個類別物件時,它就被稱為建構函數,或建構器。 (為方便理解,你可以將JavaScript中建構器的創建理解為其他語言中的類別的創建,目的就是利用它透過new來實列一個物件)

function Person(){
//...
}
//当做普通函数调用
var obj=Person();

//构造函数调用
	 var obj=new Person();

建構函數的特點:

  • 在書寫規格上,我們習慣將建構函數名稱的首字母大寫。

  • 透過new來建立一個物件。

  • 不需要在內寫入return也會有回傳值,而且回傳的是一個物件。

利用建構子建立一個js物件

  • #建構子建立物件(方法寫在建構子裡,缺點:建構函式每執行一次,就會建立一次方法。)

	  function Person(name,age,gender){
            this.name=name;
            this.age=age;
            this.gender=gender;
            // 方法写在里面
            this.sayName=function(){
                 console.log(this.name);
             }      
          }
    
          function Dog(name,age){
              this.name=name;
              this.age=age;
          }
         
          var obj=new Person("张三",18,"男");
          var obj1=new Person("李四",16,"男");
          
          var dog=new Dog("乐乐",2);
          obj.sayName();
          obj1.sayName();
          
          console.log(obj);
          console.log(obj1);
          console.log(dog);
  • # 建構子建立物件(方法寫在建構子外,缺點: 方法為全域方法,污染全域。)

	   function Person(name,age,gender){
            this.name=name;
            this.age=age;
            this.gender=gender;
            this.sayName=fun;   //方法写在外面
          }
    	 function fun(){
            console.log(this.name);
        }
          function Dog(name,age){
              this.name=name;
              this.age=age;
          }
          
          var obj=new Person("张三",18,"男");
          var obj1=new Person("李四",16,"男");
          
          var dog=new Dog("乐乐",2);
          obj.sayName();
          obj1.sayName();
          
          console.log(obj);
          console.log(obj1);
          console.log(dog);
  • 建構子建立物件改造(方法透過原型物件建立)

#原型物件:prototype

我們所建立的每一個函數,解析器都會在函數中加入一個prototype屬性。

指向建構函數的原型對象,我們可以透過__proto__來存取該屬性。

建構子.prototype.xxx , xxx可以是變量,可以是方法。執行過程中會先去物件中找方法或變量, 找不到就會去原型裡尋找。

	  function Person(name,age,gender){
            this.name=name;
            this.age=age;
            this.gender=gender;
          }
          function Dog(name,age){
              this.name=name;
              this.age=age;
          }
          
        /*为person添加统一的方法, 到原型对象中*/
        Person.prototype.sayName=function(){
            console.log(this.name);
        }
          var obj=new Person("张三",18,"男");
          var obj1=new Person("李四",16,"男");
          
          var dog=new Dog("乐乐",2);
          obj.sayName();
          obj1.sayName();
          
          console.log(obj);
          console.log(obj1);
          console.log(dog);

運行結果:什麼是構造函數?詳解JavaScript中的建構函數

2、為什麼要使用建構子?

學習每一個概念,不僅要知道它是什麼,還要知道為什麼,以及解決什麼樣的問題。

舉個例子,我們要錄入一年級一班中每一位同學的個人信息,那麼我們可以創建一些對象,比如:

var p1 = { name: 'zs', age: 6, gender: '男', hobby: 'basketball' };
var p2 = { name: 'ls', age: 6, gender: '女', hobby: 'dancing' };
var p3 = { name: 'ww', age: 6, gender: '女', hobby: 'singing' };
var p4 = { name: 'zl', age: 6, gender: '男', hobby: 'football' };
// ...

像上面這樣,我們可以把每一位同學的信息當作一個物件來處理。但是,我們會發現,我們重複地寫了很多無意義的程式碼。例如 name、age、gender、hobby 。如果這個班上有60個學生,我們得重複寫60遍。

這時候,建構函式的優勢就體現出來了。我們發現,雖然每位同學都有 name、gender、hobby這些屬性, 但它們都是不同的,那我們就把這些屬性當作建構子的參數傳遞進去。而由於都是一年級的學生,age 基本上都是6歲,所以我們可以寫死,遇到特殊情況再單獨做處理即可。此時,我們就可以創建以下的函數:

function Person(name, gender, hobby) {
    this.name = name;
    this.gender = gender;
    this.hobby = hobby;
    this.age = 6;
}

當創建上面的函數以後, 我們就可以透過 new 關鍵字調用,也就是透過建構子來建立物件了。

var p1 = new Person('zs', '男', 'basketball');
var p2 = new Person('ls', '女', 'dancing');
var p3 = new Person('ww', '女', 'singing');
var p4 = new Person('zl', '男', 'football');
// ...

此時你會發現,建立物件會變得非常方便。所以,雖然封裝建構函數的過程會比較麻煩,但一旦封裝成功,我們再建立物件就會變得非常輕鬆,這也是我們為什麼要使用建構函式的原因。 在使用物件字面量建立一系列相同類型的物件時,這些物件可能具有一些相似的特徵(屬性)和行為(方法),此時會產生許多重複的程式碼,而使用建構函數就可以實作

程式碼復用

3、建構子的執行過程

先說一點基本概念。

function Animal(color) {
 this.color = color;
}

當一個函數創建好以後,我們並不知道它是不是建構函數,即使像上面的例子一樣,函數名為大寫,我們也不能確定。只有當一個函數以 new 關鍵字來呼叫的時候,我們才能說它是一個建構子。就像下面這樣:

var dog = new Animal("black");

以下我們只討論建構函數的執行過程,也就是以 new 關鍵字來呼叫的情況。

我們還是以上面的 Person 為例。

function Person(name, gender, hobby) {
 this.name = name;
 this.gender = gender;
 this.hobby = hobby;
 this.age = 6;
}

var p1 = new Person('zs', '男', 'basketball');

此時,建構子會有以下幾個執行過程:

1)當以 new 關鍵字呼叫時,會建立一個新的記憶體空間,標記為 Animal 的實例。

2)函數體內部的 this 指向該記憶體

透過以上兩步,我們就可以得出這樣的結論。

var p2 = new Person('ls', '女', 'dancing');  // 创建一个新的内存 #f2
var p3 = new Person('ww', '女', 'singing');  // 创建一个新的内存 #f3
###每當創建一個實例的時候,就會創建一個新的內存空間(#f2, #f3),創建#f2 的時候,函數體內部的this 指向#f2, 創建#f3 的時候,函數體內部的this 指向#f3。 ###

3) 执行函数体内的代码
通过上面的讲解,你就可以知道,给 this 添加属性,就相当于给实例添加属性。

4)默认返回 this

由于函数体内部的this指向新创建的内存空间,默认返回 this ,就相当于默认返回了该内存空间,也就是上图中的 #f1。此时,#f1的内存空间被变量p1所接受。也就是说 p1 这个变量,保存的内存地址就是 #f1,同时被标记为 Person 的实例。

以上就是构造函数的整个执行过程。

4、构造函数的返回值

构造函数执行过程的最后一步是默认返回 this 。言外之意,构造函数的返回值还有其它情况。下面我们就来聊聊关于构造函数返回值的问题。

1)没有手动添加返回值,默认返回 this

function Person1() {
 this.name = 'zhangsan';
}

var p1 = new Person1();

按照上面讲的,我们复习一遍。首先,当用 new 关键字调用时,产生一个新的内存空间 #f11,并标记为 Person1 的实例;接着,函数体内部的 this 指向该内存空间 #f11;执行函数体内部的代码;由于函数体内部的this 指向该内存空间,而该内存空间又被变量 p1 所接收,所以 p1 中就会有一个 name 属性,属性值为 ‘zhangsan’。

p1: {
 name: 'zhangsan'
}

2)手动添加一个基本数据类型的返回值,最终还是返回 this

function Person2() {
 this.age = 28;
 return 50;
}

var p2 = new Person2();
console.log(p2.age);   // 28
p2: {
 age: 28
}

如果上面是一个普通函数的调用,那么返回值就是 50。

3)手动添加一个复杂数据类型(对象)的返回值,最终返回该对象

直接上例子

function Person3() {
 this.height = '180';
 return ['a', 'b', 'c'];
}

var p3 = new Person3();
console.log(p3.height);  // undefined
console.log(p3.length);  // 3
console.log(p3[0]);      // 'a'

再来一个例子

function Person4() {
  this.gender = '男';
  return { gender: '中性' };
}

var p4 = new Person4();
console.log(p4.gender);  // '中性'

5、构造函数首字母必须大写吗?

大小写都可以

6、不用new关键字,直接运行构造函数,是否会出错?

如果不会出错,那么,用new和不用new调用构造函数,有什么区别?

1)使用new操作符调用函数

例子:

function Person(name){
  this.name = name;
  this.say = function(){
    return "I am " + this.name;
  }
}

var person1 = new Person('nicole');
person1.say(); // "I am nicole"

用new调用构造函数,函数内部会发生如下变化:

创建一个this变量,该变量指向一个空对象。并且该对象继承函数的原型;
属性和方法被加入到this引用的对象中;
隐式返回this对象(如果没有显性返回其他对象)
用伪程序来展示上述变化:

function Person(name){
  // 创建this变量,指向空对象
  var this = {}; 
  // 属性和方法被加入到this引用的对象中
  this.name = name;
  this.say = function(){
    return "I am " + this.name;
  }
  // 返回this对象
  return this;
}

可以看出,用new调用构造函数,最大特点为,this对象指向构造函数生成的对象,所以,person1.say()会返回字符串: “I am nicole”。

小贴士:如果指定了返回对象,那么,this对象可能被丢失。

function Person(name){
  this.name = name;
  this.say = function(){
    return "I am " + this.name;
  }
  var that = {};
  that.name = "It is that!";
  return that;
}

var person1 = new Person('nicole');
person1.name; // "It is that!"

2)直接调用函数

如果直接调用函数,那么,this对象指向window,并且,不会默认返回任何对象(除非显性声明返回值)。

还是拿Person函数为例,直接调用Person函数:

var person1 = Person('nicole');
person1; // undefined
window.name; // nicole

可见,直接调用构造函数的结果,并不是我们想要的。

3)小结

为了防止因为忘记使用new关键字而调用构造函数,可以加一些判断条件强行调用new关键字,代码如下:

function Person(name){
  if (!(this instanceof Person)) {
    return new Person(name);
  }
  this.name = name;
  this.say = function(){
    return "I am " + this.name;
  }
}

var person1 = Person('nicole');
console.log(person1.say()); // I am nicole
var person2 = new Person('lisa');
console.log(person2.say()); // I am lisa

【相关推荐:javascript学习教程

以上是什麼是構造函數?詳解JavaScript中的建構函數的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除