對象建立模式

每個對象都是基於引用類型建立的, 也能夠是開發人員本身建立的數組

建立對象

字面量模式

  • 缺點:
    • 會產生大量重複代碼, 使用變體 工廠模式
let Person = {
        name: 'agan',
        age: 30,
        job: 'it',
        sayName: function() {
            console.log(this.name)
        }
    }
複製代碼

工廠模式

這種模式抽象了建立具體函數的過程, 用函數來封裝以特定接口建立對象的細節函數

function createPerson(name, age, job) {
        let o = new Object()
        o.name = name
        o.age = age
        o.job = job
        o.sayName = function() {
            console.log(this.name)
        }
        return o
    }
    let p1 = createPerson('agan1', 30, 'it')
    let p2 = createPerson('agan2', 40, 'cs')
複製代碼

函數 createPerson 接受三個參數來構建一個對象, 該函數能夠屢次調用. 工廠模式雖然解決了建立多個類似對象的問題, 卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)ui

構造函數模式

構造函數能夠用來建立特定類型的對象, 咱們能夠建立自定義的構造函數, 從而能夠定義自定義對象類型的屬性和方法this

  • 任何函數均可以當成構造函數
  • 只要把一個函數經過new的方式來進行調用, 咱們就把這一次函數的調用方式稱之爲: 構造函數的調用
function Person(name, age, job) {
        this.name = name
        this.age = age
        this.job = job
        this.sayName = function() {
            console.log(this.name)
        }
    }
    let p1 = new Person('agan1', 30, 'it')
    let p2 = new Person('agan2', 40, 'cs')
複製代碼

PersoncreatePerson 不一樣:spa

  1. 沒有顯式建立對象
  2. 將屬性與方法給了 this
  3. 沒有 return 語句

p1p2 保存着 Person 的兩個不一樣實例, 但都有一個 constructor (構造函數) 屬性, 指向 Personprototype

console(p1.constructor == Person); //true
    console(p2.constructor == Person); //true
複製代碼

constructor 是用來表示對象類型的, instanceof 檢測對象類型更靠譜指針

alert(person1 instanceof Object);  //true
    alert(person1 instanceof Person);  //true
    alert(person2 instanceof Object);  //true
    alert(person2 instanceof Person);  //true
複製代碼
構造函數的執行過程

var p1=new Person();code

  • 一、建立一個對象 (咱們把這個對象稱之爲Person構造函數的實例)- _p1
  • 二、建立一個內部對象,this,將this指向該實例(_p1)
  • 三、執行函數內部的代碼,其中,操做this的部分就是操做了該實例(_p1)
  • 四、返回值:
    • a、若是函數沒有返回值(沒有return語句),那麼就會返回構造函數的實例(p1)對象

    • b、若是函數返回了一個基本數據類型的值,那麼本次構造函數的返回值是該實例(_p1)接口

      function fn(){}
          var f1 = new fn();    //f1就是fn的實例
          function fn2(){
              return "abc";
          }
          var f2 = new fn2();   //f2是fn2構造函數的實例
      複製代碼
    • c、若是函數返回了一個複雜數據類型的值, 那麼本次函數的返回值就是該值

      function fn3(){
              return [1,3,5]; 
              //數組是一個對象類型的值,
              //因此數組是一個複雜數據類型的值
              //-->本次構造函數的真正返回值就是該數組
              //-->再也不是fn3構造函數的實例
          }
          var f3 = new fn3();   //f3仍是fn3的實例嗎?錯
          //f3值爲[1,3,5]
      複製代碼
將構造函數當函數
  • 任何函數均可以是構造函數, 必須使用 new 調用
  • 任何函數只要不經過 new 調用就是普通函數
// 看成構造函數
    let person = new Person("agan", 29, "se");
    person.sayName() // 'agan'
    
    // 做爲普通函數調用
    Person("agan1", 27, "IT"); // 添加到 window
    window.sayName(); // "agan1"
    
    // 在另外一個對象的做用域中調用
    var o = new Object();
    Person.call(o, "agan3", 25, "Nurse")
    o.sayName(); // "agan3"
複製代碼
構造函數問題

就是每一個方法都要在每一個實例上從新建立一遍, sayName 指向不一樣的地址

person1.sayName === person2.sayName // true
複製代碼

能夠將 sayName 方法轉移到構造函數外部解決這個問題

