建立對象的方式

1.直接經過字面量的方式建立

let obj = {
    name: 'john',
    age: 24,
    number: 18
  }
咱們常常會使用這種方式建立對象
複製代碼

2.使用Object.create()來建立對象

該方法有兩個參數:Object.create(proto, [propertiesObject])。第一個參數proto來提供新建立的對象的原型對象,第二個參數(可選)默認爲undefined。若是沒有指定爲undefined,那麼這個參數對象包含了要添加到新建立的那個對象上的可枚舉屬性以及對該可枚舉屬性的屬性描述符。數組

若是propertiesObject參數是null或非原始包裝對象,則拋出一個TypeError異常。 bash

let o = {
    name: 'Tom',
    address: 'china'
  };
 let obj1 = Object.create(o);
 console.log(obj1);
 
 //如下是爲新建立的對象添加了可枚舉屬性number
 let obj2 = Object.create(o,{
   number: {
     value: 18204,
     writable: true,
     enumerable: true,
     configurable: true
   }
 });
 console.log(obj2);
複製代碼

3.工廠模式建立對象

工廠模式雖然解決了多個類似對象之間的流水線似的建立問題,可是並無解決對象識別的問題。對象中的constructor最初是用來標識對象類型的(或經過instanceof判斷)。下面例子中對象識別的問題是函數f不是obj1和obj2的構造函數,obj一、obj2的原型也不是f.prototype函數

function f(name,age) {
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.sayName = function () {
      console.log(this.name);
    };
    return obj;
  }
  let obj1 = f('lb',23);
  let obj2 = f('lb',22);
  console.log(obj1.constructor === f); //false
  console.log(obj2.constructor === f); //false
  //或者用instanceof檢測
  console.log(obj1 instanceof f);  //false
  console.log(obj2 instanceof f);  //false
複製代碼

4.使用構造函數來建立對象

//爲了解決對象沒法被識別的問題,如今使用構造函數來建立對象
  function Create(name,age) {
    this.name = name;
    this.age = age;
    this.sayName = function () {
      console.log(this.name);
    }
  }
  let c1 = new Create('lb',23);
  let c2 = new Create('fjq',20);
  console.log(c1 instanceof Create); //true
  console.log(c2 instanceof Create); //true
  console.log(c1.sayName === c2.sayName); //false
複製代碼

能夠看到上面c1和c2都是Create的實例對象,這也就解決了對象識別的問題。可是構造函數也不是徹底沒有缺陷的,使用構造函數建立對象,每個方法都會在實例對象上從新建立一遍。因爲方法(函數)也是對象,這樣就會形成大量的內存浪費。經過將構造函數轉移到構造函數外部能夠解決該問題。優化

5.構造函數建立對象的優化

function Create(name,age) {
    this.name = name;
    this.age = age;
    this.sayName = sayName
  }
  function sayName() {
    console.log(this.name);
  }
  let c1 = new Create('lb',23);
  let c2 = new Create('fjq',20);
  console.log(c1.sayName === c2.sayName); //true
複製代碼

以上將sayName函數轉移到構造函數外部能夠看到,兩個實例對象都共享了全局做用域中的sayName函數。可是新問題又出現了,在全局做用域中定義的函數只能被某個對象調用,這讓全局做用域下的函數有點名存實亡。其次,大量在全局做用域中定義方法,也不熟是一個好的作法。這些問題,能夠經過原型模式解決。ui

6.原型模式建立對象

每個函數都有prototype屬性,該屬性是一個指針,指向一個對象,咱們將這個對象稱爲原型對象。在原型對象上的屬性和方法全部實例對象均可以共享。所以,沒必要在構造函數中添加屬性和方法,直接寫在原型對象上便可。this

function Fn() {

  }
  Fn.prototype.name = 'jack';
  Fn.prototype.age = 22;
  Fn.prototype.sayName = function () {
    console.log(this.name);
  };
  //或者直接用字面量的方式寫
  // Fn.prototype = {
  //   name: 'jack',
  //   age: 22,
  //   sayName: function () {
  //     console.log(this.name);
  //   },
  //   constructor: Fn
  // }
  var fn = new Fn();
  fn.sayName(); //jack
複製代碼

以上經過原型模式建立的實例對象,均可以訪問其原型對象上的方法和屬性。而原型模式也會出現問題。首先,它省略了構造函數傳遞初始化參數這一環節,結果全部實例都共享了相同的屬性和方法,而這正不是咱們所但願的。因爲原型中的屬性和方法是共享的,對於包含引用類型的時候就會出問題,以下。spa

function Student() {

  }
  Student.prototype = {
    name: 'jack',
    age: 29,
    color: ['red','yellow'],
    constructor: Student,
    sayName () {
      console.log(this.name);
    }
  };
  let stu1 = new Student();
  let stu2 = new Student();
  stu1.color.push('orange');
  console.log(stu1.color); //["red", "yellow", "orange"]
  console.log(stu2.color); //["red", "yellow", "orange"]
 將stu1.color所引用的數組修改了,其結果也會反應在stu2.color數組上,而有時候這樣正不是咱們所但願的
複製代碼

7.組合使用構造函數模式和原型模式

構造函數模式用於定義實例的屬性,原型模式用於定義共享的屬性和方法。這種方式避免了構造函數建立對象的缺點,即全部的屬性和方法都會在實例對象上。又避免了原型模式建立對象的缺點,即把全部的屬性和方法都寫在原型對象上,省略了構造函數初始化參數這一環節。prototype

function Student(name,age) {
    this.name = name;
    this.age = age;
    this.color = ['yellow','red']
  }
  Student.prototype = {
    constructor: Student,
    sayName () {
      console.log(this.name);
    }
  };
  let s1 = new Student('jack',19);
  let s2 = new Student('john',29);
  s1.color.push('orange');
  console.log(s1.color); //["yellow", "red", "orange"]
  console.log(s2.color); //["yellow", "red"]
複製代碼
相關文章
相關標籤/搜索