JavaScript面向對象與原型

工廠模式:沒法識別對象瀏覽器

function createObject(name, age) { //集中實例化的函數
  var obj = new Object();
  obj.name = name;
  obj.age = age;
  obj.run = function () {
    return this.name + this.age + '運行中...';
  };
  return obj;
}
var box1 = createObject('Lee', 100); //第一個實例
var box2 = createObject('Jack', 200); //第二個實例
alert(box1.run());
alert(box2.run()); //保持獨立函數

alert(typeof box1); //Object
alert(box1 instanceof Object); //true測試

構造函數(構造方法):能夠識別對象this

function Box(name, age) { //構造函數模式
  this.name = name;
  this.age = age;
  this.run = function () {
    return this.name + this.age + '運行中...';
  };
}
var box1 = new Box('Lee', 100); //new Box()便可
var box2 = new Box('Jack', 200);prototype

alert(box1.run());
alert(box1 instanceof Box); //很清晰的識別他從屬於 Box3d

構造函數的方法有一些規範:指針

1.函數名和實例化構造名相同且大寫,(PS:非強制,但這麼寫有助於區分構造函數和普通函數);對象

2.經過構造函數建立對象,必須使用 new 運算符。blog

var o = new Object();
Box.call(o, 'Jack', 200) //對象冒充調用
alert(o.run());繼承

原型(共享):建立的每一個函數都有一個 prototype(原型)屬性,這個屬性是一個對象,它的用途是包含能夠由特定類型的全部實例共享的屬性和方法。邏輯上能夠這麼理解:prototype 經過調用構造函數而建立的那個對象的原型對象。使用原型的好處可讓全部對象實例共享它所包含的屬性和方法。也就是說,沒必要在構造函數中定義對象信息,而是能夠直接將這些信息添加到原型中。

unction Box() {} //聲明一個構造函數
Box.prototype.name = 'Lee'; //在原型裏添加屬性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型裏添加方法
  return this.name + this.age + '運行中...';
};

在原型模式聲明中,多了兩個屬性,這兩個屬性都是建立對象時自動生成的。

__proto__屬性:是實例指向原型對象的一個指針,它的做用就是指向構造函數的原型屬性 constructor 。經過這兩個屬性,就能夠訪問到原型裏的屬性和方法了。

PS:IE 瀏覽器在腳本訪問__proto__會不能識別,火狐和谷歌瀏覽器及其餘某些瀏覽器均能識別。雖然能夠輸出,但沒法獲取內部信息。

判斷一個對象是否指向了該構造函數的原型對象,可使用 isPrototypeOf()方法來測試。

exp:alert(Box.prototype.isPrototypeOf(box)); //只要實例化對象,即都會指向

原型模式的執行流程:
1.先查找構造函數實例裏的屬性或方法,若是有,馬上返回;
2.若是構造函數實例裏沒有,則去它的原型對象裏找,若是有,就返回;

雖然咱們能夠經過對象實例訪問保存在原型中的值,但卻不能訪問經過對象實例重寫原型中的值。

var box1 = new Box();
alert(box1.name); //Lee,原型裏的值
box1.name = 'Jack';
alert(box.1name); //Jack,就近原則,
var box2 = new Box();
alert(box2.name); //Lee,原型裏的值,沒有被 box1 修改
若是想要 box1 也能在後面繼續訪問到原型裏的值,能夠把構造函數裏的屬性刪除便可 ,具體以下:
delete box1.name; //刪除屬性
alert(box1.name);
如何判斷屬性是在構造函數的實例裏,仍是在原型裏?可使用 hasOwnProperty()函數來驗證:

alert(box.hasOwnProperty('name')); //實例裏有返回 true,不然返回 false

 

in 操做符會在經過對象可以訪問給定屬性時返回 true,不管該屬性存在於實例中仍是原型中。
alert('name' in box); //true,存在實例中或原型中
咱們能夠經過 hasOwnProperty()方法檢測屬性是否存在實例中,也能夠經過 in 來判斷實例或原型中是否存在屬性。那麼結合這兩種方法,能夠判斷原型中是否存在屬性。

function isProperty(object, property) { //判斷原型中是否存在屬性
  return !object.hasOwnProperty(property) && (property in object);
}

var box = new Box();
alert(isProperty(box, 'name')) //true,若是原型有

爲了讓屬性和方法更好的體現封裝的效果,而且減小沒必要要的輸入,原型的建立可使用字面量的方式:

function Box() {};
Box.prototype = { //使用字面量的方式
  name : 'Lee',
  age : 100,
  run : function () {
    return this.name + this.age + '運行中...';
  }
};

