原文連接: http://www.hansmkiii.com/2018/07/06/javascript-node-1/
" 面向對象、原型、繼承 "javascript
爲了瞭解 面向對象 與 面向過程 的區別,咱們先看案例1。html
案例1:一輛汽車以60km/h,在一條長度1200km的公路上行駛,求其行駛徹底程須要的時間。java
/* 面向過程 */ var time = 1200 / 60; // 計算汽車行駛時間 console.log(time); // 打印結果
/* 面向對象 */ // 建立公路對象 var road = { long: 1200 // 公路長度 }; // 建立小汽車對象 var car = { speed: 60, // 汽車速度 road: road, // 汽車在公路上行駛,與公路對象關聯(road已聲明) run: function(){ // 汽車行駛的方法,返回行駛速度 return this.road.long / this.speed; } } var r = car.run(); // 執行汽車行駛的方法,返回行駛速度 console.log(r); // 打印結果(30)
雖然看上去面向對象比面向過程要複雜了不少,可是若是考慮到之後可能會改變某個變量(公路長度或汽車行駛速度),面向過程須要修改運算過程當中的值,而面向對象只須要在執行運算方法以前經過car.speed = 40
修改 car 對象的 speed 屬性便可,能夠有效避免修改錯誤和修改多處。node
當程序複雜程度到達必定的量級,面向對象能夠提升程序的可維護性。git
若是後面咱們的需求改變了,依舊須要汽車對象,可是要返回汽車的顏色。
若是是面向過程開發,咱們須要刪除掉面向過程的全部代碼,從新寫代碼返回汽車的顏色。而面向對象則不須要刪除原來定義的對象,只須要給 car 對象增長一個 color 屬性,擴展 car 對象。github
因此,面向對象程序設計的優點有:web
HTML數組
<div>111</div> <div>222</div> <div>333</div> <div>444</div>
JSapp
// 給div添加背景顏色 // 面向過程 var divs = document.getElementsByTagName('div'); for (var i=0; i<divs.length; i++) { divs[i].style.backgroundColor = 'red'; } // 面向對象 var domOpr = { getDiv: function(){ var divs = document.getElementsByTagName('div'); return divs; }, setStyle: function(){ var divs = this.getDiv() for (var i=0; i<divs.length; i++) { divs[i].style.backgroundColor = 'red'; } } } domOpr.setStyle();
若是多處須要相同的功能,面向對象的方法能夠提升代碼的複用性。dom
傳統的面嚮對象語言,繼承是 類 與 類 之間的關係。而JS中,因爲沒有 類 的概念。因此是 對象 與 對象 之間的關係。
定義:在JS中,繼承就是指使一個對象有權去訪問另外一個對象的能力。
好比:對象A能繼承對象B的成員(屬性和方法),那麼,對象A就繼承於對象B。
定義:原型就是指構造函數的prototype屬性所引用的對象。
目的:解決同類對象數據共享問題。
示例:
// 建立構造函數 Person function Person(name, age){ this.name = name; this.age = age; this.say = function(){ console.log('hello!') } } // 建立 Person 的實例對象 var p1 = new Person('張三', 19); var p2 = new Person('李四', 20); // Person.prototype 就是對象 p1 的原型 // p1.__proto__ 也是 p1 的原型 console.log(Person.prototype === p1.__proto__); // true
此時,p1和p2都有繼承了構造函數 Person 的 say 屬性,在控制檯打印查看:
p1.say(); // hello! p2.say(); // hello!
這是 構造函數繼承,那麼 p1 的 say 和 p2 的 say 是否是同一個方法呢?
// 打印查看 console.log(p1.say === p2.say); // false
查看結果發現兩個 say 方法並非同一個,因此在建立實例對象的時候開闢了兩塊內存分別存放了 p1.say 和 p2.say,這就致使了內存的浪費,因此咱們須要經過原型共享數據來解決這個問題。
// 建立構造函數 Person function Person(name, age){ this.name = name; this.age = age; } // 建立 Person 的實例對象 var p1 = new Person('張三', 19); var p2 = new Person('李四', 20); // Person.prototype 就是對象 p1 的原型 // p1.__proto__ 也是 p1 的原型 // 在原型上建立一個 say 屬性 Person.prototype.say = function(){ console.log('hello!'); } // 運行 p1.say 和 p2.say,並對比是否爲同一個方法。 p1.say(); // hello! p2.say(); // hello! console.log(p1.say === p2.say); // true
如此便經過原型繼承實現了數據共享。
構造函數中能夠顯式的指定return語句,可是若是返回的數據類型爲簡單數據類型,或者null undefined值都會被忽略,依舊返回的是構造函數的實例對象
類式繼承經過call(this, ... , ...)
或apply(this, arguments)
方法改變this
指向實現繼承。
// 建立 Person 構造函數 function Person(name, age) { this.name = name; this.age = age; } // 建立 Student 構造函數 function Student(name, age, id){ this.name = name; this.age = age; this.id = id; }
Student
對象是 Person
對象的一個分支,能夠繼承Person
的屬性。
因此Student
屬性能夠這樣寫:
function Student(name, age, id){ Person.call(this, name, age); this.id = id; } var s = new Student('張三','20','123456789'); console.log(s); // {name: "張三", age: "20", id: "123456789"}
或者
function Student(name, age, id){ Person.apply(this, arguments); // arguments 是一個數組 也能夠寫成 [name, age] this.id = id; } var s = new Student('張三','20','123456789'); console.log(s); // {name: "張三", age: "20", id: "123456789"}
經過前面對 原型繼承 和 類式繼承 的瞭解,咱們發現 原型繼承 用於繼承靜態數據, 類式繼承 用於繼承動態參數,因此咱們有時候須要同時使用 原型繼承 和 類式繼承,也就是 組合繼承。
function Person(name, age) { this.name = name; this.age = age; } // 類式繼承 function Student(name, age, id){ Person.call(this, name, age); this.id = id; } // 原型繼承 Student.prototype.class = function(){ console.log('English'); } var s = new Student('張三', 20, '123456'); s.class();
var a = { name: 'g' }; var b = { print: function() { console.log('Hello,JS!') } } // 將 parent 對象上的成員賦值給 child function extend(child, parent) { for (var pro in parent) { // child[pro] = parent[pro]; // 缺點:會將parent對象的原型上的成員一同複製過來,因此須要先判斷屬性是否爲parent私有的 if (parent.hasOwnProperty(pro)){ child[pro] = parent[pro]; } } } extend(a,b); a.print();
以上四種繼承方式是開發過程當中最多見的幾種,具體應用場景視狀況而定,甚至能夠多個組合使用