回想本身已經工做了有一段時間了,可是本身對JavaScript的原型鏈、和繼承的理解能力沒有到位,最近他們完全的整理而且複習了一遍。javascript
本案例中部分文案來自網絡和書籍,若有侵權請聯繫我,我只是把個人理解和想法告訴你們。java
本着互聯網分享精神、如今我就將個人學習以及理解分享給你們。若是那裏又說的不對的地方請私信我,我會及時回覆。c++
講解:不少人見到了久違的new操做符,因而就叫Person爲「類」,但是又沒有關鍵字class的出現,以爲叫「類」有點勉強。因而退而求其次叫Person爲類的構造函數。這些概念好像都沒有錯,之因此出現這樣的狀況,多是由於你們都學習了傳統的面嚮對象語言(c++,c#,Java等),還有一種思惟定勢吧。爲了讓javascript也面向對象,要在javascript中找到與傳統面嚮對象語言的影子。但是按照javascript的說法,function定義的這個Person就是一個Object(對象),並且仍是一個很特殊的對象,這個使用function定義的對象與使用new操做符生成的對象之間有一個重要的區別。這個區別就是function定義的對象有一個prototype屬性,使用new生成的對象就沒有這個prototype屬性。c#
function Person(name) { this.name = name; this.showMe = function () { console.log(this.name); } }; var per= new Person('鉛筆') //實例化這個構造函數 per.showMe() //執行函數內部的方法
講解:在上面的案例中已經說到了,function定義的函數是一個特殊對象,他擁有prototype屬性。prototype屬性能夠在對象的原型中添加原型方法。網絡
請看代碼函數
function Person(name) { this.name = name; this.showMe = function () { console.log(this.name); } }; Person.prototype.user = function (user) { // 向原型中添加原型方法 return user }; Person.prototype.age = 23; // 像原型中添加屬性,而且賦值, var one = new Person('js'); // 實例化方法。 console.log(one.user('這是原型對象')); //調用實例化方法的方法 console.log(one.age); //調用實例化方法的屬性
案例二的代碼中,爲何能夠調用Person的pertotype方法呢。這就要講到Peron在實例化時候發生的事情。學習
第一件事情:創建了一個one對象。(能夠理解爲 var one={})this
第二件事情:將one對象的內置原型對象(__proto__)設置爲構造函數person的prototype屬性所引用的那個原型對象。(能夠理解爲:one.__proto__ = Person.prototype; 這個__proto__稍後再講)spa
第三件事:將one對象做爲this參數調用構造函數person(能夠理解爲var one={ Person.call(this}; 也就至關於當前對象擁有了Person中全部的屬性和方法)prototype
這樣就能夠理解爲何one能夠直接調用proson的pertotype方法了。
在prototype對象中又有一個constructor屬性,這個constructor屬性一樣指向一個constructor對象,而這個constructor對象偏偏就是這個function函數自己(Person)。
function Person(name) { this.name = name; this.showMe = function () { alert(this.name); } }; var one = new Person('js'); alert(one.prototype)//undefined alert(typeof Person.prototype);//object alert(Person.prototype.constructor);//function Person(name) {...};
以上三案例是否是看着有點暈了,其實沒那麼複雜,直接上圖。
在案列二中講到每個函數都有一個protytype屬性,而對象卻沒有,可是對象有一個__propt__內置屬性,而且這個屬性是包含對指定對象的內部原型的引用。原型鏈上的對象正是依靠這個__proto__屬性連結在一塊兒的。注意函數本質也是對象並且是特殊對象,因此函數也有__propt__屬性。
function Person(name) { this.name = name; this.showMe = function () { console.log(this.name); } }; Person.prototype.from = function () { console.log('I come from prototype.'); } var per = new Person('js'); console.log(per.__proto__) //輸出 Person { from: [Function] }
在舉一個原型鏈的例子
var Person = function() {}; Person.prototype.say = function() { console.log("Person say"); }; Person.prototype.salary = 50000; var Programmer = function() {}; Programmer.prototype = new Person(); Programmer.prototype.writeCode = function() { console.log("Programmer writes code"); }; var p = new Programmer(); p.say(); // Person say p.writeCode(); // Programmer writes code console.log(p.salary); // 50000 console.log(p.__proto__) // Person { writeCode: [Function] } console.log(p.__proto__.__proto__) //Person { say: [Function], salary: 50000 } 看沒看到,這裏纔有salary和say方法 這就是原型鏈,直到找到這個方法爲止。 console.log(p.__proto__.__proto__.__proto__) // {} __proto__會一直向上查找,直到{}纔會中止,此刻就會證實沒有找到方法或屬性。到頭了呀親、
原型鏈上圖。唉圖畫的好累啊。
在舉一個修改__proto__指向的例子
function Person(name) { this.name = name; this.showMe = function () { console.log(this.name); } } Person.prototype.from = function () { console.log('I come from prototype.'); }; function SubPer() { this.tests = function () { console.log('tst') } } var son = new SubPer(); console.log(son.__proto__); //SubPer {} son.tests(); // son.from() //此刻會報錯 son.__proto__ = new Person('name'); //修改son實例對象的prototype原型對象的引用 console.log(son.__proto__); // Person { name: undefined, showMe: [Function] } son.showMe() //name son.tests(); //此方法會被調用,由於SubPer函數內自身就有 tests 函數。 son.from(); //此刻會調用Person原型對象方法,由於更改了son實例對象的prototype原型對象的引用。
在JavaScript的對象中有這樣一個神奇的方法hasOwnProperty,他能夠判斷該方法或屬性是對象自身的仍是原型鏈上的,廢話很少說直接上代碼。
function a(name) { this.name = name; this.showName = function () { console.log(this.name) } } function b(name) { this.name = name; this.showName = function () { console.log(this.name) } } b.prototype.say = function () { return 'say' }; var cat = new b('123456'); cat.user = '鉛筆'; cat.showName(); console.log(cat.hasOwnProperty('showName')); //true showName方法是構造函數b內的方法,cat對象是經過實例化b獲得的。在實例化b的時候會有這麼一步 b.call(cat,'123456'); 因此返回true console.log(cat.hasOwnProperty('say')); //false say屬於cat內置原型對象(__propo__)指向原型鏈的方法,不算自身的方法, console.log(cat.hasOwnProperty('user')); //true 雖然屬性是在new 後加的,可是也算是對象的屬性呀,因此返回的ture
那麼若是對象中包含haoOwnProperty怎麼辦。-終極方案
function a(name) { this.name = name; this.showName = function () { console.log(this.name) } } function b(name) { this.name = name; this.showName = function () { console.log(this.name) } } b.prototype.say = function () { return 'say' }; var cat = new b('123456'); cat.hasOwnProperty = function () { return true }; cat.user = '鉛筆'; cat.showName(); console.log(cat.hasOwnProperty('啦啦啦')) // true console.log(({}).hasOwnProperty.call(cat, ('啦啦啦'))); // false 此刻只能採用終極方案,