《javascript高級程序設計》筆記:建立對象

1. 簡單方式建立對象

// 字面量方式建立對象
  var person1 = {
    name: "xyc",
    age: 23,
    sayHi: function() {
      console.log(name);
    }
  };
  
  // Object方式建立對象
  var person2 = new Object();
  person2.name = "lxy";
  person2.age = 18;
  person2.sayHi = function() {
    console.log(person2.name);
  }

雖然Object構造函數或對象字面量均可以用來建立單個對象,但這些方式有個明顯的缺點:使用同一個接口建立不少對象,會產生大量的重複代碼,如上面的代碼,每建立一個相似的person對象,就會重複上面的寫法,代碼較爲冗餘javascript

爲了解決這個問題(代碼重複),下面引入工廠模式 ==>java

2. 工廠模式建立對象

function createPerson(name, age, job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
      alert(this.name);
    };
    return o; 
  }
  var person1 = createPerson("Nicholas", 29, "Software Engineer");
  var person2 = createPerson("Greg", 27, "Doctor");

通俗的解釋:工廠模式就是利用了函數的封裝調用,類比工廠材料==>成品的過程,完成入口參數==>對象的過程,函數能夠無數次的生成,所以可以避免上面產生大量重複代碼的狀況。工廠模式雖然解決了建立多個類似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)app

爲解決這個問題(對象識別),下面引入構造函數模式函數

3. 構造函數建立對象

3.1 構造函數與普通函數的區別

  1. 命名規則:構造函數通常是首字母大寫,普通函數遵守小駝峯式命名法
  2. 函數調用
    構造函數:
    (1)new fn( )
    (2)構造函數內部會建立一個新的對象,即f的實例
    (3)函數內部的this指向 新建立的f的實例
    (4)默認的返回值是f的實例
    普通函數:
    (1)fn( )
    (2)在調用函數的內部不會建立新的對象
    (3)函數內部的this指向調用函數的對象(若是沒有對象調用,默認是window)
    (4)返回值由return語句決定
  3. 構造函數的返回值
    有一個默認的返回值,新建立的對象(實例),當手動添加返回值後(return語句):
    (1)返回值是基本數據類型-->真正的返回值仍是那個新建立的對象(實例)
    (2)返回值是複雜數據類型(對象)-->真正的返回值是這個對象this

    function foo() {
      var f2 = new foo2();
      console.log(f2);    // {a: 3}
      console.log(this);  // window
      return true;
    }
    function foo2() {
      console.log(this);  // foo2類型的對象 不是foo2函數
      return {a: 3};
    }
    var f1 = foo();
    console.log(f1);      // true

3.2 new 操做符做用

使用new操做符調用構造函數會經歷下面幾個步驟:
(1)建立一個以這個函數爲原型的空對象.
(2)將函數的 prototype 賦值給對象的 proto 屬性
(3)將對象做爲函數的 this 傳進去。若是有 return 出來東西是對象的話就直接返回 return 的內容,沒有的話就返回建立的這個對象prototype

function NewFunc(func){
  var ret = {};
  if (func.prototype !== null) {
    ret.__proto__ = func.prototype;
  }
  var ret1 = func.apply(ret, Array.prototype.slice.call(arguments, 1));
  if ((typeof ret1 === "object" || typeof ret1 === "function") && ret1 !== null) {
    return ret1;
  }
  return ret;
}

普通函數的做用主要是封裝做用,可以在做用域內多處調用而已code

3.3 構造函數解決對象識別

建立自定義的構造函數意味着能夠經過 instanceof 將它的實例標識爲一種特定的類型對象

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.sayHi = function(){
      console.log(this.name);
    }
  }
  
  var person1 = new Person('xyc', 23);
  var person2 = new Person('lxy', 22);
  
  console.log(person1 instanceof Person); //true
  console.log(person2 instanceof Person); //true
  console.log(person1 instanceof Object); //true 由於Person繼承自Object,因此這裏同樣成立.

3.4 缺陷

構造函數建立對象的方式解決了代碼重複對象識別的問題,可是建立的對象中含有方法時,每實例化一個Person,就會產生一個方法,也就是一個對象,每一個對象分別佔據內存。所以,構造函數建立對象的方式存在內存大量佔用的風險繼承

利用原型共享的特性,下面引入原型模式接口

4. 原型建立對象

function Person(){}
  Person.prototype = {
    constructor: Person,
    name : "Nicholas",
    age : 29,
    job : "Software Engineer",
    friends : ["Shelby", "Court"],
    sayName : function () {
      alert(this.name);
    } 
  };
  
  var person1 = new Person();
  var person2 = new Person();
  
  person1.sayName();    //"Nicholas"
  person2.sayName();    //"Nicholas"
  
  person1.friends.push("Van");
  alert(person1.friends);    //"Shelby,Court,Van"
  alert(person2.friends);    //"Shelby,Court,Van"
  alert(person1.friends === person2.friends);  //true

原型建立對象的方式將屬性和方法都存在與原型中,也就是說,只要經過這種形式建立的對象都會共享這些屬性和對象,相對於方法共享這是咱們樂於看到的,可是屬性共享讓每一個實例缺失了「個性」;另外對於引用類型的屬性共享時,如上面的例子,多個實例對引用類型的操做會被篡改

實例通常都要有屬於本身的所有屬性,這也決定了原型建立方式的侷限性。下面引入很是經典的對象建立方式

5. 構造函數+原型建立對象(重點)

組合構造函數模式與原型模式:構造函數模式用於定義實力屬性,而原型模式用於定義方法和共享的屬性

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
  }
  
  Person.prototype = {
    constructor : Person,
    sayName : function(){
      alert(this.name);
    }
  }
  
  var person1 = new Person("Nicholas", 29, "Software Engineer");
  var person2 = new Person("Greg", 27, "Doctor");
  
  person1.friends.push("Van");
  alert(person1.friends);    //"Shelby,Count,Van"
  alert(person2.friends);    //"Shelby,Count"
  alert(person1.friends === person2.friends); //false
  alert(person1.sayName === person2.sayName); //true

這種方式建立的實例對象,每一個實例都會有本身的一份實例屬性的副本,但同時又共享着對方法的引用,最大限度地節省了內存;另外,這種方式還支持相構造函數傳遞參數,解決了上面的各類問題

相關文章
相關標籤/搜索