使用構造函數建立原型對象和使用字面量建立對象在使用上基本相同,但仍是有一些區別,字面量建立的方式使用 constructor 屬性不會指向實例,而會指向 Object,構造函數建立的方式則相反。

若是想讓字面量方式的 constructor 指向實例對象,那麼能夠這麼作:
Box.prototype = {
  constructor : Box, //直接強制指向便可
};

原型對象不單單能夠在自定義對象的狀況下使用,而 ECMAScript 內置的引用類型均可以使用這種方式,而且內置的引用類型自己也使用了原型。

alert(Array.prototype.sort); //sort 就是 Array 類型的原型方法
alert(String.prototype.substring); //substring 就是 String 類型的原型方法
String.prototype.addstring = function () { //給 String 類型添加一個方法
  return this + ',被添加了!'; //this 表明調用的字符串
};
alert('Lee'.addstring()); //使用這個方法

PS:儘管給原生的內置引用類型添加方法使用起來特別方便,但咱們不推薦使用這種方法。由於它可能會致使命名衝突,不利於代碼維護。

動態原型模式

function Box(name ,age) { //將全部信息封裝到函數體內
  this.name = name;
  this.age = age;
  if (typeof this.run != 'function') { //僅在第一次調用的初始化
    Box.prototype.run = function () {
      return this.name + this.age + '運行中...';
    };
  }
}
var box = new Box('Lee', 100);
alert(box.run());

PS:使用動態原型模式,要注意一點,不能夠再使用字面量的方式重寫原型,由於會切斷實例和新原型之間的聯繫。

寄生構造函數

function Box(name, age) {
  var obj = new Object();
  obj.name = name;
  obj.age = age;
  obj.run = function () {
    return this.name + this.age + '運行中...';
  };
  return obj;
}

穩妥構造函數

function Box(name , age) {
  var obj = new Object();
  obj.run = function () {
    return name + age + '運行中...'; //直接打印參數便可
  };
  return obj;
}
var box = Box('Lee', 100); //直接調用函數
alert(box.run());

繼承:依靠原型鏈完成

function Box() { //Box 構造
  this.name = 'Lee';
}
function Desk() { //Desk 構造
  this.age = 100;
}
Desk.prototype = new Box(); //Desc 繼承了 Box,經過原型,造成鏈條
var desk = new Desk();
alert(desk.age);
alert(desk.name); //獲得被繼承的屬性
function Table() { //Table 構造
  this.level = 'AAAAA';
}
Table.prototype = new Desk(); //繼續原型鏈繼承
var table = new Table();
alert(table.name); //繼承了 Box 和 Desk

在 JavaScript 裏,被繼承的函數稱爲超類型(父類,基類也行,其餘語言叫法),繼承的函數稱爲子類型(子類,派生類)。繼承也有以前問題,好比字面量重寫原型會中斷關係,使用引用類型的原型,而且子類型還沒法給超類型傳遞參數。

對象冒充(僞造對象、經典繼承、借用構造函數):解決引用共享和超類型沒法傳參的問題

function Box(age) {
  this.name = ['Lee', 'Jack', 'Hello']
  this.age = age;
}
function Desk(age) {
  Box.call(this, age); //對象冒充,給超類型傳參

}
var desk = new Desk(200);
alert(desk.age);
alert(desk.name);
desk.name.push('AAA'); //添加的新數據,只給 desk
alert(desk.name);

組合繼承:原型鏈+ 借用構造函數

function Box(age) {
  this.name = ['Lee', 'Jack', 'Hello']
  this.age = age;
}
Box.prototype.run = function () {
  return this.name + this.age;
};
function Desk(age) {
  Box.call(this, age); //對象冒充
}
Desk.prototype = new Box(); //原型鏈繼承
var desk = new Desk(100);
alert(desk.run());

寄生組合繼承

function obj(o) {     //傳遞一個字面量函數
  function F() {}    //臨時新建一個構造函數,用來存儲傳遞進來的對象
  F.prototype = o;    //將o對象實例賦值給F構造的原型對象

  return new F();    //返回實例化後的構造函數}function create(box, desk) {  var f = obj(box.prototype);  f.constructor = desk;  desk.prototype = f;}function Box(name) {  this.name = name;  this.arr = ['哥哥','妹妹','父母'];}Box.prototype.run = function () {  return this.name;};function Desk(name, age) {  Box.call(this, name);  this.age = age;}inPrototype(Box, Desk); //經過這裏實現繼承var desk = new Desk('Lee',100);desk.arr.push('姐姐');alert(desk.arr);alert(desk.run()); //只共享了方法var desk2 = new Desk('Jack', 200);alert(desk2.arr); //引用問題解決

相關文章
相關標籤/搜索