JavaScript六種繼承方式總結(jié)大全
1. 原型鏈繼承
是什么?
這是最基礎(chǔ)的繼承方式。其核心是:讓一個(gè)構(gòu)造函數(shù)的 prototype 對(duì)象指向另一個(gè)構(gòu)造函數(shù)的實(shí)例。這樣,子類就能通過(guò)原型鏈訪問(wèn)到父類的屬性和方法。
javascript
// 父類
function Parent() {
this.name = 'Parent';
this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
// 子類
function Child() {
this.type = 'Child';
}
// 關(guān)鍵!將Child的原型指向Parent的實(shí)例,建立原型鏈
Child.prototype = new Parent();
var child1 = new Child();
child1.sayName(); // 'Parent' (來(lái)自原型鏈)
console.log(child1.colors); // ['red', 'blue'] (來(lái)自原型鏈)
var child2 = new Child();
child1.colors.push('green'); // 修改child1的colors
console.log(child2.colors); // ['red', 'blue', 'green'] (問(wèn)題出現(xiàn)了!)有什么作用?
實(shí)現(xiàn)屬性和方法的繼承。
實(shí)際開發(fā)運(yùn)用場(chǎng)景?
在現(xiàn)代前端開發(fā)中,幾乎不會(huì)單獨(dú)使用原型鏈繼承,因?yàn)樗兄旅毕荨5且磺衅渌^承方式的理論基礎(chǔ)。
優(yōu)點(diǎn):
簡(jiǎn)單易懂,是理解JS繼承的基礎(chǔ)。
能夠繼承父類原型上的方法。
缺點(diǎn):
引用類型屬性被所有實(shí)例共享(如上例中的
colors數(shù)組)。一個(gè)實(shí)例修改了引用類型屬性,所有實(shí)例都會(huì)受到影響。這是最大的問(wèn)題。創(chuàng)建子類實(shí)例時(shí),無(wú)法向父類構(gòu)造函數(shù)傳參(因?yàn)?nbsp;
new Parent()在初始化原型時(shí)就執(zhí)行了)。
2. 借用構(gòu)造函數(shù)繼承(經(jīng)典繼承)
是什么?
為了解決原型鏈繼承的缺點(diǎn),這種方法的核心是:在子類構(gòu)造函數(shù)的內(nèi)部調(diào)用父類構(gòu)造函數(shù)。這利用了 call() 或 apply() 方法,使父類的 this 指向子類的實(shí)例。
javascript
// 父類
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
// 注意:父類原型上的方法子類訪問(wèn)不到
Parent.prototype.sayName = function() {
console.log(this.name);
};
// 子類
function Child(name, type) {
// 關(guān)鍵!“借用”父類的構(gòu)造函數(shù)來(lái)初始化屬性
Parent.call(this, name); // 相當(dāng)于執(zhí)行了 this.name = name; this.colors = ['red', 'blue'];
this.type = type;
}
var child1 = new Child('小明', 'Child');
var child2 = new Child('小紅', 'Child');
child1.colors.push('green');
console.log(child1.colors); // ['red', 'blue', 'green']
console.log(child2.colors); // ['red', 'blue'] (互不影響了!)
console.log(child1.sayName); // undefined (無(wú)法繼承父類原型的方法)有什么作用?
解決原型鏈繼承中“引用屬性共享”和“無(wú)法傳參”的問(wèn)題。
實(shí)際開發(fā)運(yùn)用場(chǎng)景?
通常不會(huì)單獨(dú)使用,但它是組合繼承的重要組成部分。
優(yōu)點(diǎn):
避免了引用屬性共享的問(wèn)題。
可以在子類中向父類傳遞參數(shù)。
缺點(diǎn):
方法都在構(gòu)造函數(shù)中定義,每次創(chuàng)建實(shí)例都會(huì)創(chuàng)建一遍方法,無(wú)法實(shí)現(xiàn)函數(shù)復(fù)用,效率低。
無(wú)法繼承父類原型(prototype)上的方法(如上例中的
sayName)。
3. 組合繼承
是什么?
組合繼承結(jié)合了原型鏈繼承和借用構(gòu)造函數(shù)繼承的優(yōu)點(diǎn),是 JavaScript 中最常用的繼承模式。其核心是:
使用借用構(gòu)造函數(shù)來(lái)繼承屬性(解決共享和傳參問(wèn)題)。
使用原型鏈來(lái)繼承方法(實(shí)現(xiàn)方法復(fù)用)。
javascript
// 父類
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
// 子類
function Child(name, type) {
// 1. 繼承屬性
Parent.call(this, name); // 第二次調(diào)用 Parent()
this.type = type;
}
// 2. 繼承方法
Child.prototype = new Parent(); // 第一次調(diào)用 Parent()
// 修復(fù)構(gòu)造函數(shù)指向,否則Child實(shí)例的constructor會(huì)指向Parent
Child.prototype.constructor = Child;
// 子類自己的方法
Child.prototype.sayType = function() {
console.log(this.type);
};
var child1 = new Child('小明', 'Child1');
child1.colors.push('green');
console.log(child1.colors); // ['red', 'blue', 'green']
child1.sayName(); // '小明'
child1.sayType(); // 'Child1'
var child2 = new Child('小紅', 'Child2');
console.log(child2.colors); // ['red', 'blue'] (屬性不共享)
child2.sayName(); // '小紅' (方法可復(fù)用)有什么作用?
綜合了兩種模式的優(yōu)點(diǎn),成為 JavaScript 中一種非常實(shí)用的繼承模式。
實(shí)際開發(fā)運(yùn)用場(chǎng)景?
在 ES6 的 class 出現(xiàn)之前,這是最主流、最可靠的繼承方式。常用于構(gòu)建復(fù)雜的對(duì)象系統(tǒng),如UI組件庫(kù)、游戲引擎中的實(shí)體繼承等。
優(yōu)點(diǎn):
實(shí)例擁有獨(dú)立的屬性,不會(huì)共享。
實(shí)例可以復(fù)用父類原型上的方法。
可以向父類構(gòu)造函數(shù)傳參。
缺點(diǎn):
最大的缺點(diǎn):會(huì)兩次調(diào)用父類構(gòu)造函數(shù)。
一次在
Parent.call(this)。一次在
new Parent()。
這導(dǎo)致子類實(shí)例和原型上存在兩份相同的屬性(一份在實(shí)例自身,一份在__proto__里),造成了一些不必要的浪費(fèi)(雖然實(shí)例自身的屬性會(huì)屏蔽原型上的屬性,沒(méi)有功能問(wèn)題)。
4. 原型式繼承
是什么?
道格拉斯·克羅克福德提出的方法。其核心是:創(chuàng)建一個(gè)臨時(shí)的構(gòu)造函數(shù),將其原型指向某個(gè)對(duì)象,然后返回這個(gè)臨時(shí)構(gòu)造函數(shù)的實(shí)例。本質(zhì)上是對(duì)傳入的對(duì)象進(jìn)行了一次淺復(fù)制。
javascript
// object() 就是 ES5 中 Object.create() 的模擬實(shí)現(xiàn)
function object(o) {
function F() {} // 創(chuàng)建一個(gè)臨時(shí)構(gòu)造函數(shù)
F.prototype = o; // 將其原型指向傳入的對(duì)象o
return new F(); // 返回這個(gè)臨時(shí)構(gòu)造函數(shù)的實(shí)例
}
var person = {
name: 'Nicholas',
friends: ['Shelby', 'Court']
};
var person1 = object(person);
person1.name = 'Greg';
person1.friends.push('Rob');
var person2 = object(person);
person2.name = 'Linda';
person2.friends.push('Barbie');
console.log(person.friends); // ['Shelby', 'Court', 'Rob', 'Barbie'] (共享問(wèn)題依然存在)有什么作用?
在不必興師動(dòng)眾地創(chuàng)建構(gòu)造函數(shù)的情況下,基于一個(gè)已有對(duì)象創(chuàng)建新對(duì)象。
實(shí)際開發(fā)運(yùn)用場(chǎng)景?
適用于簡(jiǎn)單對(duì)象的淺拷貝繼承。
ES5 的
Object.create()方法規(guī)范化了原型式繼承。現(xiàn)在直接使用Object.create()即可。
優(yōu)點(diǎn):
無(wú)需創(chuàng)建構(gòu)造函數(shù),代碼簡(jiǎn)潔。
缺點(diǎn):
和原型鏈繼承一樣,存在引用屬性共享的問(wèn)題。
5. 寄生式繼承
是什么?
寄生式繼承的思路與寄生構(gòu)造函數(shù)和工廠模式類似。其核心是:創(chuàng)建一個(gè)僅用于封裝繼承過(guò)程的函數(shù),該函數(shù)在內(nèi)部以某種方式(如原型式繼承)增強(qiáng)對(duì)象,最后返回這個(gè)對(duì)象。
javascript
function createAnother(original) {
var clone = object(original); // 1. 通過(guò)調(diào)用函數(shù)(如object)創(chuàng)建一個(gè)新對(duì)象(原型式繼承)
clone.sayHi = function() { // 2. 以某種方式來(lái)增強(qiáng)這個(gè)對(duì)象
console.log('hi');
};
return clone; // 3. 返回這個(gè)對(duì)象
}
var person = {
name: 'Nicholas',
friends: ['Shelby', 'Court']
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // 'hi'有什么作用?
主要關(guān)注對(duì)象而不是自定義類型和構(gòu)造函數(shù),在主要考慮對(duì)象而非自定義類型和構(gòu)造函數(shù)的情況下,實(shí)現(xiàn)簡(jiǎn)單的繼承和擴(kuò)展。
實(shí)際開發(fā)運(yùn)用場(chǎng)景?
適用于為對(duì)象添加一些額外功能的場(chǎng)景,但不廣泛使用。
優(yōu)點(diǎn):
可以在不創(chuàng)建構(gòu)造函數(shù)的情況下,為對(duì)象添加函數(shù)。
缺點(diǎn):
函數(shù)難以復(fù)用,效率低(跟借用構(gòu)造函數(shù)模式一樣)。
存在引用屬性共享的問(wèn)題(跟原型式繼承一樣)。
6. 寄生組合式繼承
是什么?
這是組合繼承的優(yōu)化版本,也是目前公認(rèn)的最理想的繼承范式。它解決了組合繼承調(diào)用兩次父類構(gòu)造函數(shù)的問(wèn)題。
其核心是:
使用借用構(gòu)造函數(shù)來(lái)繼承屬性。
使用寄生式繼承來(lái)繼承父類原型,并將其賦值給子類原型。
javascript
function inheritPrototype(child, parent) {
// 1. 創(chuàng)建父類原型的一個(gè)副本(原型式繼承)
var prototype = Object.create(parent.prototype);
// 2. 修復(fù)副本的constructor指針
prototype.constructor = child;
// 3. 將子類的原型指向這個(gè)副本
child.prototype = prototype;
}
// 父類
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
// 子類
function Child(name, type) {
// 只調(diào)用一次父類構(gòu)造函數(shù)(繼承屬性)
Parent.call(this, name);
this.type = type;
}
// 關(guān)鍵!替換掉組合繼承中的 `Child.prototype = new Parent()`
inheritPrototype(Child, Parent);
// 添加子類自己的方法
Child.prototype.sayType = function() {
console.log(this.type);
};
var child1 = new Child('小明', 'Child1');
// 實(shí)例的 __proto__ 指向 Child.prototype
// Child.prototype 的 __proto__ 指向 Parent.prototype
// 完美!有什么作用?
只調(diào)用一次父類構(gòu)造函數(shù),并且避免了在子類原型上創(chuàng)建不必要的、多余的屬性。同時(shí),原型鏈還能保持不變。
寄生組合式繼承核心目標(biāo):修復(fù)“組合繼承”的缺陷
要理解“寄生組合”,我們必須先回顧“組合繼承”的問(wèn)題。
組合繼承的做法:
Parent.call(this):在子類構(gòu)造函數(shù)里調(diào)用父類構(gòu)造函數(shù)。這會(huì)在子類實(shí)例自身上創(chuàng)建一份父類的屬性。Child.prototype = new Parent():將子類的原型指向一個(gè)父類的實(shí)例。這會(huì)在子類的原型對(duì)象上創(chuàng)建第二份父類的屬性。
這就導(dǎo)致了:
子類實(shí)例上有
name和colors屬性。子類實(shí)例的
__proto__(也就是Child.prototype)上也有name和colors屬性。
雖然實(shí)例自身的屬性會(huì)屏蔽掉原型上的同名屬性,功能上沒(méi)問(wèn)題,但多創(chuàng)建了一份多余的屬性,造成了內(nèi)存浪費(fèi)和不優(yōu)雅。
寄生組合式繼承的解決方案
它的核心思路非常巧妙:我們真的需要那個(gè) new Parent() 實(shí)例來(lái)充當(dāng)原型嗎?不,我們只需要父類原型上的方法。
我們不需要 Parent 實(shí)例上的屬性(因?yàn)槲覀円呀?jīng)通過(guò) Parent.call(this) 在子類實(shí)例上得到了一份),我們只需要能通過(guò)原型鏈找到 Parent.prototype 上的方法。
所以,新的方案是:
繼承屬性:保持不變,依然在子類構(gòu)造函數(shù)里用
Parent.call(this)在子類構(gòu)造函數(shù)里調(diào)用父類構(gòu)造函數(shù),這會(huì)在子類實(shí)例自身上創(chuàng)建一份父類的屬性。這保證了每個(gè)實(shí)例都有自己獨(dú)立的屬性。繼承方法:不再用
new Parent(),而是直接創(chuàng)建一個(gè)純凈的、指向父類原型的對(duì)象,用它來(lái)作為子類的原型。
這個(gè)“純凈的、指向父類原型的對(duì)象”就是 Object.create(Parent.prototype) 所做的事情。
一步步拆解(超級(jí)詳細(xì)版)
讓我們把 inheritPrototype(Child, Parent) 這個(gè)函數(shù)里的三步拆開來(lái)看:
javascript
function inheritPrototype(child, parent) {
// 第一步:創(chuàng)建原型副本(核心)
var prototype = Object.create(parent.prototype);
// 第二步:修復(fù)constructor指向
prototype.constructor = child;
// 第三步:將子類的原型指向這個(gè)新創(chuàng)建的對(duì)象
child.prototype = prototype;
}第一步:var prototype = Object.create(parent.prototype);
Object.create()方法會(huì)創(chuàng)建一個(gè)新對(duì)象。這個(gè)新對(duì)象的
__proto__會(huì)指向你傳入的參數(shù),也就是parent.prototype。想象一下:這就好比我們憑空造了一個(gè)空對(duì)象
{},并且讓這個(gè)空對(duì)象“認(rèn)”Parent.prototype為它的爸爸(原型)。這個(gè)空對(duì)象自己沒(méi)有任何屬性(解決了屬性重復(fù)的問(wèn)題),但它可以順著原型鏈找到 Parent.prototype 上的所有方法(sayName)。
第二步:prototype.constructor = child;
任何一個(gè)
prototype對(duì)象都有一個(gè)constructor屬性,默認(rèn)指向它關(guān)聯(lián)的構(gòu)造函數(shù)。因?yàn)槲覀冇?nbsp;
Object.create()創(chuàng)建的新對(duì)象,它的constructor指向的是parent(因?yàn)樗^承自parent.prototype,而parent.prototype.constructor指向parent)。這顯然不對(duì),我們希望子類原型的 constructor 指向子類自己 child。
所以我們需要手動(dòng)糾正一下,讓
prototype.constructor = child;。
第三步:child.prototype = prototype;
最后,我們把這個(gè)我們精心制作好的、純凈的、鏈接到了父類原型的、constructor指正確的
prototype對(duì)象,賦值給子類的prototype。從此,所有
new Child()出來(lái)的實(shí)例,它們的__proto__都指向我們這個(gè)prototype對(duì)象,從而可以順利地通過(guò)原型鏈調(diào)用父類的方法。
終極比喻:“繼承家產(chǎn)”的故事
父類 (
Parent):一個(gè)富豪老爹,他有金庫(kù)(實(shí)例屬性name,colors)和一本生意經(jīng)(原型方法sayName)。組合繼承:老爹先給你復(fù)制了一本完整的生意經(jīng)(包括金庫(kù)的地圖),然后你又自己去金庫(kù)里拿了一次金子。你手里有金子,書上也有金子的地圖,地圖是多余的。
寄生組合繼承:一個(gè)聰明的律師(
Object.create)出現(xiàn)了。他沒(méi)有復(fù)制整本生意經(jīng),而是只做了一張神奇的索引卡。這張索引卡本身是空白的(沒(méi)有多余的屬性),但它直接指向了老爹那本生意經(jīng)的原始內(nèi)容(Parent.prototype)。然后你又自己去金庫(kù)里拿了一次金子。你手里有金子,也通過(guò)索引卡學(xué)會(huì)了老爹的生意經(jīng),完美!
總結(jié):為什么它是最佳的?
| 特性 | 組合繼承 | 寄生組合繼承 | 優(yōu)勢(shì) |
|---|---|---|---|
| 父類屬性副本 | 2份 (實(shí)例上1份,原型上1份) | 1份 (僅在實(shí)例上) | 更高效,無(wú)浪費(fèi) |
| 父類方法繼承 | 通過(guò)原型鏈繼承 | 通過(guò)原型鏈繼承 | 同樣有效 |
| 父類構(gòu)造函數(shù)調(diào)用 | 2次 | 1次 | 性能更優(yōu) |
| 原型鏈 | 保持正確 | 保持正確 | 同樣正確 |
所以,寄生組合式繼承的核心貢獻(xiàn)就是:
它用一種極其巧妙的方式(Object.create)建立了子類和父類原型的直接聯(lián)系,完全跳過(guò)了創(chuàng)建父類實(shí)例這個(gè)不必要的步驟,從而避免了創(chuàng)建多余的屬性,完美地實(shí)現(xiàn)了繼承。這也是為什么ES6的 class 和 extends 語(yǔ)法糖其底層實(shí)現(xiàn)原理是寄生組合繼承的原因,因?yàn)樗_實(shí)是理論上最完美的方案
實(shí)際開發(fā)運(yùn)用場(chǎng)景?
這是實(shí)現(xiàn)基于構(gòu)造函數(shù)的繼承的最佳模式。在需要高度優(yōu)化和避免不必要的內(nèi)存開銷的庫(kù)或框架中可能會(huì)看到。但在日常開發(fā)中,我們更傾向于使用 ES6 的 class 和 extends,其底層原理就是寄生組合式繼承。
優(yōu)點(diǎn):
只調(diào)用一次父類構(gòu)造函數(shù),效率高。
避免了在子類原型上創(chuàng)建不必要的屬性。
原型鏈保持不變,能正常使用
instanceof和isPrototypeOf。
缺點(diǎn):
實(shí)現(xiàn)起來(lái)相對(duì)復(fù)雜。但通??梢苑庋b成一個(gè)函數(shù)(如上面的
inheritPrototype)來(lái)復(fù)用。
總結(jié)與建議
| 繼承方式 | 核心思想 | 優(yōu)點(diǎn) | 缺點(diǎn) | 適用場(chǎng)景 |
|---|---|---|---|---|
| 原型鏈繼承 | 子類原型 = 父類實(shí)例 | 簡(jiǎn)單,方法可復(fù)用 | 引用共享,無(wú)法傳參 | 基礎(chǔ)學(xué)習(xí),幾乎不用 |
| 借用構(gòu)造函數(shù) | 在子類中 Parent.call(this) | 屬性獨(dú)立,可傳參 | 方法不能復(fù)用 | 組合繼承的一部分 |
| 組合繼承 | 借用構(gòu)造 + 原型鏈 | 屬性獨(dú)立,方法可復(fù)用,可傳參 | 調(diào)用兩次父類構(gòu)造函數(shù) | ES6前的主流方式 |
| 原型式繼承 | Object.create() | 無(wú)需構(gòu)造函數(shù),簡(jiǎn)單 | 引用共享 | 對(duì)象淺拷貝 |
| 寄生式繼承 | 工廠模式+增強(qiáng)對(duì)象 | 無(wú)需構(gòu)造函數(shù),可增強(qiáng)對(duì)象 | 方法不能復(fù)用,引用共享 | 為對(duì)象添加功能 |
| 寄生組合繼承 | 借用構(gòu)造 + 寄生式繼承父類原型 | 近乎完美,只調(diào)用一次父類構(gòu)造函數(shù) | 實(shí)現(xiàn)稍復(fù)雜 | 理想的繼承范式 |
現(xiàn)代開發(fā)建議:
直接使用 ES6 的 class 和 extends 關(guān)鍵字。它們的語(yǔ)法更清晰、更易于理解,并且其底層實(shí)現(xiàn)的就是寄生組合式繼承這種最理想的方式。你不再需要手動(dòng)處理原型鏈,避免了出錯(cuò)的可能。
javascript
// ES6 的寫法,底層是寄生組合式繼承
class Parent {
constructor(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
sayName() {
console.log(this.name);
}
}
class Child extends Parent {
constructor(name, type) {
super(name); // 相當(dāng)于 Parent.call(this, name)
this.type = type;
}
sayType() {
console.log(this.type);
}
}到此這篇關(guān)于JavaScript六種繼承方式的文章就介紹到這了,更多相關(guān)JS六種繼承方式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript中call,apply,bind的區(qū)別詳解
這篇文章主要介紹了javascript中call,apply,bind的區(qū)別詳解,幫助大家更好的理解和使用JavaScript,感興趣的朋友可以了解下2020-12-12
小程序使用webview內(nèi)嵌h5頁(yè)面 wx.miniProgram.getEnv失效問(wèn)題
本文主要介紹了小程序使用webview內(nèi)嵌h5頁(yè)面 wx.miniProgram.getEnv失效問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
通過(guò)遮罩層實(shí)現(xiàn)浮層DIV登錄的js代碼
遮罩層實(shí)現(xiàn)浮層DIV登錄的效果,想必很多的朋友都有遇到過(guò)吧,實(shí)現(xiàn)起來(lái)也是很簡(jiǎn)單的,下面有個(gè)不錯(cuò)的實(shí)現(xiàn),大家可以感受下2014-02-02
javascript實(shí)現(xiàn)的動(dòng)態(tài)文字變換
javascript實(shí)現(xiàn)的動(dòng)態(tài)文字變換...2007-07-07
axios攔截器機(jī)制的實(shí)現(xiàn)原理詳解
axios 不僅提供了一套跨平臺(tái)請(qǐng)求,另外還提供了“攔截器”這一中間件機(jī)制,方便你在請(qǐng)求之前、響應(yīng)之后做些操作處理,本文給大家詳細(xì)介紹了axios 攔截器機(jī)制是如何實(shí)現(xiàn)的,需要的朋友可以參考下2024-05-05
JS聲明式函數(shù)與賦值式函數(shù)實(shí)例分析
這篇文章主要介紹了JS聲明式函數(shù)與賦值式函數(shù),結(jié)合實(shí)例形式分析了JS函數(shù)解析的流程與執(zhí)行順序,需要的朋友可以參考下2016-12-12
JavaScript實(shí)現(xiàn)煙花綻放動(dòng)畫效果
這篇文章主要介紹了JavaScript如何實(shí)現(xiàn)煙花綻放動(dòng)畫效果,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-08-08
完美解決手機(jī)瀏覽器頂部下拉出現(xiàn)網(wǎng)頁(yè)源或刷新的問(wèn)題
下面小編就為大家分享一篇完美解決手機(jī)瀏覽器頂部下拉出現(xiàn)網(wǎng)頁(yè)源或刷新的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
利用JavaScript實(shí)現(xiàn)放鞭炮動(dòng)畫效果
談及過(guò)年,最先讓人想到的就是,噼里啪啦的鞭炮聲。本文將利用JavaScript實(shí)現(xiàn)一個(gè)簡(jiǎn)單的放鞭炮動(dòng)畫效果,感興趣的小伙伴可以了解一下2023-01-01

