1、前言html
上個月底,爸爸由於事故忽然離世,說內心話,如今看到'去世','爸爸'這樣的字眼,眼淚都會忍不住在眼眶打轉,仍是須要時間治癒。最近也只是零碎的看了下東西,始終沉不下心去讀書,直到今天仍是決定撿起以前看的JS模式。函數
前面兩篇博客大概記錄了書中前兩章節我以爲一些好用的知識,從這篇開始就是第三章--直接量和構造函數了,難度也不算大,最近下班了在公司花點時間慢慢寫。測試
從第三篇開始,我想在介紹每一個知識點前,先以概要的形式將這一部分介紹的東西以問題的形式列出來,方便你們帶着問題去讀,讀完了再回頭看問題,看本身能不能答出來,這樣也方便檢驗本身對於知識的掌握狀況。this
2、對象直接量spa
概要:什麼是對象,什麼是對象直接量寫法,空對象不是真正的空對象prototype
咱們能夠將js中的對象簡單理解爲名值對組成的散列表,其中值都是名的屬性,值能夠是原始值(string,number...),也能夠是對象,而當對象是函數時,咱們通常稱之爲方法。code
js中自定義的對象任什麼時候候都是可變的,內置本地對象的屬性也是能夠修改的,好比你能夠先建立一個空對象,而後在給它添加一些屬性或方法。而在建立對象時,對象直接量寫法是較爲理想的方式。htm
var me = {}; me.name = "echo"; me.getName = function () { return me.name; };
你能夠刪除對象的某個屬性或方法對象
delete me.name;
其實咱們建立一個對象也不必像上面先建立空對象,再一步步添加屬性方式,在對象建立時能夠同時把你須要定義的屬性同時添加好。blog
let me = { name = "echo", getName = function () { return this.name; } };
那麼這種直接用 = 建立對象的方式就是對象直接量的寫法,很直接不是嗎。對象直接量語法包括:
• 將對象主體包含在一對花括號內。
• 對象內的屬性或方法之間使用逗號分隔。最後一個名值對後也能夠有逗號,但 在IE下會報錯,因此儘可能不要在最後一個屬性或方法後加逗號。
• 屬性名和值之間使用冒號分隔
• 若是將對象賦值給一個變量,不要忘了在右括號以後補上分號
我在上方提到的空對象能夠說是算是一種簡稱,它們並非真正的空對象,即使申明一個{},它也會從Object.prototype繼承不少屬性和方法,可是咱們其實有方法建立嚴格意義上的空對象,我在上一篇文章中就列出了兩種方法,有興趣能夠去看看。
3、經過構造函數建立對象
概要:爲何推薦對象直接量寫法而不是構造函數寫法
儘管JS中沒有類的概念,但當你想快速建立多個具備共同特徵的實例時仍是可使用構造函數,JS中內置了很多構造函數,例如Object(),Date(),String()等等。
咱們用構造函數的寫法來創造上面的對象:
var me = new Object(); me.name = "echo"; me.getName = function () { return me.name; }
相比之下,對象直接量的寫法與構造函數寫法相比代碼更少,推薦直接量寫法的還有兩個緣由
一是它能夠強調對象是一個簡單的可變的散列表,而沒必要必定派生自某個類。
二是當你使用Object()建立對象時,解析器須要順着做用域鏈開始查找,直到找到Object構造函數爲止,而直接量的寫法是不會存在做用域解析行爲。
4、自定義構造函數
概要:當你new一個構造函數時發生了什麼?
除了對象直接量和內置構造函數以外,咱們還能夠經過自定義的構造函數來建立實例對象,像這樣。
var Person = function () { this.name = "echo"; this.sayName = function () { console.log('my name is '+ this.name); }; } var me = new Person(); me.sayName();//my name is echo
說個小插曲,這裏我自定義的構造函數名Person的字母P其實能夠小寫,但咱們都知道,內置構造函數都是大寫開頭,因此爲了讓構造函數更爲醒目,推薦首字母大寫!
很奇怪對不對,咱們new一個構造函數獲得一個實例,這個實例就繼承了構造函數的屬性方法,那new這個過程當中到底發生了什麼?
1.建立一個空對象,將它的引用賦給this,繼承函數的原型。
2.經過this將屬性和方法添加至這個對象。
3.最後返回this指向的新對象。
咱們用代碼模擬這三句話,就像這樣:
var Person = function () { // var this = {}; this.name = "echo"; this.sayName = function () { console.log('my name is '+ this.name); }; // return this; 這裏隱性返回的其實就是上面建立的空對象,這個空對象被賦予了name屬性和一個sayName方法 } var me = new Person(); me.sayName();//my name is echo
在這段代碼中,sayName()方法被添加到了this中,但有個問題,無論咱們執行幾回new Person(),sayName()方法會反覆的被添加到this中,且每次sayName()方法都會在內存中新開內存。
當咱們全部實例中的sayName()方法都是如出一轍時,這種作法是很浪費內存的,推薦作法是將sayName()方法添加在Person的原型中。
var Person = function () { this.name = "echo"; }; Person.prototype.sayName = function () { console.log('my name is '+ this.name); }; var me = new Person(); me.sayName();//my name is echo
關於new一個構造函數到底發生了什麼,我在前面說會隱性的新建一個空對象賦予this,仍是那句話,這裏的空對象並非嚴格意義上的空,它仍是繼承了Person的原型,準確來講應該是這樣。
// var this = Object.create(Person.prototype);
5、構造函數的返回值
概要:構造函數能返回什麼?默認返回什麼?
當咱們new一個構造函數老是會返回一個對象,默認返回this所指向的對象。若是咱們沒有在構造函數內爲this賦予任何屬性,則會返回一個集成了構造函數原型,沒有本身屬性的'空對象'。(若是讀不懂這句話,請結合new發生的過程去理解)
儘管咱們沒在構造函數內寫return語句,也會隱性的返回this,但其實咱們能夠返回自定義的對象。像這樣:
var Person = function () { this.name = "echo"; var that = {}; that.name = "wl"; return that; }; var me = new Person(); me.name;//wl
構造函數能夠返回任意對象,只要你返回的是個對象。假設你返回的不是對象,程序也不會報錯,但這個返回值會被忽略,最終仍是隱性的返回this所指向的對象。
var Person = function () { this.name = "echo"; var name = "wl"; return name; }; var me = new Person(); me.name; //echo
6、強制使用new 的模式
概要:不使用new調用構造函數會怎樣?構造函數內能自定義對象嗎?不使用new也能繼承構造函數原型的作法
構造函數與普通函數無異,只是調用須要使用new,當咱們不使用new調用時,語法也不會出錯,但函數中的this會指向全局對象(非嚴格模式是window)。
var Person = function () { this.name = "echo"; }; var me = Person(); window.name//echo
在這段代碼中實際上建立了一個全局對象屬性name,你能夠經過window.name訪問到它。那麼說到這裏對於構造函數咱們強調兩點。
1.構造函數名首字母大寫
2.調用構造函數使用new
遵照這些約定確定是好的,但在實際開發的構造函數中,咱們經常看看使用that等其它字面量代替this的作法。這麼作的目的是爲了確保構造函數按照本身定義的方式執行,而不存建立空對象賦予this等隱性不可見的行爲,更可預測。
var Person = function () { var that = {}; that.name = "echo"; return that; }; var me = new Person(); me.name;//echo
對於上述代碼中,咱們使用that代替了this,使用that只是一種命名約定,你可使用self,me甚至任意非js語言保留字的字段。
或者that都不建立,直接返回一個對象。
var Person = function () { return { name : "echo" }; }; var me = new Person(); var you = Person(); me.name//echo you.name//echo
這種寫法無論咱們是否使用new去調用,都能獲得一個實例,但這種模式丟失了原型,全部的實例都不會繼承Person()原型上的屬性。
var Person = function () { return { name:'echo' } }; Person.prototype.sayName = function () { console.log(1); }; me.sayName();//報錯,自定義對象未指向Person,沒繼承Person的方法 you.sayName();//報錯,同上
咱們在前面說,不使用new時,this指向window(非嚴格模式),沒法繼承Person的任何屬性。
var Person = function () { this.name = "echo" }; Person.prototype.sayName = function () { console.log(1); }; var me = new Person(); var you = Person(); me.name;//echo you.name;//報錯,此時的name是window的屬性 me.sayName();//1 you.sayName();//報錯,在實例化過程當中this指向window,並未繼承Person的方法
那有辦法可讓不使用new狀況下實例也能繼承Person屬性的作法嗎,固然有,好比調用自身的構造函數:
var Person = function () { if(!(this instanceof Person)){ return new Person(); } this.name = "echo" }; Person.prototype.sayName = function () { console.log(1); }; var me = new Person(); var you = Person(); me.name;//echo you.name;//echo me.sayName();//1 you.sayName();//1
看到沒,沒使用new的實例you也繼承了Person的屬性和方法,若是看不懂那應該是對於instanceof運算符不太瞭解,這裏順帶說下
instanceof運算符用於測試構造函數的prototype屬性是否出如今對象的原型鏈中的任何位置---MDN
object instanceof constructor object:要檢測的對象. constructor:某個構造函數
function Car(make, model, year) { this.make = make; this.model = model; this.year = year; } var auto = new Car('Honda', 'Accord', 1998); console.log(auto instanceof Car);//true console.log(auto instanceof Object);//true
那麼上述代碼中,new Person()好理解,this就是隱性返回的實例,this instanceof Person爲true跳過判斷,直接走new一個構造函數時發生的過程,獲得實例天然會繼承Person的屬性和方法。
Person()呢,this指向window,很明顯this instanceof Person爲false,假假爲真,執行判斷內的代碼new Person();同理,也走了new過程的三部曲,獲得的實例也繼承了Person的屬性和方法。
有疑問或者錯誤也歡迎你們指出來。
最近老是以爲沒什麼值得開心的事情,趁着雙十二,給本身換了個鍵盤,畢竟天天都是要寫代碼的,也算換一個心情。
儘管除了吃飯交房租就沒什麼開銷,仍是挺心疼的。
若是你們對於new過程還有疑惑,以及如何實現一個new方法,歡迎閱讀博主這篇文章 js new一個對象的過程,實現一個簡單的new方法
那麼就先寫到這裏了。