1.使用Object或對象字面量建立對象前端
2.工廠模式建立對象面試
3.構造函數模式建立對象數組
4.原型模式建立對象app
JS中最基本建立對象的方式:函數
var student = new Object(); student.name = "easy"; student.age = "20";
這樣,一個student對象就建立完畢,擁有2個屬性name
以及age
,分別賦值爲"easy"
和20
。ui
若是你嫌這種方法有一種封裝性不良的感受。來一個對象字面量方式建立對象。this
var sutdent = { name : "easy", age : 20 };
這樣看起來彷佛就完美了。可是立刻咱們就會發現一個十分尖銳的問題:當咱們要建立同類的student1,student2,…,studentn時,咱們不得不將以上的代碼重複n次....spa
var sutdent1 = { name : "easy1", age : 20 }; var sutdent2 = { name : "easy2", age : 20 }; ... var sutdentn = { name : "easyn", age : 20 };
有個提問?能不能像工廠車間那樣,有一個車牀就不斷生產出對象呢?咱們看」工廠模式」。prototype
JS中沒有類的概念,那麼咱們不妨就使用一種函數將以上對象建立過程封裝起來以便於重複調用,同時能夠給出特定接口來初始化對象指針
function createStudent(name, age) { var obj = new Object(); obj.name = name; obj.age = age; return obj; } var student1 = createStudent("easy1", 20); var student2 = createStudent("easy2", 20); ... var studentn = createStudent("easyn", 20);
這樣一來咱們就能夠經過createStudent函數源源不斷地」生產」對象了。看起來已經高枕無憂了,但貪婪的人類總有不知足於現狀的天性:咱們不只但願」產品」的生產能夠像工廠車間通常源源不斷,咱們還想知道生產的產品到底是哪種類型的。
好比說,咱們同時又定義了」生產」水果對象的createFruit()函數:
function createFruit(name, color) { var obj = new Object(); obj.name = name; obj.color = color; return obj; } var v1 = createStudent("easy1", 20); var v2 = createFruit("apple", "green");
對於以上代碼建立的對象v一、v2,咱們用instanceof操做符去檢測,他們通通都是Object類型。咱們的固然不知足於此,咱們但願v1是Student類型的,而v2是Fruit類型的。爲了實現這個目標,咱們能夠用自定義構造函數的方法來建立對象
在上面建立Object這樣的原生對象的時候,咱們就使用過其構造函數:
var obj = new Object();
在建立原生數組Array類型對象時也使用過其構造函數:
var arr = new Array(10); //構造一個初始長度爲10的數組對象
在進行自定義構造函數建立對象以前,咱們首先了解一下構造函數
和普通函數
有什麼區別。
一、實際上並不存在建立構造函數的特殊語法,其與普通函數惟一的區別在於調用方法。對於任意函數,使用new操做符調用,那麼它就是構造函數;不使用new操做符調用,那麼它就是普通函數。
二、按照慣例,咱們約定構造函數名以大寫字母開頭,普通函數以小寫字母開頭,這樣有利於顯性區分兩者。例如上面的new Array(),new Object()。
三、使用new操做符調用構造函數時,會經歷(1)建立一個新對象;(2)將構造函數做用域賦給新對象(使this指向該新對象);(3)執行構造函數代碼;(4)返回新對象;4個階段。
ok,瞭解了構造函數
和普通函數
的區別以後,咱們使用構造函數將工廠模式
的函數重寫,並添加一個方法屬性:
function Student(name, age) { this.name = name; this.age = age; this.alertName = function(){ alert(this.name) }; } function Fruit(name, color) { this.name = name; this.color = color; this.alertName = function(){ alert(this.name) }; }
//這樣咱們再分別建立Student和Fruit的對象
var v1 = new Student("easy", 20); var v2 = new Fruit("apple", "green");
//這時咱們再來用instanceof操做符來檢測以上對象類型就能夠區分出Student以及Fruit了:
alert(v1 instanceof Student); //true alert(v2 instanceof Student); //false alert(v1 instanceof Fruit); //false alert(v2 instanceof Fruit); //true alert(v1 instanceof Object); //true 任何對象均繼承自Object alert(v2 instanceof Object); //true 任何對象均繼承自Object
/*
Stuent,Fruit充當構造函數
this 代指當前對象
建立對象時須要使用 new
*/
這樣咱們就解決了工廠模式
沒法區分對象類型的尷尬。那麼使用構造方法來建立對象是否已經完美了呢?使用構造器函數一般在js中咱們來建立對象。
咱們會發現Student和Fruit對象中共有一樣的方法,當咱們進行調用的時候這無疑是內存的消耗。
咱們徹底能夠在執行該函數的時候再這樣作,辦法是將對象方法移到構造函數外部:
function Student(name, age) { this.name = name; this.age = age; this.alertName = alertName; } function alertName() { alert(this.name); } var stu1 = new Student("easy1", 20); var stu2 = new Student("easy2", 20);
在調用stu1.alertName()時,this對象才被綁定到stu1上。
咱們經過將alertName()函數定義爲全局函數,這樣對象中的alertName屬性則被設置爲指向該全局函數的指針。由此stu1和stu2共享了該全局函數,解決了內存浪費的問題
可是,經過全局函數的方式解決對象內部共享的問題,終究不像一個好的解決方法。若是這樣定義的全局函數多了,咱們想要將自定義對象封裝的初衷便幾乎沒法實現了。更好的方案是經過原型對象模式來解決。
原型鏈甚至原型繼承,是整個JS中最難的一部分也是最很差理解的一部分,在這裏因爲咱們課程定位的緣由,若是對js有興趣的同窗,能夠去查閱一下相關JS原型的一些知識點。更加有助於你之後前端JS的面試。
function Student() { this.name = 'easy'; this.age = 20; } Student.prototype.alertName = function(){ alert(this.name); }; var stu1 = new Student(); var stu2 = new Student(); stu1.alertName(); //easy stu2.alertName(); //easy alert(stu1.alertName == stu2.alertName); //true 兩者共享同一函數
或者這樣寫
function Foo (name,age) { this.Name = name; this.Age = age; } Foo.prototype = { GetInfo: function(){ return this.Name + this.Age }, Func : function(arg){ return this.Name + arg; } }