js繼承——從建立對象開始

從建立對象開始安全

建立對象的簡單方法就是:使用Object構造函數或者對象字面量。但這兩個方法有個缺點:建立相同對象,要編寫大量重複代碼。app

爲了不這個問題——>工廠模式:用函數封裝以特定接口建立對象函數

function createPerson(name, age){            
  var o = new Object();
  o.name = name;
  o.age = age;
  o.sayName = function(){
    return this.name;
  }
  return o;
}
var p1 = createPerson("Tom", 23);
var p2 = createPerson("Joe", 20)
p1 instanceof createPerson    //false 
 

工廠模式:優勢: 解決了建立多個類似對象問題   缺點:沒有解決對象識別問題(不知道對象的類型)this

爲了解決對象識別問題——>構造函數模式:建立特定類型的對象 spa

function Person(name, age){
  this.name = name;
  this.age = age;
  this.sayName = function(){
      return this.name;
  }
}
var p1 = new Person("Tom", 23);
var p2 = new Person("Joe", 20);
p1 instanceof Person    //true   檢測對象類型
p1.constructor === Person  //true 識別對象類型


p1.sayName === p2.sayName //false

構造函數模式:Person()與createPerson()   不一樣之處:沒有顯示建立對象、將屬性方法值賦值給this、沒有return 語句,還有就是函數名一個字母大寫。prototype

構造函數也是函數,那麼函數用new執行會發生什麼呢(重點)設計

          1.建立一個空對象。code

          2.將構造函數的做用域賦值給新對象(this指向該對象, 同時還繼承了該函數的原型)。對象

          3.執行構造函數中的代碼(添加屬性和方法)blog

          4.隱式的返回this(返回該對象, 若是沒有顯示的返回其餘對象的話)

上面說了構造函數也是函數,只是使用了new 調用,若是不使用new就和普通函數同樣

var wp = Person("Marry", 18);
window.sayName();  //"Marry"
wp.sayName();  //TypeError: Cannot read property 'sayName' of undefined
sayName()    //"Marry"

