【筆記】JavaScript高級篇——面向對象、原型、繼承

原文連接: http://www.hansmkiii.com/2018/07/06/javascript-node-1/

" 面向對象、原型、繼承 "javascript

一、面向對象

1.1 什麼是面向對象程序設計

爲了瞭解 面向對象面向過程 的區別,咱們先看案例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

  1. 提升代碼的可維護性
  2. 提升代碼的可擴展性

1.2 面向對象的方式操做DOM

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。

2.1 原型繼承

原型

定義:原型就是指構造函數的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值都會被忽略,依舊返回的是構造函數的實例對象

2.2 類式繼承

類式繼承經過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"}

2.3 組合繼承

經過前面對 原型繼承類式繼承 的瞭解,咱們發現 原型繼承 用於繼承靜態數據, 類式繼承 用於繼承動態參數,因此咱們有時候須要同時使用 原型繼承類式繼承,也就是 組合繼承

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();

2.4 extend方法

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();

以上四種繼承方式是開發過程當中最多見的幾種,具體應用場景視狀況而定,甚至能夠多個組合使用

相關文章
相關標籤/搜索