1. 程式人生 > >js中的物件建立的模式

js中的物件建立的模式

開發十年,就只剩下這套架構體系了! >>>   

物件建立模式:

  • 工廠模式
    • 由於javascript中沒有類的概念,所以用開發人員發明了一種函式,通過函式來封裝特定介面建立物件的,見例子:
    	function createObj (name, age) {
    		return {
    		name: name,
    		age: age
    		}
    	}
    
  • 建構函式模式
    • 該模式和上述工廠模式的不同點:
      • 沒有顯示地建立物件
      • 直接將屬性和方法賦給了this物件
      • 沒有return語句
      function Person (name, age){
          this.name = name;
          this.age = age;
      }
      
    • 建構函式名首字母約定大寫
    • 建立物件的過程大概分為以下三個步驟:
      1. 建立一個物件
      2. 將建構函式的作用域賦給新物件
      3. 執行建構函式內的程式碼,為這個物件新增屬性
      4. 返回物件
      var obj = Object.create(Person.prototype);
      // 建立一個物件,將這個物件的原型指向建構函式的的原型;
      Person.call(obj);
      // 執行步驟2和3, 將建構函式的this指向建立的obj物件,並執行建構函式內的程式碼,為obj物件新增屬性
      
    • 需要注意的是: 如果建構函式中有return語句,返回的是引用型別的資料,則返回該引用型別資料
    • 如果做普通函式,在全域性作用域中執行,則this指向window
    • 建構函式存在的問題:每個方法都要在每個例項上建立一遍,對此有一個簡單的方法解決,將方法函式的定義放在外面,但是如果有很多個方法,那就需要在外面定義很多個方法,好在可以通過原型模式來解決這一問題
  • 原型模式
    • 將共同的方法屬性新增到函式的原型中,好處也體現在此處,讓所有的物件例項共享它包含的屬性和方法
      function Person () {};
      Person.prototype.name = 'zhanhui';
      Person.prototype.job = 'programmer';
      Person.prototype.sayName = function () {
          console.log(this.name);
      };
      
    • 每一個函式都有一個prototype原型屬性,指向一個物件
    • prototype原型物件上有一個constructor屬性,指向建構函式
    • Object.getPrototypeOf() 獲取物件的原型屬性
    • isPrototypeOf()方法確定物件之間是否存在原型關係
    • 雖然可以通過物件例項訪問儲存在原型中的值,但是不能通過物件例項重寫原型中的值,並且為物件例項新增一個原型上重名的屬性時,這個屬性會遮蔽原型物件中儲存的同名屬性,如果要回復對原型物件該屬性的訪問,可以用delete操作符刪除例項屬性,見下例子:
    var person1 = new Person();
    var person2 = new Person();
    person1.name = 'wawa';
    console.log(person1.name); // 'wawa'來自例項物件
    console.log(person2.name); // 'zhanhui'來自原型物件
    
    • hasOwnProperty()獲取例項物件上的屬性,不能獲取原型物件的屬性
    • in 操作符 單獨使用或者配合for-in迴圈中使用
      • 單獨使用時,in操作符會在通過物件是否能訪問到給定的屬性時返回true,無論該屬性是否存在於例項中還是原型中
      • for-in迴圈時,可以通過物件訪問到、可列舉的屬性,該屬性既可以在例項中,也可以是原型中,並且即使將該屬性的Enumerable標記為false,也是可以遍歷到的
      person1.age = 26;
      Object.defineProperty(Object.getPrototypeOf(person1), 'age', {
          enumerable: false
      });
      for (var key in person1) {
          console.log(key); // name job sayName
      }
      
      • for-in獲取key
      var obj = {
          x: 1,
          y: 2
          };
          var props = [];
          var i = 0;
      
          for (props[i++] in obj);
      
          props // ['x', 'y']
      
    • hasPrototypeProperty() 判斷讀取到的某屬性是在例項中讀取到還時原型中讀取到
    hasPrototypeProperty(person1, 'name'); // false person1中的name是從例項中讀取的
    hasPrototypeProperty(person2, 'name'); // true person2中的name是從原型中讀取的
    
    • Object.keys()返回所有可列舉屬性的字串陣列
    • Object.getOwnPropertyNames()可以獲取例項的所有屬性,不管是否可列舉
    • 原型物件:
      • 預設原型物件的constructor是不可列舉的,當我們重寫原型時,constructor指向object建構函式,所以在重寫建構函式的原型物件時,如果constructor很重要,會用的到,就需要特別指定一下,見例子:
      function Person (){}
      Person.prototype = {
          constructor: Person, // 特別指定
          name: 'zhanhui',
          say: function() {
              console.log(this.name);
          }
      }
      // 將constructor屬性設定為不可列舉
      Object.defineProperty(Person.prototype, 'constructor', {
          enumberable: false
      })
      
      • 重寫原型後,呼叫建構函式時會為例項新增一個指向最初的原型,而重寫原型則將建構函式和其最初的原型斷開了聯絡,例項化時又將例項物件的__proto__指向最初的原型,所以就存在問題,例項不能獲取重寫後的原型上的屬性和方法,見例子:
      var boo = new Person();
      boo.say() // error
      // 但是經過驗證並不是,新的瀏覽器核心已經不存在這樣的問題了
      
      • 原型物件存在的問題:主要是其共享性所帶來的,例如原型物件上掛載了一個引用型別的屬性,其他一個例項中更改這個引用屬性都會將影響擴散到其他的所有例項上,見例子:
      function Person (){}
          Person.prototype = {
              constructor: Person, // 特別指定
              name: 'zhanhui',
              say: function() {
                  console.log(this.name);
              },
              friends: ['boo', 'mike']
          }
          var person1 = new Person();
          var person2 = new Person();
          person1.friends.push('lili');
          console.log(person1.friends); // ['boo', 'mike', 'lili']    console.log(person2.friends); // ['boo', 'mike', 'lili'] 
      
  • 組合建構函式模式和原型模式
    • 建構函式模式用來定義例項屬性,而原型模式用於定義方法和共享的屬性
    • 見例子:
    function Person (name, age) {
        this.name = name;
        this.age = age;
        this.friends = ['boo', 'mike'];
    }
    Person.prototype = {
        constructor: Person,
        say: function () {
            console.log(this.name);
        }
    }
    
  • 寄生建構函式模式
    • 其實和工廠模式類似,建構函式用來封裝建立物件的程式碼,然後在返回新建立的物件,見例子:
    function Person (name, age) {
        var obj = {};
        obj.name = name;
        obj.age = age;
        obj.say = function () {
            console.log(this.name);
        }
    }
    var person1 = new Person('zhanhui', 26);
    
    • 應用場景: 例如我們要建立一個額外方法的陣列
    • 特殊說明:建構函式返回的物件和建構函式以及建構函式的原型沒有關係,因此不能用instanceof來確定物件型別
  • 穩妥建構函式
    • 所謂穩妥就是沒有公共屬性,其方法不使用new呼叫函式,也不引用this
    • 見例子:
    function Person (name,age){
        var o = {};
        // 定義私有變數和方法;
        function howOld () {
            console.log(age);
        }
        // 新增共有方法,這也是閉包的應用場景,共有方法訪問私有變數
        o.say = function () {
            console.log(this.name);
        };
        return o;
    }
     var person1 = Person('zh', 26);
    // 只能通過say方法訪問私有變數name