JavaScript 面向對象編程

来源:http://www.cnblogs.com/zkh101/archive/2017/09/28/7606104.html
-Advertisement-
Play Games

面向對象編程就是將你的需求抽象成一個對象,針對這個對象分析其特征(屬性)和動作(方法),這個對象稱為“類”。JavaScript 的核心是支持面向對象的,同時它也提供了強大靈活的 OOP 語言能力,遺憾的是對於JavaScript這種解釋性的弱類型語言,沒有強類型語言中那種通過class等關鍵字實現... ...


前言

面向對象編程就是將你的需求抽象成一個對象,針對這個對象分析其特征(屬性)和動作(方法),這個對象稱為“類”。JavaScript 的核心是支持面向對象的,同時它也提供了強大靈活的 OOP 語言能力,遺憾的是對於JavaScript這種解釋性的弱類型語言,沒有強類型語言中那種通過class等關鍵字實現類的方式,但JavaScript可以通過一些特性模仿實現面向對象編程。

面向對象有三個基本特征:封裝繼承多態

封裝

封裝,就是把客觀事物封裝成抽象的類,類中包含了事物的屬性和方法,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。
JavaScript創建一個類很容易,通過聲明一個函數保存在一個變數里來實現,這個類的類名通常會採用首字母大寫的形式來表示,然後在這個函數(類)的內部使用this關鍵字來定義類的屬性和方法。例如:

var Person = function(name,sex,age){
    this.name = name;
    this.sex = sex;
    this.age = age;
}

也可以在類的原型上添加屬性和方法。例如:

Person.prototype.say = function(){
    // Say something
}

或者

Person.prototype = {
    say: function(){}
}

這樣就完成了Person類的封裝,當我們要使用這個類時,需要通過new關鍵字來實例化(創建)一個新的對象,通過.操作符訪問對象的屬性和方法。例如:

var person = new Person('Scott','male',20);
console.log(person.name);  // Scott

通過this添加的屬性和方法是在當前對象上添加的,而JavaScript是一種基於原型prototype的語言,每創建一種對象時,都有一個原型prototype用於指向其繼承的屬性和方法,通過prototype繼承的屬性和方法不是屬於對象本身的,在使用這些方法時,會通過原型鏈進行查找。當創建一個對象時,會創建this指向的屬性和方法,而通過prototype繼承的屬性或方法是該類的每個對象所共有的,不會再次創建。

當創建一個函數或者對象時都會為其創建一個prototype對象,原對象中的__proto__屬性指向該原型對象,prototype對象中會有一個constructor屬性指向擁有整個原型對象的函數或者類。

通過new關鍵字創建對象時實際上是對新對象中this的不斷賦值,並將prototype指向類的原型對象,而在類外通過.操作符定義的屬性和方法是不會添加到新建對象上的,通過對象進行訪問的結果是undefined。例如:

Person.isChinese = true;
Person.eat = function(){}

var person = new Person("Alex","female",19);
console.log(person.name);    // Alex
console.log(person.isChinese);    // undefined
console.log(person.eat());    // undefined

如果你忽略了new關鍵字直接調用類,如:var person = Person("Alex","female",19);,此時會直接調用Person這個函數,如果這個函數是在全局作用域里執行的,則此時類中的this指向的當前對象就是全局變數,在頁面中全局變數就是window,所以通過this添加的屬性或方法會被添加到window中,並且最終的person對象會是undefined

要解決這個問題可以採用“安全模式”,例如:

var Person = function(name,sex,age){
    if(this instanceof Person){
        this.name = name;
        this.sex = sex;
        this.age = age;
    }else{    // 未使用 new
        return new Person(name,sex,age);
    }
}

var person = Person("Scott","male",20);

這樣就不用當心創建對象時忘記使用new關鍵字了。

繼承

繼承可以使用現有類的所有功能,併在無需重新編寫原來的類的情況下對這些功能進行擴展。繼承所涉及的對象不止一個,JavaScript並沒有提供繼承這一現有的機制,也正因為JavaScript少了這些顯性的限制,使其更具有靈活性。在JavaScript中可以使用類式繼承構造函數繼承組合繼承來達到繼承的效果。

類式繼承

