淺談對象(本筆記中截圖和部分代碼取自慕課網視頻http://www.imooc.com/learn/277第四章對象) 面向對象原型鏈繼承這塊,應該算是javascript中最難理解的部分了,小弟腦子比較難轉彎,也是看了好久視頻,博文,慢慢的才有了自己的理解,現在記錄一下學習的內容和總結。首先第一節 ...
淺談對象(本筆記中截圖和部分代碼取自慕課網視頻http://www.imooc.com/learn/277第四章對象)
面向對象原型鏈繼承這塊,應該算是javascript中最難理解的部分了,小弟腦子比較難轉彎,也是看了好久視頻,博文,慢慢的才有了自己的理解,現在記錄一下學習的內容和總結。首先第一節應該說說對象這個東西了,js中對象和其他語言還是有所不同的,現在切入正題,開始淺談對象。
什麼是對象
定義(ECMA-262):無序屬性的集合,其屬性可以包含基本值、對象或者函數。
通過定義可以看出來,對象是屬性的集合,這些屬性又會是一個基本值,一個函數或者又是一個新的對象。記住,函數也是對象,瞭解這點以後原型鏈會很好理解。
對象結構
大概解釋一下這個圖,var obj = {}; obj.x = 1; obj.y = 2;這段代碼先定義了一個obj空對象,然後定義了兩個屬性x和y,每一個屬性又都是一個鍵值對的對象。有writable,enumberable,configurable,value四個屬性和一對get/set方法。一個對象又有三個預設的屬性__proto__,__class__,__extensible__,其中__proto__屬性是指向創建該對象的函數(方法)的prototype對象,如第二個obj的__proto__對象會指向fn的構造函數的prototype屬性。好吧,有點繞,後續會繞出來的。。。
創建對象
1.new
首先,需要說明的是,對象其實都是通過函數創建的,如上圖中的var obj=new fn();,再比如這種:var obj={},arr=[];,其實這兩種創建對象的方法的實質分別為var obj=new Object(),arr = new Array();,所以對象都是通過函數創建的這個話就不足為奇了。
上個模塊我們知道了函數會預設有prototype這個屬性,如下:
這個prototype的屬性值是個對象,即屬性的集合,該屬性一般情況下只有一個constructor屬性,指向函數本身。
我們也可以在自定義函數的prototype中添加一些屬性,看代碼:
1 function Fn(){ 2 3 }; 4 Fn.prototype.x=1; 5 var f1 = new Fn();
這時,實例化的f1對象就可以訪問Fn()的原型屬性x的值了。也就是說,f1對象是從Fn函數new出來的,這樣f1對象就可以調用Fn.prototype中的屬性了。這是因為每個對象都有一個__proto__屬性(chrome將這個屬性暴露了出來,大家可以在chrome瀏覽器上看一下這個屬性),這個屬性引用了創建該對象的函數的prototype屬性,可以得到f1.__proto__===Fn.prototype.__proto__會在下節詳細說明,這個算是原型鏈繼承的關鍵(一條看不見的鏈條連接了所有有血緣關係的家族~)。本節還是繼續扯函數的prototype屬性。
那麼原型這塊大體上可以用下圖來表示:
一句話概括:實例化出來的對象的__proto__屬性連接實例函數的prototype屬性,這樣實例化對象就可以通過看不見的那根鏈條訪問prototype屬性。那麼問題來了:怎麼判斷一個對象的屬性值是原型上的屬性還是自身的屬性,js為我們提供了這麼一個方法:hasOwnProperty()和in操作符,如上,使用in操作符可以判斷屬性是否存在(包含原型鏈上屬性),hasOwnProperty()方法也是判斷屬性是否存在(不包含原型屬性)。
2.Object.create()
根據代碼對比來理解一下:
1 function Person (name) { 2 this.name=name 3 }; 4 Person.prototype.sayName=function(){ 5 console.log(this.name) 6 }; 7 function Teacher(name){ 8 this.name=name 9 }; 10 Teacher.prototype = Person.prototype; 11 var teacher = new Teacher("James"); 12 teacher.sayName(); //James 13 14 Teacher.prototype.teach = function(course){ 15 console.log("I teach "+course) 16 } 17 teacher.teach("English"); //I teach English 18 19 var person = new Person("Lebro"); 20 person.teach("Chinese"); //I teach Chinese
如上第10行代碼,將Person的原型直接賦值給Teacher的原型看似是沒問題的,對象teacher會繼承Person()的sayName()方法,但是給Teacher拓展方法時就會出現共用的問題,Teacher.prototype 會和 Person.prototype指向一處引用,這樣拓展Teacher的原型方法就相當於給Person的原型添加了方法,顯然不是我們想要的;
所以引入了Object.create()方法免除這個問題,實質還是對prototype屬性的賦值:
1 function Person (name) { 2 this.name=name 3 }; 4 Person.prototype.sayName=function(){ 5 console.log(this.name) 6 }; 7 function Teacher(name){ 8 this.name=name 9 }; 10 Teacher.prototype = Object.create(Person.prototype); 11 var teacher = new Teacher("James"); 12 teacher.sayName(); //James 13 14 Teacher.prototype.teach = function(course){ 15 console.log("I teach "+course) 16 } 17 teacher.teach("English"); //I teach English 18 19 var person = new Person("Lebro"); 20 person.teach("Chinese"); //Uncaught TypeError: person.teach is not a function
如上代碼,用Object.create()方法代替直接賦值就會避免這個問題,因此這個方法實際上就是先創建一個空對象,把參數Person.prototype的值有這個空對象進行傳遞給Teacher.prototype,這樣就不會有原型共用的問題了,使用Object.create()方法時考慮到這一點就不會出錯了(沒有研究源碼,只是本人暫時這麼理解的,歡迎指正)。
第一節就先說這麼多,沒有按照常用思維來寫,因為本次執筆只是本人的一次學習總結,是按照我從不懂到慢慢縷清整個思路的一個過程來寫的,可能不會滿足所有人的學習模式,而且最基礎的一些對象方面的知識也沒提到,因為能看到原型繼承這塊我覺得最基本的基礎應該是具有的,所以,請盡情吐槽吧~~~下節說一下對象的屬性操作,包括讀寫,刪除,枚舉,檢測。下節再見了。
原型與繼承學習筆記2 http://www.cnblogs.com/wangxiaosan/p/5535606.html