python封裝繼承多態_淺談JavaScript的面向對象和它的封裝、繼承、多態

寫在前面

既然是淺談,就不會從原理上深度分析,只是幫助我們更好地理解...

面向對象與面向過程

面向對象和面向過程是兩種不同的編程思想,剛開始接觸編程的時候,我們大都是從面向過程起步的,畢竟像我一樣,大家接觸的第一門計算機語言大概率都是C語言,C語言就是一門典型的面向過程的計算機語言。

面向過程主要是以動詞為主,解決問題的方式是按照順序一步一步調用不同的函數。

面向對象是以名詞為主,將問題抽象出具體的對象,而這個對象有自己的屬性和方法,在解決問題的時候,是將不同的對象組合在一起使用。

//面向過程裝大象

1.開(冰箱)

2.(大象)裝進(冰箱)

3.關(冰箱)

//面向對象裝大象

1. 冰箱.開門()

2. 冰箱.裝進(大象)

3. 冰箱.關門()

從這個例子可以看出,面向對象是以主謂為主,將主謂堪稱一個一個的對象,然后對象有自己的屬性和方法。

面向對象是以功能來劃分問題的,而不是步驟。功能上的統一保證了面向對象設計的可擴展性,解決了代碼重用性的問題。

這也是在漫長的程序設計的發展過程中得到的驗證結果,面向對象的編程思想較之于面向過程較好一點

封裝

面向對象有封裝、繼承和多態三大特性。

封裝:就是把事物封裝成類,隱藏事物的屬性和方法的實現細節,僅對外公開接口。

在ES5中,并沒有class的概念,但是由于js的函數級作用域(函數內部的變量函數外訪問不到)。所以我們可以模擬class。在es5中,類其實就是保存了一個函數的變量,這個函數有自己的屬性和方法。將屬性和方法組成一個類的過程就是封裝。

1.通過構造函數添加

JavaScript提供了一個構造函數(Constructor)模式,用來在創建對象時初始化對象。構造函數其實就是普通的函數,只不過有以下的特點

①首字母大寫(建議構造函數首字母大寫,即使用大駝峰命名,非構造函數首字母小寫)

②內部使用this

③使用new生成實例

通過構造函數添加屬性和方法實際上也就是通過this添加的屬性和方法。因為this總是指向當前對象的,所以通過this添加的屬性和方法只在當前對象上添加,是該對象自身擁有的。所以我們實例化一個新對象的時候,this指向的屬性和方法都會得到相應的創建,也就是會在內存中復制一份,這樣就造成了內存的浪費。

function Cat(name,color){

this.name = name;

this.color = color;

this.eat = (() => {

console.log("fish!")

})

}

//生成實例

var cat1 = new Cat("tom", "gray")

通過this定義的屬性和方法,我們實例化對象的時候斗湖重新復制一份

2.通過原型prototype封裝

在類上通過this的方式添加屬性和方法會導致內存浪費的現象,有什么辦法可以讓實例化的類所使用的屬性和方法 直接使用指針 指向同一個屬性和方法。

這就是原型的方法

JavaScript規定,每一個構造函數都有一個prototype屬性,指向另一個對象。這個對象的所有屬性和方法,都會被構造函數的實例繼承。

也就是說,對于那些不變的屬性和方法,我們可以直接將其添加在類的prototype對象上。

function Cat(name,color){

this.name = name;

this.color = color;

}

Cat.prototype.type = "英短";

Cat.prototype.eat = ( () => {

alert("fish!")

} )

//生成實例

var cat1 = new Cat('Tom', 'gray');

var cat2 = new Cat('Kobe', 'purple');

console.log(cat1.type); //英短

cat2.eat(); //fish!

這時所有實例的type屬性和eat()方法,其實都是同一個內存地址,指向prototype對象,因此就提高了運行效率。

但是這樣做也有弊端,因為實例化的對象的原型都是指向同一內存地址,改動其中一個對象的屬性可能會影響到其他的對象

es6中的類和封裝

es6聲明一個類

①構造器:構造器內創建自有屬性

②方法:聲明類實例具有的方法

