var cat = { name: 'tom', info: this.name + ': 1212', getName: function() { return this.name; } };
注意上例屬性info中,使用了this.name,這裏的this指向window對象,請儘可能避免在定義對象屬性時使用表達式,而將有表達式的內容寫入到函數中。javascript
var dog = new Object(); dog.name = 'tim'; dog.getName = function() { return dog.name; }
delete dog.name;
在window做用域中,不能使用delete刪除var, function定義的屬性和方法,能夠刪除沒有使用var, function定義的屬性和方法java
在實際使用當中,字面量建立對象雖然頗有用,可是它並不能知足咱們的全部需求,咱們但願可以可以和其餘後臺語言同樣建立一個類,而後聲明類的實例就可以屢次使用,而不用每次使用的時候都要從新建立它,因而,便有了工廠模式的出現。jquery
function person(name) { var o = new Object(); o.name = name; o.getName = function() { return this.name; } return o; } var person1 = person('rose'); var person2 = person('jake');
這種模式在函數的內部建立了一個空對象,而後逐一添加屬性和方法,最後返回,實現了對象得以複用的目的。可是存在2個很大的問題chrome
console.log(person1 instanceof person); // false
console.log(person1.getName == person2.getName); // false
其實就至關於每次聲明對象都被從新建立,只不過寫法上簡單了一點而已。瀏覽器
var Person = function(name) { this.name = name; this.getName = function() { return this.name; } } var person1 = new Person('tom'); var person2 = new Person('tim');
使用var或者function聲明函數均可以,只是我寫例子的時候想到什麼就寫了什麼,這個區別在這裏不是重點函數
和工廠模式相比,自定義構造函數沒有在函數內部顯示的建立和返回對象,而是使用this,固然,看上去簡潔了許多,那麼它解決了工廠模式的什麼問題呢?this
console.log(person1 instanceof Person); // ture console.log(person1.getName == person2.getName); //false
從上面代碼能夠看出,對象的類別能夠判斷了,person1就是Person的對象,但是2個同名方法任然不是同一個方法,而是從新建立,其實構造函數內部的實現,能夠將上面的代碼寫成這樣來理解spa
var Person = function(name) { var this = {}; this.name = name; this.getName = function() { return this.name; } return this; }
看上去和工廠模式就有點像,只是this的聲明和返回都是隱式的。下一步,咱們將要介紹關鍵先生prototype
原型指針
原型並無那麼神祕,由於在javascript中,它無處不在。爲了瞭解原型,咱們能夠在chrome瀏覽器的console中,隨意建立一個函數
function a(){}
而後繼續輸入
a.prototype
獲得的結果以下
a { constructor: function a(), _proto_: Object }
沒錯,獲得的這個a,就是關鍵先生原型了。每個函數都有一個proportype屬性,他就像一個指針同樣指向它的原型,而每個原型,都有一個contructor屬性,指向他的構造函數。
那麼原型在建立對象中有什麼用呢?
一個例子,千錘百煉,以下
function Person(name) { this.name = name; } Person.prototype.getName = function() { return this.name; } var person1 = new Person('rose'); var person2 = new Person('Jake');
在這裏,咱們將屬性寫在了構造函數裏,將使人頭疼的方法寫在原型裏,看看上面的問題獲得解決沒有
console.log(person1 instanceof Person); // true console.log(person1.getName === person2.getName); // true
OK,問題完美解決。
固然也能夠將屬性寫入原型中,可是若是那樣的話,屬性就會如同方法同樣被公用了,所以通常來講,屬性會寫入構造函數之中,方法寫入原型之中。固然,這視狀況而定。
若是你想進一步瞭解原型,能夠看下圖。
當咱們使用new person時便會建立一個實例,好比這裏的person1與person2,這裏的實例中,會有一個_proto_屬性指向原型。
當咱們使用實例person1調用方法person.getName( )時,咱們首先找的,是看看構造函數裏面有沒有這個方法,若是構造函數中存在,就直接調用構造函數的方法,若是構造函數不存在,纔回去查找原型中是否存在該方法
function Cat(name) { this.name = name; this.age = 12; this.getName = function() { return 'constructor'; } } Cat.prototype.name = 'proto name'; Cat.prototype.getName = function() { return this.name; } var tim = new Cat('Tim'); console.log(tim.name); // tim console.log(tim.getName()); // constructor
能夠看到上例中,當原型和構造函數中擁有一樣的方法和屬性的時候,構造函數中的被執行。
因而,這裏便會有一個十分重要的概念須要理解,那就是this的指向問題。
在整個建立對象的過程中,this到底指向誰?
function Person(name) { this.name = name; // this.getName = function() { // return 'constructor'; // } } Person.prototype.getName = function() { return this.name; } Person.prototype.showName = function() { return this.getName(); } var rose = new Person('rose'); console.log(rose.showName()); //rose
其實在new執行時,構造函數中的this與原型中的this都被強行指向了new建立的實例對象。
若是須要寫在原型上的方法不少的話,還能夠這樣來寫,讓寫法看上去更加簡潔
Person.prototype = { constructor: Person, getName: function(){}, showName: function(){}, ... }
constructor:Person這一句必不可少,由於{}也是一個對象,當使用Person.prototype = { }時,至關於從新定義了prototype的指向,所以手動修正{}的constructor屬性,讓他成爲Person的原型。
其實經過上面方式,使用構造函數聲明實例的專屬變量和方法,使用原型聲明公用的實例和方法,已是建立對象的完美解決方案了。但是惟一的不足在於,每次建立實例都要使用new來聲明。這樣未免太過麻煩,若是jquery對象也這樣建立,那麼你就會看到一段代碼中有無數個new,但是jQuery僅僅只是使用了$('xxxx')便完成了實例的建立,這是如何作到的呢?
仍是按照慣例,先貼代碼。
(function(window, undefined) { var Person = function(name) { return new Person.fn.init(name); } Person.prototype = Person.fn = { constructor: Person, init: function(name) { this.name = name; return this; }, getName: function() { return this.name; } } Person.fn.init.prototype = Person.fn; window.Person = window.$ = Person; })(window); console.log($('tom').getName());
一步一步來分析
(function(){ ... })()
傳入window參數,是爲了讓jquery對象在外window中能夠被訪問,所以有以下一句代碼
window.Person = window.$ = Person;
這樣咱們就能夠直接使用"$"來調用Person構造函數
當外部調用$( ).getName( )時,函數內部的執行順序以下
new Person.fn.init() // 而init的原型,經過下面一句指向了Person的原型 Person.fn.init.prototype = Person.fn; // 因而就能夠調用原型中的getName方法了