精品伊人久久大香线蕉,开心久久婷婷综合中文字幕,杏田冲梨,人妻无码aⅴ不卡中文字幕

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
深入淺出JavaScript之原型鏈&繼承

Javascript語言的繼承機制,它沒有'子類'和'父類'的概念,也沒有'類'(class)和'實例'(instance)的區分,全靠一種很奇特的'原型鏈'(prototype chain)模式,來實現繼承。

這部分知識也是JavaScript里的核心重點之一,同時也是一個難點。我把學習筆記整理了一下,方便大家學習,同時自己也加深印象。這部分代碼的細節很多,需要反復推敲。那我們就開始吧。

小試身手

原型鏈例子(要點寫在注釋里,可以把代碼復制到瀏覽器里測試,下同)

function foo{} //通過function foo{}定義一個函數對象foo.prototype.z = 3; //函數默認帶個prototype對象屬性 (

typeof foo.prototype;//'object'

)var obj =new foo; //我們通過new foo構造器的方式構造了一個新的對象obj.y = 2; //通過賦值添加兩個屬性給objobj.x = 1; //

通過這種方式構造對象,對象的原型會指向構造函數的prototype屬性,也就是foo.prototype

obj.x; // 1 //當訪問obj.x時,發現obj上有x屬性,所以返回1obj.y; // 2 //當訪問obj.y時,發現obj上有y屬性,所以返回2obj.z; // 3 //當訪問obj.z時,發現obj上沒有z屬性,那怎么辦呢?它不會停止查找,它會查找它的原型,也就是foo.prototype,這時找到z了,所以返回3
//我們用字面量創建的對象或者函數的默認prototype對象,實際上它也是有原型的,它的原型指向

Object.prototype

,然后Object.prototype也是有原型的,它的原型指向

null


//那這里的Object.prototype有什么作用呢?
typeof obj.toString; // ‘function'

//我們發現typeof obj.toString是一個函數,但是不管在對象上還是對象的原型上都沒有toString方法,因為在它原型鏈的末端null之前都有個Object.prototype方法,
//而toString正是Object.prototype上面的方法。這也解釋了為什么JS基本上所有對象都有toString方法
'z' in obj; // true //obj.z是從foo.prototype繼承而來的,所以'z' in obj返回了true
obj.hasOwnProperty('z'); // false //但是obj.hasOwnProperty('z')返回了false,表示z不是obj直接對象上的,而是對象的原型鏈上面的屬性。(hsaOwnProperty也是Object.prototype上的方法)

剛才我們訪問x,y和z,分別通過原型鏈去查找,我們可以知道:當我們訪問對象的某屬性時,而該對象上沒有相應屬性時,那么它會通過原型鏈向上查找,一直找到null還沒有話,就會返回undefined。

基于原型的繼承

function Foo{ this.y = 2; }Foo.prototype.x = 1;var obj3 = new Foo; //

①當使用new去調用的時候,函數會作為構造器去調用

②this會指向一個對象(這里是obj3),而這個對象的原型會指向構造器的prototype屬性(這里是Foo.prototype)

obj3.y; //2
obj3.x; //1 //可以看到y是對象上的,x是原型鏈上的原型(也就是Foo.prototype上)
prototype屬性與原型

我們再來看看Foo.prototype是什么樣的結構,當我們用函數聲明去創建一個空函數的時候,那么這個函數就有個prototype屬性,并且它默認有兩個屬性,constructor和__proto__,

constructor屬性會指向它本身Foo,__proto__是在chrome中暴露的(不是一個標準屬性,知道就行),那么Foo.prototype的原型會指向Object.prototype。因此Object.prototype上

的一些方法toString,valueOf才會被每個一般的對象所使用。

function Foo{}typeof Foo.prototype; // 'object'Foo.prototype.x = 1;var obj3 = new Foo;

總結一下:我們這里有個Foo函數,這個函數有個prototype的對象屬性,它的作用就是當使用new Foo去構造實例的時候,這個構造器的prototype屬性會用作new出來的這些對象的原型。

所以我們要搞清楚,prototype和原型是兩回事,prototype是函數對象上的預設屬性,原型通常是構造器上的prototype屬性。