function Person(name, age, job) {
        this.name = name
        this.age = age
        this.job = job
        this.sayName = sayName
    }
    function sayName() {
        console.log(this.name)
    }
    let person1 = new Person("agan1", 29, "se");
    let person2 = new Person("agan2", 27, "it");
    
    person1.sayName === person2.sayName  // true
複製代碼

上面代碼中將 sayName 函數轉移到了構造函數的外部, 在函數內部咱們將 sayName 指向這個全局函數, 這樣就解決了多個函數作一件事情的問題, 但又帶來了兩個新的問題:

  • 全局做用域的函數只能被某個對象調用
  • 若是對象須要多個方法那麼就須要定義多個全局函數

原型模式

咱們建立的每個函數都有一個 prototype 屬性, 這是一個指針, 指向了一個對象, 這個對象包含了特定類型對象的全部公共屬性和方法, 至關於一個父類

function Person() {
    }
    
    Person.prototype.name = 'agan'
    Person.prototype.age = '30'
    Person.prototype.job = 'it'
    Person.prototype.sayName = function() {
        console.log(this.name)
    }
    
    let p1 = new Person()
    let p2 = new Person()
    
    p1.sayName() // 'agan'
    p2.sayName() // 'agan'

    console.log(p1.sayName === p2.sayName) // true
複製代碼

上述代碼中 person1person2 訪問的是同一組對象和方法 假如咱們想在全部的實例中共享一個數據, 那麼咱們可使用原型模式, 但是通常實例都有本身的屬性

組合使用構造函數與原型模式

構造函數用於建立實例屬性, 原型模式用於定義方法和共享的屬性

function Person(name, age, job) {
        this.name = name
        this.age = age
        this.job = job
        this.friends = ['a', 'b']
    }
    
    Person.prototype = {
        constructor: Person,
        sayName: function() {
            console.log(this.name)
        }
    }
    
    var p1 = new Person('agan', 29, 'it')
    var p2 = new Person('agan2', 30, 'cs')
    p1.friends.push('c')
    console.log(p1.friends) // 'a' 'b' 'c'
    console.log(p2.friends) // 'a' 'b'
    console.log(p1.friends === p2.friends) // false
    console.log(p1.sayName === p2.sayName) // true
複製代碼

在上面的代碼中, 實例屬性是定義在構造函數中的, 實例共享的屬性和方法是在原型中定義的

動態原型模式

動態原型模式將全部信息都封裝在了構造函數中, 在構造函數中初始化原型

function Person(name, age, job) {
        this.name = name
        this.age = age
        this.job = job
        
        // 方法
        if (typeof this.sayName !== 'function') {
            Person.prototype.sayName = function() {
                console.log(this.name)
            }
        }
    }
    let f = new Person('agan', 30, 'it')
    f.sayName()
複製代碼

上述代碼中, sayName方法只有在不存在的狀況下, 纔會添加到原型中, 這段代碼只會在初次調用構造函數時纔會執行

寄生構造函數模式

基本思想: 建立一個函數, 該函數用來封裝建立對象的代碼, 而後再返回建立的對象 跟工廠模式相似

function Person(name, age, job) {
        let o = new Object()
        o.name = name
        o.age = age
        o.job = job
        o.sayName = function() {
            console.log(this.name)
        }
        return o
    }
    let f = new Person('agan', 20, 'it')
    f.sayName() // 'agan'
複製代碼
  • 說明:
    • 返回的對象與構造函數或者與構造函數的原型屬 性之間沒有關係
    • 構造函數返回的對象與在構造函數外部建立的對象沒有什麼不一樣
    • 不能依賴 instanceof 操做符來肯定對象類型

穩妥構造函數

穩妥對象: 指的是沒有公共屬性, 其方法也不引用 this 對象

  • 穩妥構造函數與寄生構造函數不一樣點:
    • 函數內部新建立對象的實例方法不引用 this
    • 函數外部不使用 new 操做符調用構造函數
    • 函數內部中變量不能掛在到要返回的變量上
function Person(name, age, job) {
        let o = new Object()
        // 定義私有變量和函數
        let name = name
        let age = age
        let job = job
        
        // 添加方法
        o.sayName = function() {
            console.log(name)
        }
        return o
    }
複製代碼

這種模式中建立的對象, 除了使用 sayName 方法外沒有其餘方法訪問name的值

let f = Person('agan', 30, 'it')
    f.sayName() // agan
複製代碼
相關文章
相關標籤/搜索