// 聲明父類
function Parent(){
    this.parentValue = true;
}
// 為父類添加共有方法
Parent.prototype.getParentValue = function(){
    return this.parentValue;
}

// 聲明子類
function Child(){
    this.childValue = false;
}
// 繼承父類
Child.prototype = new Parent();
// 為子類添加共有方法
Child.prototype.getChildValue = function(){
    return this.childValue;
}

類的原型對象的作用是為類的原型添加共有屬性和方法,但類必須通過原型prototype來訪問這些屬性和方法。當實例化一個父類時,新建對象複製了父類構造函數內的屬性和方法,並且將原型__proto__指向了父類的原型對象,這樣就擁有了父類原型對象上的屬性和方法,新建對象可以直接訪問父類原型對象的屬性和方法,接著將這個新建的對象賦值給子類的原型,那麼子類的原型就可以訪問父類的原型屬性和方法。將這個對象賦值給子類的原型,那麼這個子類就可以訪問父類原型上的屬性和方法,並且可以訪問從父類構造函數中複製的屬性和方法。我們可以來測試一下:

var child = new Child();
console.log(child.getParentValue());      // true
console.log(child.getChildValue());       // false
console.log(child instanceof Parent);     // true
console.log(child instanceof Child);      // true
console.log(Child instanceof Parent);     // false

但這種繼承方式有2個缺點:

  • 由於子類是通過其原型prototype對父類實例化,如果父類中的共有屬性是引用類型,會被所有實例所共用,一個子類的實例修改了該屬性會直接影響到所有實例。例如:

    function Parent(){
    this.values = ['A','B','C'];
    }
    function Child(){}
    Child.prototype = new Parent();
    var child1 = new Child();
    var child2 = new Child();
    console.log(child2.values);    // ["A","B","C"]
    child1.values.push('D');
    console.log(child2.values);    // ["A","B","C","D"]
  • 創建父類實例時,是無法向父類傳遞參數的,也就是無法對父類構造函數內的屬性進行初始化。例如這種錯誤的繼承方式:

    function Parent(name){
    this.name = name;
    }
    function Child(){}
    Child.prototype = new Parent('name');    // 錯誤

構造函數繼承

// 聲明父類
function Parent(name){
    this.name = name;
    this.values = ['A','B','C'];
}
Parent.prototype.showName = function(){
    console.log(this.name);
}
// 聲明子類
function Child(name){
    Parent.call(this,name);
}
var child1 = new Child('one');
var child2 = new Child('two');
child1.values.push('D');
console.log(child1.name);   // one
console.log(child1.values); // ["A","B","C","D"]
console.log(child2.name);   // two
console.log(child2.values); // ["A","B","C"]
child1.showName();          // TypeError

語句Parent.call(this,name);是構造函數繼承的精華,call方法可以更改函數的作用環境,在子類中執行該方法相當於將子類的變數在父類中執行一遍,此時父類方法中的this屬性指的是子類中的this,由於父類中是給this綁定屬性的,所以子類也就繼承了父類的屬性和方法。構造函數繼承並沒有涉及原型prototype,所以父類的原型方法不會被子類繼承,子類的每個實例會單獨擁有一份父類的屬性方法而不能共用,如果想被子類繼承就必須放在構造函數中,要實現這樣的效果可以採用組合繼承的方式。

組合繼承

類式繼承是通過子類的原型prototype對父類實例化來實現的,構造函數繼承是通過在子類的構造函數作用環境中執行一次父類的構造函數來實現的,而組合繼承則同時做到這兩點。

// 聲明父類
function Parent(name){
    this.name = name;
    this.values = ['A','B','C'];
}
Parent.prototype.getName = function(){
    console.log(this.name);
}
// 聲明子類
function Child(name,id){
    Parent.call(this, name);
    this.id = id;
}
Child.prototype = new Parent();
Child.prototype.getId = function(){
    console.log(this.id);
}

var child1 = new Child('child1', 1);
child1.values.push('D');
console.log(child1.values); // ["A", "B", "C", "D"]
child1.getName();           // child1
child1.getId();             // 1

var child2 = new Child('child2', 2);
console.log(child2.values);    // ["A", "B", "C"]
child2.getName();              // child2
child2.getId();                // 2