實現一個class繼承另外一個class
function Person(name, age) { this.name = name; //直接調用的話,this指向全局對象(this知識點整理) this.age = age; //

使用new調用Peoson的話,this會指向原型為Person.prototype的空對象,通過this.name給空對象賦值,最后this作為return值

}Person.prototype.hi = function {

//通過Person.prototype.hi創建所有Person實例共享的方法,(可以參考上節的左圖:對象的原型會指向構造器的prototype屬性,所以想讓obj1,obj2,obj3共享一些方法的話,只需在原型對象上一次性地添加屬性和方法就可以了);

console.log('Hi, my name is ' + this.name + ',I am ' + this.age + ' years old now.')//這里的this是全局對象};Person.prototype.LEGS_NUM = 2; //再設置一些對Person類的所有實例共享的數據Person.prototype.ARMS_NUM = 2;Person.prototype.walk = function { console.log(this.name + ' is walking...');};function Student(name, age, className) { //每個學生都屬于人 Person.call(this, name, age); //在Student這個子類里面先調用一下父類 this.className = className;}
//下一步就是我們怎么去把Student的實例繼承Person.prototype的一些方法

Student.prototype = Object.create(Person.prototype); //Object.create:創建一個空對象,并且這個對象的原型指向它的參數 //這樣子我們可以在訪問Student.prototype的時候可以向上查找到Person.prototype,又可以在不影響Person的情況下,創建自己的方法

Student.prototype.constructor = Student; //保持一致性,不設置的話constructor會指向PersonStudent.prototype.hi = function { //通過Student.prototype.hi這樣子的賦值可以覆蓋我們基類Person.prototype.hi console.log('Hi, my name is ' + this.name + ',I am ' + this.age + ' years old now, and from ' + this.className + '.');}Student.prototype.learn = function(subject) { //同時,我們又有自己的learn方法 console.log(this.name + 'is learning ' + subject + ' at' + this.className + '.');};//testvar yun = new Student('Yunyun', 22, 'Class 3,Grade 2');yun.hi; //Hi,my name is Yunyun,I'm 22 years old now,and from Class 3, Grade 2.console.log(yun.ARMS_NUM); // 2 //我們本身對象是沒有的,對象的原型也就是Student.prototype也沒有,但是我們用了繼承,繼續向上查找,找到了Person.prototype.ARMS_NUM,所以返回2yun.walk; //Yunyun is walking...yun.learn('math'); //Yunyun is learning math at Class 3,Grade 2.

結合圖我們來倒過來分析一下上面代碼:我們先通過new Student創建了一個Student的實例yun,yun的原型指向構造器的prototype屬性(這里就是Student.prototype), Student.prototype上有hi方法和learn方法,Student.prototype是通過Object.create(Person.prototype)構造的,所以這里的Student.prototype是空對象,并且這個對象的原型指向Person.prototype,接著我們在Person.prototype上也設置了LEGS_NUM,ARMS_NUM屬性以及hi,walk方法。然后我們直接定義了一個Person函數,Person.prototype就是一個預置的對象,它本身也會有它的原型,它的原型就是Object.prototype,也正是因為這樣,我們隨便一個對象才會有hasOwnProperty,valueOf,toString這樣些公共的函數,這些函數都是從Object.prototype上來的。這樣子就實現了基于原型鏈的繼承。 那我們調用hi,walk,learn方法的時候發生了什么呢?比如我們調用hi方法的時候,我們首先看這個對象yun上有沒有hi方法,但是在這個實例中沒有所以會向上查找,查找到yun的原型也就是Student.protoype上有這hi方法,所以最終調用的是Student.prototype.hi,調用其他方法也是類似的。

改變prototype

我們知道JavaScript中的prototype原型不像Java中的class,Java中的class一旦寫好就很難動態的去改變了,但是JavaScript中的原型實際上也是普通的對象,那就意味著在程序運行的階段,我們也可以動態的給prototype添加或刪除些屬性。

在上述代碼的基礎上,我們已經有yun這個實例了,我們接著來進行實驗:

Student.prototype.x = 101; //通過Student.prototype.x把yun的原型動態地添加一個屬性xyun.x; //101 //那我們發現所有的實例都會受到影響//接著我們做個有趣的實驗Student.prototype = {y:2}; //我們直接修改構造器的prototype屬性,把它賦值為一個新的對象yun.y; //undefined yun.x; //101 //所以我們得出:當我們

修改Student.prototype值的時候,并不能修改已經實例化的對象

var Tom = new Student('Tom',3,'Class LOL KengB');
Tom.x; //undefined //但當我們創建一個新的實例時,這一次x就不見了,
Tom.y; //2 //并且y是新的值

所以說當動態修改prototype的時候,是會影響所有已創建或新創建的實例的,但是修改整個prototype賦值為新的對象的話,對已創建的實例是不會影響的,但是會影響后續的實例。

實現繼承的方式

實現繼承有多種方式,下面我們還是以Person和Student來分析

function Person {}function Student {}Student.prototype = Person.prototype; // 我們可不可用這種方式呢?這種方法是錯誤的:因為子類Student有自己的一些方法
//,如果通過這樣子賦值,改變Student的同時也改變了Person。Student.prototype = new Person; //這種方式是可以實現的,但是調用構造函數有時候也是有問題的,比如要傳進Person一個name和age
//,這里的Student是個類,還沒實例化,這時候有些奇怪了,傳什么都不是。Student.prototype = Object.create(Person.prototype); //相對來說這中方式是比較理想的,這里我們創建了一個空的對象
//,并且對象的原型指向Person.prototype,這樣我們既保證了繼承了Person.prototype上的方法,并且Student.prototype又有自己空的對象。
//但是Object.create是ES5以后才有的
總結

做項目的時候才發現這些基礎概念有多么的重要,如果不把它們逐個落實了,真的是一不小心就會掉進坑里。后續會繼續對JavaScript其他部分以及node作相關總結,歡迎關注。

本站僅提供存儲服務,所有內容均由用戶發布,如發現有害或侵權內容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
詳解prototype與
ECMAScript:原型鏈 (prototype 與
字節跳動最愛考的web前端面試題:計算機網絡基礎
JavaScript構造函數及原型對象
js的原型及繼承實現 | 一鍋亂燉
深入理解javascript原型和閉包(6)
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯系客服!

聯系客服

主站蜘蛛池模板: 宜城市| 江山市| 山东| 辉南县| 黎城县| 卓尼县| 永仁县| 潮州市| 孙吴县| 临沧市| 公主岭市| 于田县| 卢氏县| 安泽县| 来凤县| 山西省| 丹江口市| 纳雍县| 靖宇县| 义乌市| 余干县| 庆阳市| 太仆寺旗| 涞源县| 兴城市| 邓州市| 韶关市| 龙里县| 井陉县| 阿鲁科尔沁旗| 台湾省| 库伦旗| 齐齐哈尔市| 安多县| 玛多县| 乌拉特中旗| 新蔡县| 错那县| 旬阳县| 区。| 安康市|