構造函數解決了對象識別問題,可是缺點:正如前面(p1.sayName === p2.sayName //false )會致使不一樣做用域鏈和標識符解析

//構造函數中的
this.sayName = function(){
      return this.name;
  }
//等價於
this.sayName = new Function("return this.name;")

爲了解決這個問題,咱們能夠把sayName函數放外面做爲全局函數,但這方法缺點頗多,很不合適。因而...

出現了——>原型模式:函數有個prototype屬性,指向一個對象,該對象能夠包含由特定類型的全部實例共享的屬性和方法,簡稱之prototype是p1,p2的原型對象。這個原型對象可讓全部實例共享它包含的方法和屬性。

function Person(){}
Person.prototype.name = "Tom";
Person.prototype.age = 20;
Person.prototype.friends = ["A", "B", "C"];
Person.prototype.sayName = function(){
  return this.name;
}
var p1 = new Person();
var p2 = new Person();
p1.sayName === p2.sayName;   //true

p1.name = "other";
p2.friends.push("D");
p1.sayName(); // "other" ---來自實例
p2.sayName(); // "Tom"; ---來自原型

p1.friends;  //["A", "B", "C", "D"]  ----引用類型
p2.friends;  //["A", "B", "C", "D"]

Person.prototype.isPrototypeOf(p1)   //true
Object.getPrototypeOf(p1) === Person.prototype  //true

//in 經過對象能訪問給定屬性就返回true,無論實例仍是原型
"name" in p1;  //true
"name" in p2;  //true

//
hasOwnProperty() 只返回實例中的屬性 p1.hasOwnProperty("name"); //true p2.hasOwnProperty("name"); //false //Object.keys() 返回對象上全部可枚舉實例屬性 Object.keys(Person.prototype); //["name", "age", "friends", "sayName"] Object.keys(p1); //["name"]
//
Object.getOwnPropertyNames() 返回全部實例屬性,不管是否可枚舉 Object.getOwnPropertyNames(Person.prototype) //["constructor", "name", "age", "friends", "sayName"] Object.getOwnPropertyNames(p1) //["name"]

說明:實例,構造函數,原型的關係:實例的[[Prototype]](__proto__)只指向原型、構造函數的prototype屬性指向原型、原型的constructor指向構造函數。

         in操做符:在<= IE8中 in不會枚舉[[Enumerate]]爲false的同名屬性 ;在safari3中 in會枚舉被隱藏的屬性。 因此in慎用。

        其中上述代碼

Person.prototype.name = "Tom";
Person.prototype.age = 20;
Person.prototype.friends = ["A", "B", "C"];
Person.prototype.sayName = function(){
  return this.name;
}
//可寫成:            ---至關於重寫原型
Person.prototype = {
  constructor: Person,
  name = "Tom",
  age = 20,
  friends = ["A", "B", "C"],
  sayName = function(){
    return this.name;
  }
}

//支持ES5中,可去掉 上句 constructor: Person,添上以下代碼:
  Object.defineProperty(Person.prototype, "constructor", {
      enumerate: false,
      value: Person 
  });

原型模式:雖然解決了構造函數的缺點問題,但缺點: 1.默認狀況下取相同的值;2.引用類型的實例被不少實例共享

爲了不這些問題——〉組合使用構造函數和原型模式:構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享屬性, 最大限度的節省了內存

function Person(name, age){
  this.name = name;
  this.age = age;
  this.friends = ["A", "B", "C"];
}
Person.prototype = {
  constructor: Person,
  sayName: function(){
        return this.name;
    }
}
var p1 = new Person("Tom", 34);
var p2 = new Person("Joe", 21);
p1.friends.push("D");
p1.friends    //["A", "B", "C", "D"]
p2.friends   //["A", "B", "C"]
p1.sayName === p2.sayName;  //true

組合模式:優勢:集兩模式優勢於一身,極好!所以該模式使用最普遍、認知度最高。

東西一好,就有人挑骨頭,其餘oo語言開發經驗者看到獨立的構造函數與原型會以爲很困惑,因而...

出現了——>動態原型模式:將全部信息封裝在構造函數中

function Person(name, age){
  this.name = name;
  this.age = age;
  this.friends = ["A", "B", "C"];
  if (typeof this.sayName != "function") {
    Person.prototype.sayName = function(){
        return this.name;
    }
  } 
}

//建立第一個實例纔會執行,此後,原型完成初始化
var p1 = new Person("Tom", 23);
p1.sayName();  //"Tom"

至此,若是都不適用的話...

出現了——〉寄生構造函數模式:建立一個函數,做用僅僅是封裝建立對象的代碼,而後再返回新建立的對象

function Person(name, age){            
  var o = new Object();
  o.name = name;
  o.age = age;
  o.sayName = function(){
    return this.name;
  }
  return o;
}
var p1 = new Person("Tom", 23);
p1.sayName();

該模式準確的說與工廠模式如出一轍。只是把該函數當構造函數調用(使用new)而與構造函數模式類似,又有點不一樣,這不一樣之處就是:顯示的建立對象,重寫了返回值。

既然如此,這個模式的優點在哪裏呢?對於構造函數模式而言,它能夠new一個除object類型以外的其餘類型——在不擴展原生構造函數的狀況下自定義一個擴展型的構造函數。

function SpecialArray(){
   var values = new Array();
   values.push.apply(values, arguments);
   values.toPipedString = function(){
     return this.join("|"); 
   };
   return values;
}
var friends = new SpecialArray("A", "B", "C");
friends.toPipedString();  //"A|B|C"
friends instanceof SpecialArray  //false
friends instanceof Array  //true

說明:構造函數返回的對象與構造函數或者構造函數的原型之間沒有任何關係,也就是說構造函數外面建立或者裏面建立是沒有區別的,(只是寄生在構造函數中)所以沒法用instanceof來識別對象。

還有一種建立對象的方法:——>穩妥構造函數模式:沒有公共屬性,不使用this、new,適合安全環境下使用

function Person(name, age){
}
function Person(name){
  var o = new Object();
  o.sayName = function(){
     return name;
  }
  return o;
}
var p1 = Person("Tom");
var p2 = Person("Joe");
p1.name  //undefined
p2.name  //undefined
p1.sayName()  //"Tom"
p2.sayName()  //"Joe"       傳入構造函數的name之恩那個經過sayName()訪問

穩妥構造函數模式與寄生構造函數模式相似:建立的對象與構造函數之間沒有任何關係。因此沒法用instanceof來識別對象。

該博客與js高級程序設計內容類似,加之本身的理解與總結,不對之處歡迎指出。詳情可看js高級程序設計。

相關文章
相關標籤/搜索