目錄 js面向對象編程 js原型鏈 共用方法 原型繼承 class繼承 js面向對象編程 js原型鏈 共用方法 原型繼承 class繼承 js面向對象編程 js面向對象編程不同於 java 的類和對象 JavaScript 不區分類和實例的概念,而是通過原型(prototype)來實現面向對象編程。 ...
目錄
js面向對象編程
js面向對象編程不同於 java 的類和對象
JavaScript 不區分類和實例的概念,而是通過原型(prototype)來實現面向對象編程。
js聲明的構造函數,類似於普通函數的聲明,但又不同,
實例對象時,如果不寫new,就是一個普通函數,它返回 undefined。
但是,如果寫了new,它就變成了一個構造函數,它綁定的 this 指向新創建的對象,
並預設返回 this,也就是說,不需要在最後寫return this;。
js原型鏈
代碼段一:
function Student(name){ this.name = name; this.say = function(){ console.log('my name:', this.name); } } let student1 = new Student('student1'); let student2 = new Student('student2');
console.log(student1.constructor === Student.prototype.constructor) // true
橙色箭頭表示原型鏈,其原型鏈為:
student1 --> Student.prototype --> Object.prototype --> null
當我們用 obj.xx
訪問一個對象的屬性時,JavaScript引擎先在當前對象上查找該屬性,
如果沒有找到,就到其原型對象上找,如果還沒有找到,就一直上溯到Object.prototype
對象,
最後,如果還沒有找到,就只能返回undefined
。
共用方法
代碼段二:
function Student2(){ this.say = function(){ console.log('hi') } } console.log(new Student2().say === new Student2().say)
結果:
false
實例化的對象方法,雖然方法名稱和代碼完全一樣,但是不同對象指向的不是同一個方法
需要創建一個共用的方法,
根據原型鏈圖,需要將這個共用方法聲明在 Student2 的原型對象上,
xxx.prototype.xxx = function(){}
function Student2(){ this.say = function(){ console.log('hi') } } Student2.prototype.publicSay = function(){ console.log('public say'); } console.log(new Student2().say === new Student2().say) console.log(new Student2().publicSay === new Student2().publicSay)
結果:
false true
原型繼承
學過 java 的都知道,類的繼承通過 extends 會很容易實現,
但是 javascript 的原型繼承有點麻煩,不過 class繼承就很方便
function Father(name){ this.say = function(){ console.log(name) } } function Son(name){ Father.call(this, name) } console.log(Son.prototype.__proto__) // Object
這樣看似繼承了,但是其原型鏈的指向並沒有改變
其原型鏈圖為:
要實現原型繼承,看圖的話很容易,只需要將 Son 的原型對象的原型指向 Father 的原型對象
要實現原型繼承,這裡有三種方法,
法一:
這個方法簡介明瞭,但是不推薦直接通過 __proto__ 直接改變原型
function Father(name){ this.say = function(){ console.log(name) } } function Son(name){ Father.call(this, name) } Son.prototype.__proto__ = Father.prototype; console.log(Son.prototype.__proto__) // Father
法二:
通過實例化 Father 生成一個對象,
new Father() 的原型會預設指向 Father 的原型
通過修改 Son 的 prototype 屬性和 new Father() 的 constructor 屬性,
來綁定 Son 和 new Father() 之間的關係
function Father(name){ this.say = function(){ console.log(name) } } function Son(name){ Father.call(this, name) } Son.prototype = new Father(); Son.prototype.constructor = Son; console.log(Son.prototype.__proto__) // Father
法三:
類似法二,聲明一個中間對象來改變指向
Mid.prototype = Father.prototype;
Son.prototype = new Mid();
Son.prototype.constructor = Son;
第一步,將 Mid 的原型對象指向 Father 的原型對象,
第二步,將 Son 的屬性 prototype 指向 Mid,
此時代碼上的 new Mid(),實際上是 new Father(),
第三步,將 Son.prototype.constructor 也就是 Mid.prototype.constructor 指向 Son
看起來有點亂,看數字步驟,方便理解
function Father(name){ this.say = function(){ console.log(name) } } function Son(name){ Father.call(this, name) } Mid.prototype = Father.prototype; Son.prototype = new Mid(); Son.prototype.constructor = Son; console.log(Son.prototype.__proto__) // Father
class繼承
ES6 提供了關鍵字 class,定義類變得更便捷
共用方法
class Father{ // 構造方法 constructor(name){ this.name = name; } hello(){ console.log("hello", this.name) } } console.log(new Father("f").hello === new Father("f").hello) // true,共用方法
class繼承
class Father{ // 構造方法 constructor(name){ this.name = name; } hello(){ console.log("hello", this.name) } } class Son extends Father{ constructor(name){ super(name); } } console.log(Son.prototype.__proto__) // Father