子類的實例中更改父類繼承下來的引用類型屬性,不會影響到其它實例,並且子類實例化過程中又能將參數傳遞到父類的構造函數中。

多態

多態就是同一個方法多種調用方式,JavaScript可以通過對傳入的參數列表arguments進行判斷來實現多種調用方式。例如:

function Add(){
    // 無參數
    function zero(){
        return 0;
    }
    // 一個參數
    function one(num){
        return num;
    }
    // 兩個參數
    function two(num1, num2){
        return num1 + num2;
    }

    this.add = function(){
        // 獲取參數列表及參數個數
        var arg = arguments,
            len = arg.length;
        switch(len){
            case 0:
                return zero();
            case 1:
                return one(arg[0]);
            case 2:
                return two(arg[0], arg[1]);
        }
    }
}

var A = new Add();
console.log(A.add());       // 0
console.log(A.add(1));      // 1
console.log(A.add(1,2));    // 3

當調用add進行運算時,會根據參數列表的不同做相應的運算,這就是JavaScript的多態實現方式。

總結

面向對象設計方法的應用解決了傳統結構化開發方法中客觀世界描述工具與軟體結構的不一致性問題,縮短了開發周期,解決了從分析和設計到軟體模塊結構之間多次轉換映射的繁雜過程,是一種高效率的軟體開發方式,特別是在多人協作開發的情況下,可以提高代碼的可復用性和維護性,使開發更有效率。

本文為作者kMacro原創,轉載請註明來源:http://www.jianshu.com/p/c2083cf275ec


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 專業定製仿百度文庫網站系統,仿豆丁網網站,仿道客巴巴網站,文庫網站系統源碼,文庫網站建設開發,支持電腦版+手機版+微信版+小程式版+APP版,由10年的技術團隊專業定製,需要的朋友可以聯繫我們。 網站採用:PHP+MySQL+thinkPHP框架(可定製) <ignore_js_op> <ignor ...
  • 所謂素數是指除了1和它本身以外,不能被任何整數整除的數,例如17就是素數,因為它不能被2~16的任一整數整除。因此判斷一個整數m是否是素數,只需把m被2~m-1之間的每一個整數去除,如果都不能被整除,那麼m就是一個素數另外判斷方法還可以簡化。m不必唄2~m-1之間的每一個整數去除,只需被2~√m之間 ...
  • 修改表 修改表 語法: Alter table <舊表名> rename [ TO] <新表名>; 例子:Alter table `demo01` rename `demo02`; 添加欄位 語法: Alter Table 表名 ADD 欄位名 數據類型 [屬性]; 例子:Alter Table ` ...
  • 第一部分說明 第一部分大概有20來章,主要講的是一些開發常識、開發前中後期準備內容、開發環境與伺服器部署環境安裝設置、python基礎框架結構與功能等內容,代碼會比較簡單。 本系列會以故事的方式,向大家描述一位有點開發基礎的程式猿,怎麼將一個小系統,根據需求的增加,以及對技術的追求,慢慢的通過重構, ...
  • 項目結構 在目錄中運行 ant war 即可. ...
  • 本來應一個可愛帥氣的編輯之邀,要寫一本書《靜兒的互聯網服務治理私房菜》。想選服務治理的題材,想急著簽協議就寫了一個很匆忙的目錄和例章。寫書本是計划了很久的一件事情。現在反而有些猶豫了。我是不是應該把腳步放慢一些,再穩一些。我是不是應該自己先寫了一部分,再考慮簽約出版的事情。要做的事情太多了,比如:家 ...
  • 原文地址: http://michael-j.net/2017/09/24/%E7%BC%96%E7%A8%8B%E5%BF%83%E6%99%BA-%E4%B8%80-%E2%80%94%E2%80%94%E4%BB%A3%E7%A0%81%E6%9E%B6%E6%9E%84%E4%B8%8E%E ...
  • 1.什麼是UML狀態圖 UML狀態圖是描述類對象可能經歷的所有狀態的模型圖,描述了對象基於事件反應的動態行為。顯示實體根據當時的狀態做出具體的動作。 2.UML類圖的作用。 UML類圖的作用是研究類對象,角色,子系統或者其他組件之間的實時行為。 3.UML狀態圖的繪製 3.1 狀態圖的模型組成元素 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...