class Cat {

//等價于Cat構造器

constructor(name) {

this.name = name;

}

//更加簡單的聲明類的內部函數

//等價于 Cat.prototype.eat

eat() {

console.log("fish!");

}

}

//生成實例

var cat1 = new Cat("tom");

cat1.eat(); //fish!

console.log(cat1 instanceof Cat); //true

console.log(cat1 instanceof Object); //true

console.log(typeof Cat); //function

console.log(typeof Cat.prototype.eat); //function

從上面class聲明的Cat為例:Cat類是一個具有構造函數行為的函數,其中內部方法eat實際上就是Cat.prototype.eat()

所以說es6的class封裝類,本質上是es5實現方式的語法糖

最主要的區別在于,class類的屬性是不可重新賦值和不可枚舉的,Cat.prototype就是一個只讀屬性

class和自定義類型的區別

(1)class的聲明不會提升,與let類似

(2)class的聲明自動運行于嚴格模式之下

(3)class聲明的方法不可枚舉

(4)class的內部方法沒有 constructor 屬性,無法new

(5)調用class的構造函數必須new

(6)class內部方法不能同名

class類的使用

class作為js中的一級公民,可以被當作值來直接使用

//1.類名作為參數傳入函數

function createObj (ClassName) {

return new ClassName()

}

//2.立即執行,實現單例模式

