何混淆JavaScript代碼?
JavaScript混淆是一種技術(shù),用于使代碼難以閱讀和理解,同時保持其功能性。這通常是為了保護代碼的安全性、隱藏實現(xiàn)細節(jié),或者僅僅是為了阻止隨意的逆向工程嘗試。然而,需要注意的是,混淆并不能提供絕對的安全性,也不應(yīng)作為主要的防御手段。
為什么混淆JavaScript?
1. 保護知識產(chǎn)權(quán):公司或開發(fā)者希望保護其獨特的算法和邏輯,防止被競爭對手竊取或復(fù)制。
2. 隱藏實現(xiàn)細節(jié):有時候,開發(fā)者可能希望隱藏某些特定的實現(xiàn)細節(jié),以降低被攻擊者利用的風險,或者避免暴露核心的業(yè)務(wù)邏輯。
3. 增加攻擊難度:混淆后的代碼對于非專業(yè)的攻擊者來說,會大大增加其理解和分析的難度,從而在一定程度上起到威懾作用。
JavaScript混淆技術(shù)
1. 變量和函數(shù)名混淆:將變量和函數(shù)名更改為無意義或隨機的名稱,使代碼難以閱讀和理解。這可以通過手動修改或使用專門的混淆工具來實現(xiàn)。
2. 字符串加密:將代碼中的字符串進行加密或編碼,如使用Base64、Hex等,使得攻擊者難以直接讀取到原始信息。
3. 控制流混淆:通過改變代碼的執(zhí)行流程,增加額外的條件判斷、循環(huán)和跳轉(zhuǎn)語句,使得代碼的執(zhí)行邏輯變得復(fù)雜且難以追蹤。
4. 代碼拆分和動態(tài)執(zhí)行:將代碼拆分成多個部分,并在運行時動態(tài)地拼接和執(zhí)行這些部分。這樣可以使攻擊者難以通過靜態(tài)分析來獲取完整的代碼邏輯。
混淆的最佳實踐
1. 使用成熟的混淆工具:如JShaman、JS-Obfuscator等,這些工具已經(jīng)經(jīng)過廣泛的測試和應(yīng)用,可以提供可靠的混淆效果。
2. 徹底測試混淆后的代碼:在混淆代碼后,進行全面的測試以確保其仍然能夠正常工作,沒有引入新的錯誤或問題。
3. 不要過度依賴混淆:混淆只能作為一種額外的安全措施,而不應(yīng)成為主要的安全手段。開發(fā)者仍需要關(guān)注其他的安全最佳實踐,如輸入驗證、訪問控制等。
4. 意識到混淆的局限性:混淆后的代碼可能會增加調(diào)試和維護的難度。因此,在決定對代碼進行混淆之前,需要權(quán)衡其帶來的好處和潛在的負面影響。
結(jié)論
JavaScript混淆是一種有效的技術(shù),可以幫助保護代碼的安全性、隱藏實現(xiàn)細節(jié),并增加攻擊者分析的難度。
程控制,是任何一門編程語言都有的一個語法。
如果你學習C語言,或者學過C#、Java等,應(yīng)該對“流程控制”很熟悉。
所謂的流程控制,指的是控制程序按照怎樣的順序執(zhí)行。
在JavaScript中,共有三種流程控制方式(其實任何語言也都只有這三種啦)。
順序結(jié)構(gòu)
選擇結(jié)構(gòu)
循環(huán)結(jié)構(gòu)
順序結(jié)構(gòu)
在JavaScript中,順序結(jié)構(gòu)是最基本的結(jié)構(gòu)。所謂順序結(jié)構(gòu),
就是代碼按照從上到下、從左到右的“順序”執(zhí)行,
舉例:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script>
var str1="綠葉學習網(wǎng)";
var str2="JavaScript";
var str3=str1 + str2;
document.write(str3);
</script>
</head>
<body>
</body>
</html>
面向?qū)ο蟪绦蛟O(shè)計有三要素:封裝、繼承(或組合)、多態(tài),前兩者較好理解,多態(tài)總讓人困惑,不知道具體有什么作用,更不知道為什么要用多態(tài)。今天就來詳細分析下什么是多態(tài),以及多態(tài)有哪些好處,為什么要用多態(tài)?
多態(tài)是指同一行為作用于不同對象時,可以表現(xiàn)出多種不同的形式和結(jié)果來。例如,子類繼承父類并覆蓋其方法后,用父類引用指向子類對象并調(diào)用該方法時,實際執(zhí)行的是子類的方法。
這種根據(jù)對象實際類型而非聲明類型來確定執(zhí)行方法的行為,就是多態(tài)性的體現(xiàn)。多態(tài)主要通過繼承和接口實現(xiàn),允許同一接口有多種不同的實現(xiàn)方式。
重載指在同一個類中可以有多個方法,這些方法名稱相同但參數(shù)列表不同(參數(shù)數(shù)量或類型不同)。
編譯器在編譯階段就能確定具體的方法。以下是一個重載示例,展示了多個同名方法,但參數(shù)個數(shù)或類型不同。重載的好處是簡化接口設(shè)計,不需要為不同類型編寫多個方法名。
java// OverloadExample.java 全部源碼見文檔鏈接
/**
* 重載示例,同名方法,參數(shù)個數(shù)或類型不同。
* 編譯器在編譯時確定具體的調(diào)用方法。
*/
class Calculator {
public int add(int num1, int num2) {
return num1 + num2;
}
public int add(int... nums) {
int sum=0;
for (int num : nums) {
sum +=num;
}
return sum;
}
}
運行時多態(tài)是在程序運行時確定實際要執(zhí)行的方法。
當子類繼承父類并覆蓋同名方法時,這稱為重寫。使用父類引用來聲明子類對象時,子類會向上轉(zhuǎn)型為父類類型。調(diào)用該對象的方法時,實際執(zhí)行的是子類的方法,而不是父類的方法。
向上轉(zhuǎn)型是指使用父類引用聲明子類對象,使子類對象的實際類型變?yōu)楦割悺Mㄟ^父類引用調(diào)用子類的方法,使代碼更加通用,處理一組相關(guān)對象時無需知道它們的具體類型。
向下轉(zhuǎn)型則是將父類引用轉(zhuǎn)換為子類引用,這需要顯式進行,并且在轉(zhuǎn)換前需要使用 instanceof 關(guān)鍵字進行類型檢查。
java// OverrideExample.java 全部源碼見文檔鏈接
/**
* 重寫示例,子類覆蓋父類同名方法,體現(xiàn)多態(tài)。
* 子類向上轉(zhuǎn)型為父類型,父類強制向下轉(zhuǎn)型為子類型。
*/
class Shape {
void draw() {
System.out.println("Shape->draw");
}
void drawShape() {
System.out.println("Shape->drawShape");
}
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Circle->draw");
}
void drawCircle() {
System.out.println("Circle->drawCircle");
}
}
class Square extends Shape {
@Override
void draw() {
System.out.println("Square->draw");
}
void drawSquare() {
System.out.println("Square->drawSquare");
}
}
public class OverrideExample {
public static void main(String[] args) {
// 用父類引用聲明子類對象,向上轉(zhuǎn)型
Shape shape1=new Circle();
Shape shape2=new Square();
// 子類有同名方法,動態(tài)綁定到子類,實質(zhì)執(zhí)行的是Circle.draw(),體現(xiàn)多態(tài)
shape1.draw();
// 報錯,編譯時按聲明類型檢查,Shape類中沒有drawCircle方法
// shape1.drawCircle();
// 執(zhí)行父類方法,輸出 "Shape->drawShape"
shape1.drawShape();
if (shape2 instanceof Square) {
// 向下轉(zhuǎn)型,用子類重新聲明,成為子類型了
Square mySquare=(Square) shape2;
// 輸出 "Square->draw"
mySquare.draw();
// 輸出 "Square->drawSquare"
mySquare.drawSquare();
// 報錯。若強轉(zhuǎn)為父類型,則無法調(diào)用drawSquare方法
// ((Shape) mySquare).drawSquare();
// 繼承父類,輸出 "Shape->drawShape"
mySquare.drawShape();
}
}
}
嚴格來說,多態(tài)需要具備以下三個條件。
重載不屬于嚴格意義上的多態(tài),因為重載在編譯階段就確定了。我們主要探討運行時的多態(tài),即針對某個類型的方法調(diào)用,實際執(zhí)行的方法取決于運行時的對象,而不是聲明時的類型。
java// 父類
class Animal {
void makeSound() {
System.out.println("Animal makes a sound");
}
}
// 子類繼承并重寫同名方法
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog barks");
}
}
public class Test {
public static void main(String[] args) {
// 父類引用聲明子類
Animal myAnimal=new Dog();
// 運行時對象為子類,故輸出"Dog barks"
myAnimal.makeSound();
}
}
如何理解父類聲明子類 Parent child=new Child(); ?
在面向?qū)ο笤O(shè)計中,“開閉原則”是非常重要的一條。即系統(tǒng)中的類應(yīng)該對擴展開放,而對修改關(guān)閉。這樣的代碼更可維護和可擴展,同時更加簡潔與清晰。
延續(xù)上面的例子,假設(shè)業(yè)務(wù)需要擴充更多子類,我們可以通過以下步驟來體現(xiàn)開閉原則:
java// 定義一個通用Animal類
class Animal {
void makeSound() {
System.out.println("Animal makes a sound");
}
}
// 定義Dog類,它是動物的子類
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog barks");
}
}
// 定義Cat類,它是動物的子類
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Cat meows");
}
// Cat自有方法
void meow() {
System.out.println("Cat is meowing...");
}
}
// 定義一個動物園類,管理不同的動物
class Zoo {
// 傳入的是抽象父類或接口,方便擴展
void letAnimalMakeSound(Animal animal) {
animal.makeSound();
}
}
public class AnimalExample {
public static void main(String[] args) {
Zoo zoo=new Zoo();
Animal myDog=new Dog(); // 向上轉(zhuǎn)型
Animal myCat=new Cat(); // 向上轉(zhuǎn)型
((Cat)myCat).meow(); // 向下強轉(zhuǎn),打印自有方法
// 通過多態(tài)性,動物園可以使用相同的方法處理不同種類的動物
zoo.letAnimalMakeSound(myDog); // 輸出 "Dog barks"
zoo.letAnimalMakeSound(myCat); // 輸出 "Cat meows"
}
}
要增加新的動物(如鳥類,Bird),只需擴展 Animal 類,而無需修改現(xiàn)有 Zoo 類中的方法。
javaclass Bird extends Animal {
@Override
void makeSound() {
System.out.println("Bird chirps");
}
}
public class AnimalExample {
public static void main(String[] args) {
Zoo zoo=new Zoo();
Animal myDog=new Dog(); // 向上轉(zhuǎn)型
Animal myCat=new Cat(); // 向上轉(zhuǎn)型
Animal myBird=new Bird(); // 向上轉(zhuǎn)型
// 通過多態(tài)性,動物園可以使用相同的方法處理不同種類的動物
zoo.letAnimalMakeSound(myDog); // 輸出 "Dog barks"
zoo.letAnimalMakeSound(myCat); // 輸出 "Cat meows"
zoo.letAnimalMakeSound(myBird); // 輸出 "Bird chirps"
}
}
這種設(shè)計:
我們的業(yè)務(wù)總在不停變化,如何使得代碼底層不用大改,而表層又能跟隨業(yè)務(wù)不停變動,這就顯得十分重要。通過這種方式,我們在不修改現(xiàn)有代碼的情況下,可以輕松地引入新的子類并擴展系統(tǒng)功能,同時保持現(xiàn)有代碼的穩(wěn)定性和可靠性。
不同語言因為語言特性的不同,在實現(xiàn)多態(tài)上也有不同。Go語言有接口,有struct,但沒有繼承和方法重載,實現(xiàn)多態(tài)與Java有所不同。Python和JavaScript作為動態(tài)語言,沒有接口和顯式類型聲明,但由于其本身的靈活性,在實現(xiàn)多態(tài)上也跟Java有區(qū)別。C語言沒有class和接口,struct也沒有成員函數(shù),可通過struct和函數(shù)指針來模擬多態(tài)。C++有class,在多態(tài)上跟Java有點像,但其支持多重繼承,且顯示聲明為virtual的方法才支持動態(tài)綁定,其核心機制上與Java也有所不同。
雖然各語言實現(xiàn)多態(tài)各不相同,但總的概念是一致的,即通過多態(tài)達到“開閉原則”的設(shè)計目標。以下一些語言的例子,其他例子請從倉庫查找源碼。
在Go語言中,雖然沒有傳統(tǒng)意義上的類繼承、父類聲明子類和方法重載,但通過結(jié)構(gòu)體(struct)和接口(interface)以及匿名組合等方式實現(xiàn)類似的功能。這樣也能實現(xiàn)代碼的組織和復(fù)用,同時保持了靈活性和簡潔性。
gopackage main
import (
"fmt"
)
// 定義一個Animal接口
type Animal interface {
MakeSound()
}
// 定義一個 Dog 類型
type Dog struct{}
// 實現(xiàn) Animal 接口的 MakeSound 方法
func (d Dog) MakeSound() {
fmt.Println("Dog barks")
}
// 定義一個 Cat 類型
type Cat struct{}
// 實現(xiàn) Animal 接口的 MakeSound 方法
func (c Cat) MakeSound() {
fmt.Println("Cat meows")
}
// Cat自有方法
func (c *Cat) Meow() {
fmt.Println("Cat is meowing...")
}
// 定義一個 Zoo 類型,用于管理動物
type Zoo struct{}
// 定義一個方法,讓動物發(fā)出聲音
func (z Zoo) LetAnimalMakeSound(a Animal) {
a.MakeSound()
}
func main() {
zoo :=Zoo{}
myDog :=Dog{}
// 接口斷言
var myCat Animal=&Cat{}
// 類型斷言,打印自有方法
(myCat.(*Cat)).Meow()
// 使用多態(tài)性,通過接口類型處理不同的具體類型
zoo.LetAnimalMakeSound(myDog) // 輸出 "Dog barks"
zoo.LetAnimalMakeSound(myCat) // 輸出 "Cat meows"
}
當需要增加Bird類型時,直接增加即可。同樣無需修改Zoo類里面的LetAnimalMakeSound方法。
gotype Bird struct{}
// 實現(xiàn) Animal 接口的 MakeSound 方法
func (b Bird) MakeSound() {
fmt.Println("Bird chirps")
}
func main() {
zoo :=Zoo{}
myDog :=Dog{}
var myCat Animal=&Cat{}
(myCat.(*Cat)).Meow()
myBird :=Bird{}
// 使用多態(tài)性,通過接口類型處理不同的具體類型
zoo.LetAnimalMakeSound(myDog) // 輸出 "Dog barks"
zoo.LetAnimalMakeSound(myCat) // 輸出 "Cat meows"
zoo.LetAnimalMakeSound(myBird) // 輸出 "Bird chirps"
}
嚴格的多態(tài)概念,包括子類繼承父類、方法重寫以及父類聲明子類等,這些特性在Go語言中無法實現(xiàn)。Go語言沒有class概念,雖然它的struct可以包含方法,看起來像class,但實際上沒有繼承和重載的支持,它們本質(zhì)上仍是結(jié)構(gòu)體。
Go語言摒棄了傳統(tǒng)面向?qū)ο笳Z言中的class和繼承概念,我們需要用新的視角來理解和實踐面向?qū)ο缶幊淘贕o中的應(yīng)用方式
JavaScript是一種動態(tài)弱類型的基于對象的語言,其一切皆是對象。它通過對象的原型鏈來實現(xiàn)面向?qū)ο缶幊獭1M管JavaScript具有class和繼承的能力,但由于缺少強類型系統(tǒng),因此無法實現(xiàn)傳統(tǒng)意義上的多態(tài)。
當然,JavaScript作為動態(tài)語言,具有天然的動態(tài)性優(yōu)勢。這使得它在靈活性和擴展性方面更具優(yōu)勢。
js// 定義一個通用Animal類
class Animal {
makeSound() {
console.log("Animal makes a sound");
}
}
// 定義Dog類,它是動物的子類
class Dog extends Animal {
makeSound() {
console.log("Dog barks");
}
}
// 定義Cat類,它是動物的子類
class Cat extends Animal {
makeSound() {
console.log("Cat meows");
}
// Cat自有函數(shù)
meow() {
console.log("Cat is meowing...", this);
}
}
// 定義一個動物園類,管理不同的動物
class Zoo {
// JS沒有嚴格類型,出原始數(shù)據(jù)類型外,其他均是Object
// 說出傳入的對象只要有makeSound方法即可。
letAnimalMakeSound(animal) {
animal.makeSound();
}
}
// 測試代碼
const zoo=new Zoo();
// JS沒有父類定義子類概念,直接聲明即可,無需向上轉(zhuǎn)型
// 通過instanceof類型判斷時可得到子類和父類類型
const myDog=new Dog();
const myCat=new Cat();
// 直接調(diào)用自有函數(shù)
myCat.meow();
// 可以動態(tài)給對象設(shè)置函數(shù)并綁定對象
myDog.meow=myCat.meow.bind(myDog);
myDog.meow();
// 動物園可以使用相同的方法處理不同種類的動物
// 當需要增加其他動物時,直接建立新的類繼承Animal,而無需修改Zoo。
zoo.letAnimalMakeSound(myDog); // 輸出 "Dog barks"
zoo.letAnimalMakeSound(myCat); // 輸出 "Cat meows"
可以看出JS要實現(xiàn)Java意義的多態(tài)是做不到的,但JavaScript更加靈活方便,聲明對象無需類型,還可以動態(tài)添加函數(shù)和綁定對象。
py# 定義一個通用Animal類
class Animal:
def make_sound(self):
print("Animal makes a sound")
# 定義Dog類,繼承Animal
class Dog(Animal):
name="Dog"
def make_sound(self):
print("Dog barks")
# 定義Cat類,繼承Animal
class Cat(Animal):
name="Cat"
def make_sound(self):
print("Cat meows")
# Cat自有方法
def meow(self):
print(self.name + " is meowing...")
# 定義Bird類,它是動物的子類
class Bird(Animal):
def make_sound(self):
print("Bird chirps")
# 定義管理類
class Zoo:
# python與js一樣為動態(tài)語言,使用duck typing,不需要顯式聲明接口
def let_animal_make_sound(self, animal):
animal.make_sound()
# 測試代碼
if __name__=="__main__":
zoo=Zoo()
# 直接創(chuàng)建實例,Python中不需要向上轉(zhuǎn)型
my_dog=Dog()
my_cat=Cat()
my_bird=Bird()
# 直接調(diào)用自有方法
my_cat.meow()
# Python中可直接給對象設(shè)置方法,self不會改變
my_dog.meow=my_cat.meow
my_dog.meow()
# 動物園可以使用相同的方法處理不同種類的動物
zoo.let_animal_make_sound(my_dog) # 輸出 "Dog barks"
zoo.let_animal_make_sound(my_cat) # 輸出 "Cat meows"
zoo.let_animal_make_sound(my_bird) # 輸出 "Bird chirps"
Python是一種動態(tài)語言,它使用 self 參數(shù)來引用實例,無需像其他語言那樣使用 new 關(guān)鍵字來實例化對象。Python沒有嚴格的接口概念,不需要像其他語言那樣顯示聲明對象的接口。Python通過繼承和方法重寫來實現(xiàn)多態(tài)概念,但不支持傳統(tǒng)意義上的父類聲明子類和方法重載。
因此,Python在多態(tài)性上的表現(xiàn)與JavaScript相似,都是基于動態(tài)語言特性,靈活而動態(tài),通過繼承和重寫實現(xiàn)對象行為的多樣性。
理解Java多態(tài)的實例可以幫助澄清其原理和執(zhí)行過程。以下是一個簡單而詳盡的例子,幫助你全面理解Java中多態(tài)的工作機制。
// PolymorphismSimple.java
// 父類A
class A {
public String show(D object) {
return ("A and D");
}
public String show(A object) {
return ("A and A");
}
// 默認注釋掉。可開關(guān)注釋測試下
// public String show(B object) {
// return ("A and B");
// }
}
// 子類B
class B extends A {
public String show(B object) {
return ("B and B");
}
public String show(A object) {
return ("B and A");
}
}
// 孫子類C
class C extends B {
}
// 孫子類D
class D extends B {
}
// 測試驗證
public class PolymorphismSimple {
public static void main(String[] args) {
// 父類聲明自己
A a=new A();
// 父類聲明子類
A ab=new B();
// 子類聲明自己
B b=new B();
C c=new C();
D d=new D();
// 1) A and A。b的類型是B,也是B的實例,A里沒有show(B)方法,但有show(A)方法。B的父類是A,因此定位到A.show(A)。
System.out.println("1) " + a.show(b));
// 2) A and A。c的類型是C,也是C的實例,C繼承B,B繼承A。A里沒有show(C)方法,也沒有show(B)方法,最后指向A.show(A)。
System.out.println("2) " + a.show(c));
// 3) A and D, d的類型是D,也是D的實例,D繼承B,B繼承A。A里有show(D)方法,直接定位到A.show(D)。
System.out.println("3) " + a.show(d));
// 4) B and A, ab是B的實例,但用A聲明,即向上轉(zhuǎn)型得到的類型是A,運行時才能確定具體該調(diào)用哪個方法。
// ab是B的實例對象,但引用類型是A。類型是在編譯時確定,因此從類型開始定位方法。
// A類中沒有show(B)方法,但有show(A)方法,因為A是B的父類,ab也是A的實例,于是定位到A.show(A)方法。
// 由于B是A的子類,且B重寫了A的show(A),A的方法被覆蓋了,于是定位到B.show(A),這就是動態(tài)綁定。
// 雖然B中有show(B)方法,但是因為ab的類型是A,編譯時根據(jù)類型定位到A的方法,而不是B。
// 以下幾種可開關(guān)打開/注釋代碼測試下。
// -
// 若A里有show(A)和show(B),B里有show(B)有show(A),則編譯時關(guān)聯(lián)到A.show(B),因B覆蓋了A.show(B),動態(tài)綁定到B.show(B)。
// -
// 若A里有show(A)和show(B),B里無show(B)有show(A),則編譯時關(guān)聯(lián)到A.show(B),因B無覆蓋,則直接調(diào)用A.show(B)。
// -
// 若A里有show(A)無show(B),B里無show(B)有show(A),則編譯時關(guān)聯(lián)到A.show(A),因B覆蓋了A.show(A),動態(tài)綁定到B.show(A)。
// -
// 若A里有show(A)無show(B),B里無show(A)有show(B),則編譯時關(guān)聯(lián)到A.show(A),因B無覆蓋,則直接調(diào)用A.show(A)。
// 查找順序為:編譯時根據(jù)引用類型確定所屬類 -> 根據(jù)重載參數(shù)類型定位(類型按子->父->祖逐級往上查找)到類的具體方法(包括繼承的方法) ->
// 運行時實例對象覆蓋(覆蓋只有子->父一層)了引用類型的同名方法 -> 定位到實例對象的方法。
System.out.println("4) " + ab.show(b));
// 5) B and A。ab是B的實例,類型是A。從A類沒找到show(C)方法,也沒找到A.show(B)方法,找到A.show(A)方法。A.show(A)被B.show(A)覆蓋,因此調(diào)用B.show(A)。
System.out.println("5) " + ab.show(c));
// 6) A and D。A里面有show(D)的方法,直接定位到。
System.out.println("6) " + ab.show(d));
// 7) B and B。B里面有show(B)的方法,直接定位到。
System.out.println("7) " + b.show(b));
// 8) B and B。B沒有show(c)方法,但有show(B)方法。C繼承自B,父類型是B,因此調(diào)用B.show(B)。
System.out.println("8) " + b.show(c));
// 9) A and D。B中沒有show(D)方法,B繼承A,A里有show(D), 故調(diào)用A.show(D)方法。
System.out.println("9) " + b.show(d));
// 10) B and A。父類聲明子類,存在向上轉(zhuǎn)型。A里有show(A),被B.show(A)覆蓋了,因此定位到B.show(A)。
System.out.println("10) " + ab.show(a));
}
}
多態(tài)包括編譯時多態(tài)和運行時多態(tài)。編譯時多態(tài),即靜態(tài)綁定,通常通過方法重載實現(xiàn)。運行時多態(tài)則是在代碼運行時確定具體調(diào)用的方法。
從Java的角度看,嚴格意義上的多態(tài)需要滿足三個條件:繼承、方法覆蓋和父類引用子類對象。Java完全符合這些要求,實現(xiàn)了嚴格意義上的多態(tài)。
Go語言、Python和JavaScript不完全符合嚴格意義上的多態(tài),但具備多態(tài)特性,能夠達成動態(tài)確定實際要執(zhí)行的方法,從而使代碼更加靈活、易于維護和擴展。
https://github.com/microwind/design-pattern/tree/main/programming-paradigm/oop/polymorphism
PolymorphismSimple.java PolymorphismSimple.go polymorphism_simple.c PolymorphismSimple.cpp PolymorphismSimple.js PolymorphismSimple.py PolymorphismSimple.ts
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。