let cat1 = new class{

constructor (name) {

this.name = name

}

eat() {

console.log("fish!")

}

}("tom”)

cat1.eat() //fish!

繼承

繼承就是子類可以使用父類的所有功能,并且對這些功能進行擴展。繼承的過程,就是從一般到特殊的過程。

1.類式繼承

所謂的類式繼承就是使用的原型的方式,將方法添加在父類的原型上,然后子類的原型是父類的一個實例化對象。

//聲明父類

var SuperClass = function(){

let id = 1;

this.name = ['java'];

this.superValue = function() {

console.log('this is superValue!')

}

}

//為父類添加共有方法

SuperClass.prototype.getSuperValue = function () {

return this.superValue();

};

//聲明子類

var SubClass = function() {

this.subValue = (() => {

console.log('this is subValue!')

})

}

//繼承父類

SubClass.prototype = new SuperClass();

//為子類添加共有方法

SubClass.prototype.getSubValue = function() {

return this.subValue()

}

//生成實例

var sub1 = new SubClass();

var sub2 = new SubClass();

sub1.getSuperValue(); //this is superValue!

sub1.getSubValue(); //this is subValue!

console.log(sub1.id); //undefined

console.log(sub1.name); //["java"]

sub1.name.push("php");

console.log(sub1.name); //["java", "php"]

console.log(sub2.name); //["java", "php"]

其中最核心的是SubClass.prototype = new SuperClass();

類的原型對象prototype對象的作用就是為類的原型添加共有的方法的,但是類不能直接訪問這些方法,只有將類實例化之后,新創建的對象復制了父類構造函數的屬性和方法,并將原型 proto 指向了父類的原型對象。這樣子類就可以訪問父類的屬性和方法,同時,父類中定義的屬性和方法不會被子類繼承。

but使用類繼承的方法,如果父類的構造函數中有引用數據類型,就會在子類中被所有實例共用,因此一個子類的實例如果更改了這個引用數據類型,就會影響到其他子類的實例。

構造函數繼承

為了克服類繼承的缺點,才有了構造函數繼承,構造函數繼承的核心思想就是SuperClass.call(this, id),直接改變this的指向,使通過this創建的屬性和方法在子類中復制一份,因為是單獨復制的,所以各個實例化的子類互不影響。but會造成內存浪費的問題

//構造函數繼承

//聲明父類

var SuperClass = function(id){

var name = 'java'

this.languages = ['java', 'php', 'ruby'];

this.id = id

}

//聲明子類

var SubClass = function(id){

SuperClass.call(this, id)

}

//生成實例

var sub1 = new SubClass(1);

var sub2 = new SubClass(2);

console.log(sub2.id); // 2

console.log(sub1.name); //undefined

sub1.languages.push("python");

console.log(sub1.languages); // ['java', 'php', 'ruby', 'python']

console.log(sub2.languages); // ['java', 'php', 'ruby']

組合式繼承

組合式繼承是汲取了兩者的優點,既避免了內存浪費,又使得每個實例化的子類互不影響。

//組合式繼承

//聲明父類

var SuperClass = function(name){

this.languages = ['java', 'php', 'ruby'];

this.name = name;

}

//聲明父類原型方法

SuperClass.prototype.showLangs = function () {

console.log(this.languages);

}

//聲明子類

var SubClass = function(name){

SuperClass.call(this, name)

}

//子類繼承父類(鏈式繼承)

SubClass.prototype = new SuperClass();

//生成實例

var sub1 = new SubClass('python');

var sub2 = new SubClass('go');

sub2.showLangs(); //['java', 'php', 'ruby']

sub1.languages.push(sub1.name);

console.log(sub1.languages);//["java", "php", "ruby", "python"]

console.log(sub2.languages);//['java', 'php', 'ruby']

but警告:組合式繼承方法固然好,但是會導致一個問題,父類的構造函數會被創建兩次(call()的時候一遍,new的時候又一遍)

寄生組合繼承

組合式繼承的缺點的關鍵是 父類的構造函數在類繼承和構造函數繼承的組合形式被創建了兩邊,但是在類繼承中我們并不需要創建父類的構造函數,我們只要子類繼承父類的原型即可。

所以我們先給父類的原型創建一個副本,然后修改子類的 constructor 屬性,最后在設置子類的原型就可以了

//原型式繼承

//原型式繼承其實就是類式繼承的封裝,實現的功能返回一個實例,該實例的原型繼承了傳入的o對象

function inheritObject(o) {

//聲明一個過渡函數

function F() {}

//過渡對象的原型鏈繼承父對象

F.prototype = o;

//返回一個過渡對象的實例,該實例的原型繼承了父對象

return new F();

}

//寄生式繼承

//寄生式繼承就是對原型繼承的第二次封裝,使得子類的原型等于父類的原型。并且在第二次封裝的過程中對繼承的對象進行了擴展

function inheritPrototype(subClass, superClass){

//復制一份父類的原型保存在變量中,使得p的原型等于父類的原型

var p = inheritObject(superClass.prototype);

//修正因為重寫子類原型導致子類constructor屬性被修改

p.constructor = subClass;

//設置子類的原型

subClass.prototype = p;

}

//定義父類

var SuperClass = function(name) {

this.name = name;

this.languages = ["java", "php", "python"]

}

//定義父類原型方法

SuperClass.prototype.showLangs = function() {

console.log(this.languages);

}

//定義子類

var SubClass = function(name) {

SuperClass.call(this,name)

}

inheritPrototype(SubClass, SuperClass);

var sub1 = new SubClass('go');

es6中的繼承

class SuperClass {

constructor(name) {

this.name = name

this.languages = ['java', 'php', 'go'];

}

showLangs() {

console.log(this.languages);

}

}

class SubClass extends SuperClass {

constructor(name) {

super(name)

}

//重寫父類中的方法

showLangs() {

this.languages.push(this.name)

console.log(this.languages);

}

}

//生成實例

var sub = new SubClass('韓二虎');

console.log(sub.name); //韓二虎

sub.showLangs(); //["java", "php", "go", "韓二虎"]

多態

多態實際上是不同對象作用與同一操作產生不同的效果。多態的思想實際上是把 “想做什么” 和 “誰去做” 分開。

多態的好處在于,你不必再向對象詢問“你是什么類型”后根據得到的答案再去調用對象的某個行為。你盡管去調用這個行為就是了,其他的一切可以由多態來負責。規范來說,多態最根本的作用就是通過吧過程化的條件語句轉化為對象的多態性,從而消除這些條件分支語句。

由于JavaScript中提到的關于多態的詳細介紹并不多,這里簡單的通過一個例子來介紹就好

//非多態

var hobby = function(animal){

if(animal == 'cat'){

cat.eat()

}else if(animal == 'dog'){

dog.eat()

}

}

var cat = {

eat: function() {

alert("fish!")

}

}

var dog = {

eat: function() {

alert("meat!")

}

}

console.log(123);

hobby('cat'); //fish!

hobby('dog'); //meat!

從上面的例子能看到,雖然 hobby 函數目前保持了一定的彈性,但這種彈性很脆弱的,一旦需要替換或者增加成其他的animal,必須改動hobby函數,繼續往里面堆砌條件分支語句。我們把程序中相同的部分抽象出來,那就是吃某個東西。然后再重新編程。

//多態

var hobby = function(animal){

if(animal.eat instanceof Function){

animal.eat();

}

}

var cat = {

eat: function() {

alert("fish!")

}

}

var dog = {

eat: function() {

alert("meat!")

}

}

現在來看這段代碼中的多態性。當我們向兩種 animal 發出 eat 的消息時,會分別調用他們的 eat 方法,就會產生不同的執行結果。對象的多態性提示我們,“做什么” 和 “怎么去做”是可以分開的,這樣代碼的彈性就增強了很多。即使以后增加了其他的animal,hobby函數仍舊不會做任何改變。

//多態

var hobby = function(animal){

if(animal.eat instanceof Function){

animal.eat();

}

}

var cat = {

eat: function() {

alert("fish!")

}

}

var dog = {

eat: function() {

alert("meat!")

}

}

var aoteman = {

eat: function(){

alert("lil-monster!")

}

}

hobby(cat); //fish!

hobby(dog); //meat!

hobby(aoteman); //lil-monster!

快樂面向對象😁

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/454193.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454193.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454193.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

將萬億以下的阿拉伯數字轉為中文金額

package test.practice.month3; public class Test005 { //可以不用swich case將123456789轉為一二三四五六七八九 //直接用char[] chars {一,二,三,四,五,六,七,八,九}; public static void main(String[] args) { System.out.println(getCMoney(102030405067L)); } private …

8.2 命令歷史

2019獨角獸企業重金招聘Python工程師標準>>> 命令歷史 history //查看之前的命令.bash_history //存放之前敲過的命令,在 /root/ 目錄下最大1000條 //默認參數值是1000條變量HISTSIZE/etc/profile中修改 //在其中可編輯HISTSIZE參數HISTTIMEFORMAT"…

使用GCC生成無格式二進制文件(plain binary files)

使用C語言生成一個二進制文件 使用自己喜歡的文本編輯器寫一個test.c: int main() { } 再使用如下命令編譯: gcc –c test.c ld –o test –Ttext 0x0 –e main test.o objcopy –R .note –R .comment –S –O binary test test.bin 最后生成的二進…

TensorFlow 實例一:線性回歸模型

代碼 # -- encoding:utf-8 -- """ Create by ibf on 2018/5/6 """import numpy as np import tensorflow as tf# 1. 構造一個數據 np.random.seed(28) N 100 x np.linspace(0, 6, N) np.random.normal(loc0.0, scale2, sizeN) y 14 * x - …

python后端數據發送到前端_Python Django 前后端數據交互 之 后端向前端發送數據...

Django 從后臺往前臺傳遞數據時有多種方法可以實現。最簡單的后臺是這樣的:from django.shortcuts import renderdefmain_page(request):return render(request, ‘index.html‘)這個就是返回index.html的內容,但是如果要帶一些數據一起傳給前臺的話&…

Dapper的基本使用

Dapper是.NET下一個micro的ORM,它和Entity Framework或Nhibnate不同,屬于輕量級的,并且是半自動的。也就是說實體類都要自己寫。它沒有復雜的配置文件,一個單文件就可以了。給出官方地址。 http://code.google.com/p/dapper-dot-n…

函數名作為參數傳遞

假如不知道signal的函數原型,考慮child_handler函數的參數從哪里來? void child_handler(int sig) { if (sig SIGINT) kill(pid_parent, SIGUSR1); } int main(void) { ...... signal(SIGINT, child_handler); ...... } 1、…

易語言神經網絡驗證碼識別_遞歸神經網絡 GRU+CTC+CNN 教會驗證碼識別

利用 NLP 技術做簡單數據可視化分析Chat 簡介:用遞歸神經網絡采用端到端識別圖片文字,遞歸神經網絡大家最早用 RNN ,缺陷造成梯度消失問題;然后采用了 LSTM,解決 RNN 問題,并且大大提高準確率;現…

GCC 生成的符號表調試信息剖析

GCC把C語言源文件('.c')編譯成匯編語言文件('.s'),匯編器把匯編語言文件翻譯成目標文件('.o'),最后由鏈接器鏈…

《操作系統》OS學習(一):OS相關

清華大學操作系統OS(向勇、陳渝)視頻地址:http://www.xuetangx.com/courses/course-v1:TsinghuaX30240243Xsp/about 在ucore實驗中,一些基本的常用工具如下: 命令行shell: bash shell -- 有對文件和目錄操作的各種命令…

Android4.0藍牙使能的詳細解析

毫無疑問,bluetooth的打開是在Settings中進行的操作。因此,冤有頭,債有主,我們來到了Settings.java中,果然發現了相關的代碼如下: mBluetoothEnabler new BluetoothEnabler(context, new Switch(context));…

第一次沖刺

本人小組分工角色:產品負責人 本組沖刺訂單介紹:經過小組的成員討論,我們大概確立了一個沖刺的訂單是完成一個簡易的長沙學院網站項目。 最后完成情況概述:經過大約兩周的努力,我們組的編程人員運用python構建了一個簡…

移動端導出excel_連載系列【4】Excel開發移動端quot;APPquot;

前三篇文章介紹了百度地圖生成器、源代碼編輯器、GPS經緯度批量轉換工具、源代碼編輯器中添加自定義功能按鈕和地圖控件。這些寫好的Java Script代碼雖然可以實現所有期望的結果,但畢竟不是一個HTML文件,不便于傳播和使用,更無法變成一個類似…

《操作系統》OS學習(二):啟動、中斷、異常

Bootloader:加載OS。操作系統一開始是放在DISK(硬盤)中,并不是放在內存中。 BIOS:基本I/O處理系統。存放在ROMRead-Only Memory)只讀存儲中 BIOS(Basic Input/Output System)基本輸入輸出系統。…

[GCC for C]編譯選項---IDE掩蓋下的天空

編譯選項 ---------IDE掩蓋下的天空 /*************************************** * gcc for c language ***************************************/ Single Source to Executable $ gcc helloworld.c [-o howdy] 默認生成的名字a.exe ______________________________________ …

2016級算法第二次上機-F.ModricWang's Number Theory II

891 ModricWangs Number Theory II 思路 使得序列的最大公約數不為1,就是大于等于2,就是找到一個大于等于2的數,它能夠整除序列中的所有數。 考慮使得一個數d整除數組中所有數的代價: 如果一個數不能被b整除,那么可以花…

常用css屬性集(持續更新…)

禁止換行,超出部分顯示…:a. 代碼:.hide_word{ max-width: 100px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } b. 效果: 本文轉自 bilinyee博客,原文鏈接: http://blog.51cto.co…

parallels網絡初始化失敗_33 個神經網絡「煉丹」技巧

自然語言處理Andrej Karpathy 是深度學習計算機視覺領域、與領域的研究員。博士期間師從李飛飛。在讀博期間,兩次在谷歌實習,研究在 Youtube 視頻上的大規模特征學習,2015 年在 DeepMind 實習,研究深度強化學習。畢業后&#xff0…

《操作系統》OS學習(三):系統調用

例子 首先看一個標準C庫的例子:當我們程序中使用了C庫中的printf()函數,實際在底層是在內核態中調用了write()函數。圖中右側則是將程序代碼與C庫都算到應用程序中,內核提供了一個系統調用接口。 從這個例子我們可以得到以下幾點&#xff1a…

cygwin/gcc與MinGW

cygwin/gcc和MinGW都是gcc在windows下的編譯環境,但是它們有什么區別?在實際工作中如何選擇這兩種編譯器呢?cygwin/gcc完全可以和在linux下的gcc劃等號,這個從boost庫的劃分中就可以看出來端倪,cygwin下的gcc和linux下的gcc使用的是相同的T…