深入學(xué)習(xí)JavaScript中的promise
為什么要用Promise?
我們知道JavaScript是單線程的,一次只能執(zhí)行一個(gè)任務(wù),會(huì)阻塞其他任務(wù)。因此,所有的網(wǎng)絡(luò)任務(wù)、游覽器事件等都是異步的,我們可以使用異步回調(diào)函數(shù)來(lái)進(jìn)行異步操作。
有這么一個(gè)場(chǎng)景,我可以通過(guò)6個(gè)人能夠認(rèn)識(shí)到任何一個(gè)人。但是我們不知道當(dāng)前的人聯(lián)系到下一個(gè)人的時(shí)間是多久,假如這是一個(gè)異步的操作。
可以用如下代碼表示:
function ConnectPeople(i) {
console.log(`我聯(lián)系到了第${i}個(gè)人`);
return i + 1;
}
let i = 1;
setTimeout(() => {
const result1 = ConnectPeople(i);
setTimeout(() => {
const result2 = ConnectPeople(result1);
setTimeout(() => {
const result3 = ConnectPeople(result2);
setTimeout(() => {
const result4 = ConnectPeople(result3);
setTimeout(() => {
const result5 = ConnectPeople(result4);
setTimeout(() => {
const result6 = ConnectPeople(result5);
setTimeout(() => {
const result7 = ConnectPeople(result6);
setTimeout(() => {
const result8 = ConnectPeople(result7);
setTimeout(() => {
const result9 = ConnectPeople(result8);
setTimeout(() => {
const result10 = ConnectPeople(result9);
}, 10000);
}, 5000);
}, 3000);
}, 2000);
}, 3000);
}, 2000);
}, 1000);
}, 500);
}, 2000);
}, 1000);
如上所示,當(dāng)我們聯(lián)系到了第一個(gè)人后,再去聯(lián)系第二個(gè)人,然后再去聯(lián)系第三個(gè)人...直到我聯(lián)系到了10個(gè)人。乍一看,代碼好像還挺規(guī)整,但是如果100個(gè)人,1000個(gè)人呢?由于回調(diào)很多,函數(shù)作為參數(shù)層層嵌套,就陷入了回調(diào)地獄。這種情況下,就像是金字塔一樣的代碼非常不利于閱讀。
但是還好,我們有解決辦法。
使用Promise解決異步控制問(wèn)題
什么是Promise?
Promise對(duì)象的主要?途是通過(guò)鏈?zhǔn)秸{(diào)?的結(jié)構(gòu),將原本回調(diào)嵌套的異步處理流程,轉(zhuǎn)化成“對(duì)象.then().then()...”的鏈?zhǔn)浇Y(jié)構(gòu),這樣雖然仍離不開回調(diào)函數(shù),但是將原本的回調(diào)嵌套結(jié)構(gòu),轉(zhuǎn)化成了連續(xù)調(diào)?的結(jié)構(gòu),這樣就可以在閱讀上編程上下左右結(jié)構(gòu)的異步執(zhí)?流程了。
因此,Promise的作?是解決“回調(diào)地獄”,他的解決?式是將回調(diào)嵌套拆成鏈?zhǔn)秸{(diào)?,這樣便可以按照上下順序來(lái)進(jìn)?異步代碼的流程控制。 如下代碼所示,我們使用了Promise,代碼也從原先的金字塔形式轉(zhuǎn)變成了從上往下的執(zhí)行流程。
function ConnectPeople(i) {
console.log(`我聯(lián)系到了第${i}個(gè)人`);
return i + 1;
}
const p = new Promise((resolve) => {
setTimeout(() => {
resolve(ConnectPeople(1));
}, 1000);
});
p.then((v1) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(ConnectPeople(v1));
}, 1000);
});
}).then((v2) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(ConnectPeople(v2));
}, 1000);
});
});
Promise的結(jié)構(gòu)
根據(jù)上面的代碼案例,我們發(fā)現(xiàn)Promise需要通過(guò)new關(guān)鍵字同時(shí)傳入一個(gè)參數(shù)來(lái)創(chuàng)建,所以我們可以嘗試打印一下window對(duì)象console.log(window)(window 對(duì)象在瀏覽器中有兩重身份,一個(gè)是ECMAScript 中的 Global 對(duì)象,另一個(gè)就是瀏覽器窗口的 JavaScript 接口),可以發(fā)現(xiàn)存在一個(gè)Promise的構(gòu)造函數(shù)。

Promise初始化的時(shí)候需要傳入一個(gè)函數(shù),如下所示:
const p = new Promise(fn) // fn是初始化的時(shí)候調(diào)用的函數(shù),它是同步的回調(diào)函數(shù)
回調(diào)函數(shù)
什么是回調(diào)函數(shù)?
JavaScript中的回調(diào)函數(shù)結(jié)構(gòu),默認(rèn)是同步的結(jié)構(gòu)。由于JavaScript單線程異步模型的規(guī)則,如果想要編寫異步的代碼,必須使?回調(diào)嵌套的形式才能實(shí)現(xiàn),所以回調(diào)函數(shù)結(jié)構(gòu)不?定是異步代碼,但是異步代碼?定是回調(diào)函數(shù)結(jié)構(gòu)。
為什么異步代碼一定是回調(diào)函數(shù)結(jié)構(gòu)?
我們知道JavaScript是單線程異步模型,嚴(yán)格按照同步在前異步在后的順序執(zhí)行。如果用默認(rèn)的上下結(jié)構(gòu),我們拿不到異步回調(diào)中的結(jié)果。
如下所示,代碼執(zhí)行的時(shí)候會(huì)先執(zhí)行同步代碼,異步代碼會(huì)掛起,繼續(xù)執(zhí)行同步代碼,到1s的時(shí)候掛起的任務(wù)會(huì)進(jìn)入隊(duì)列,到2s的時(shí)候會(huì)繼續(xù)執(zhí)行同步代碼打印1,然后從任務(wù)隊(duì)列中取任務(wù)將num變成100,打印num。 所以實(shí)際執(zhí)行效果是,過(guò)2秒后,先打印1再打印100。
var num = 1;
setTimeout(()=>{
num = 100
console.log(num)
},0)
var d = new Date().getTime()
var d1 = new Date().getTime()
while ( d1 - d < 1000 ) {
d1 = new Date().getTime()
}
console.log(num) // 1刨析Promise
翻譯一下promise,結(jié)果是承諾,保證,紅寶書中的解釋是期約。
它有三個(gè)狀態(tài):
- pending 待定,初始狀態(tài)
- fulfilled 兌現(xiàn),已完成,通常代表成功執(zhí)行了某一任務(wù)。初始化函數(shù)中的resolve()執(zhí)行時(shí),狀態(tài)就會(huì)變味fulfilled,而且.then函數(shù)注冊(cè)的回調(diào)會(huì)開始執(zhí)行,resolve中傳遞的參數(shù)會(huì)進(jìn)入回調(diào)函數(shù)成為形參。
- rejected 拒絕,通常代表執(zhí)行一次任務(wù)失敗,調(diào)用reject()時(shí),catch注冊(cè)的函數(shù)就會(huì)觸發(fā),并且reject中傳遞的內(nèi)容會(huì)變成回調(diào)函數(shù)的形參。
三種狀態(tài)之間的關(guān)系:
當(dāng)對(duì)象創(chuàng)建之后同?個(gè)Promise對(duì)象只能從pending狀態(tài)變更為fulfilled或rejected中的其中?種,并且狀態(tài)?旦變更就不會(huì)再改變,此時(shí)Promise對(duì)象的流程執(zhí)?完成并且finally函數(shù)執(zhí)?。

我們打印一下Promise對(duì)象,發(fā)現(xiàn)它的構(gòu)造函數(shù)中定義了all、allSettled、any、race、reject、resolve方法(這些是實(shí)例方法),它的原型上存在catch、finally、then方法(這些是原型方法)。
原型方法——catch\finally\then
首先看下面代碼:
new Promise(function (resolve, reject) {
resolve();
reject();
})
.then(function () {
console.log("then執(zhí)?");
})
.catch(function () {
console.log("catch執(zhí)?");
})
.finally(function () {
console.log("finally執(zhí)?");
});執(zhí)行后依次打印then執(zhí)行->finally執(zhí)行,發(fā)現(xiàn).catch的回調(diào)沒(méi)有執(zhí)行。
再看如下代碼:
new Promise(function (resolve, reject) {
reject();
resolve();
})
.then(function () {
console.log("then執(zhí)?");
})
.catch(function () {
console.log("catch執(zhí)?");
})
.finally(function () {
console.log("finally執(zhí)?");
});這個(gè)串代碼和之前的代碼唯一的不同在于Promise中的回調(diào)先執(zhí)行了resolve()還是先執(zhí)行了reject(),打印結(jié)果是catch執(zhí)行->finally執(zhí)行,發(fā)現(xiàn).then的回調(diào)沒(méi)有執(zhí)行。
那如果Promise的回調(diào)不執(zhí)行reject()和resolve()呢?
會(huì)發(fā)現(xiàn)什么輸出都沒(méi)有!
注意:Promise.prototype.catch()其實(shí)是一個(gè)語(yǔ)法糖,相當(dāng)于是調(diào)用 Promise.prototype.then(null, onRejected)。.then中其實(shí)是可以傳入2個(gè)回調(diào)函數(shù),第一個(gè)回調(diào)函數(shù)是resolve()后執(zhí)行,第二個(gè)回調(diào)函數(shù)是reject()后執(zhí)行,2個(gè)是互斥的。
這是因?yàn)镻romise的異步回調(diào)部分如何執(zhí)?,取決于我們?cè)诔跏蓟瘮?shù)中的操作,并且初始化函數(shù)中?旦調(diào)?了resolve后?再執(zhí)?reject也不會(huì)影響then執(zhí)?,catch也不會(huì)執(zhí)?,反之同理。
?在初始化回調(diào)函數(shù)中,如果不執(zhí)?任何操作,那么promise的狀態(tài)就仍然是pending,所有注冊(cè)的回調(diào)函數(shù)都不會(huì)執(zhí)?。
由此可見(jiàn),執(zhí)行完resolve()之后才能夠執(zhí)行.then的回調(diào);執(zhí)行reject()之后才能夠執(zhí)行.catch的回調(diào);finally()的回調(diào)會(huì)在執(zhí)行完.then或.catch之后執(zhí)行。
這時(shí)候,我們就會(huì)想,是不是可以把resolve或者reject的調(diào)用設(shè)定在異步函數(shù)內(nèi)去調(diào)用,這樣是不是就能解決回調(diào)地獄的問(wèn)題了?
所以我們就去嘗試一下:
new Promise(function (resolve, reject) {
setTimeout(() => {
console.log(111);
resolve();
}, 2000);
})
.then(function () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log(222);
resolve();
}, 2000);
});
})
.then(function () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log(333);
resolve();
}, 2000);
});
})
.catch(function () {
console.log("catch執(zhí)?");
})
.finally(function () {
console.log("finally執(zhí)?");
});上面代碼每隔2s依次打印111->222->333 finally執(zhí)行。333執(zhí)行后立馬執(zhí)行finally。
為什么要在.then的回調(diào)函數(shù)中return一個(gè)Promise呢?
因?yàn)橄乱粋€(gè)異步的執(zhí)行,需要等待前一個(gè)異步執(zhí)行完畢后才調(diào)用,我們需要用到resolve來(lái)控制.then執(zhí)行的時(shí)機(jī)。
那如果我們不指明return返回值,它會(huì)返回什么呢?是如何實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用呢?
看下面代碼:
const p2 = new Promise((resolve, reject) => {
resolve();
});
const p3 = p2.then(() => {
console.log("resolved");
});
console.log(p3, 111);p2.then的回調(diào)函數(shù)中沒(méi)有return,但是我們知道一般來(lái)說(shuō)函數(shù)返回值默認(rèn)返回undefined,但是undefined中不會(huì)存在.then的方法。
因此我們就看一下p3里面到底是什么。有些人會(huì)想,.then是異步調(diào)用的,它是一個(gè)微任務(wù),那訪問(wèn)p3是不是不太正確?
我們打印一下p3,就會(huì)看到如下信息:

第一行p3的狀態(tài)還是pending,當(dāng)我們點(diǎn)開,發(fā)現(xiàn)已經(jīng)變成了fulfilled了,因?yàn)橐妙愋褪前吹刂吩L問(wèn)的,當(dāng)我們點(diǎn)開的時(shí)候會(huì)發(fā)現(xiàn)指向這個(gè)地址里最后的數(shù)據(jù)是什么。普通對(duì)象同理。
如下所示,我們console的時(shí)候a對(duì)象的name還是a,但是我們點(diǎn)開后發(fā)現(xiàn)程序執(zhí)行完后a對(duì)象的實(shí)際name變成了b。
const a = { name: "a" };
console.log(a);
a.name = "b";

回歸正傳,我們發(fā)現(xiàn)p3里面有3個(gè)字段,[[Prototype]]我們很熟悉,這個(gè)是一個(gè)指向當(dāng)前對(duì)象原型的指針。在大多數(shù)游覽器中是可以通過(guò)__proto__訪問(wèn)到的。
我們嘗試著去訪問(wèn):
console.log(p3, 111); console.log(p3.__proto__); console.log(p3.__proto__ === Promise.prototype); // true

我們可以看到.then默認(rèn)返回的有3個(gè)字段,然后通過(guò)原型鏈來(lái)實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用:
- [[Prototype]]代表Promise的原型對(duì)象
- [[PromiseState]]代表Promise對(duì)象當(dāng)前的狀態(tài)
- [[PromiseResult]]代表Promise對(duì)象的值,分別對(duì)應(yīng)resolve或reject傳?的結(jié)果
本質(zhì)就是在我們調(diào)?這些?持鏈?zhǔn)秸{(diào)?的函數(shù)的結(jié)尾時(shí),他?返回了?個(gè)包含他??的對(duì)象或者是?個(gè)新的??,這些?式都可以實(shí)現(xiàn)鏈?zhǔn)秸{(diào)?。
中斷鏈?zhǔn)秸{(diào)用的方式:
中斷的?式可以使?拋出?個(gè)異?;蚍祷?個(gè)rejected狀態(tài)的Promise對(duì)象
鏈?zhǔn)秸{(diào)用的基本形式:
- 只要有then()并且觸發(fā)了resolve,整個(gè)鏈條就會(huì)執(zhí)?到結(jié)尾,這個(gè)過(guò)程中的第?個(gè)回調(diào)函數(shù)的參數(shù)是resolve傳?的值
- 后續(xù)每個(gè)函數(shù)都可以使?return返回?個(gè)結(jié)果,如果沒(méi)有返回結(jié)果的話下?個(gè)then中回調(diào)函數(shù)的參數(shù)就是undefined
- 返回結(jié)果如果是普通變量,那么這個(gè)值就是下?個(gè)then中回調(diào)函數(shù)的參數(shù)
- 如果返回的是?個(gè)Promise對(duì)象,那么這個(gè)Promise對(duì)象resolve的結(jié)果會(huì)變成下?次then中回調(diào)的函數(shù)的參數(shù)
- 如果then中傳?的不是函數(shù)或者未傳值,Promise鏈條并不會(huì)中斷then的鏈?zhǔn)秸{(diào)?,并且在這之前最后?次的返回結(jié)果,會(huì)直接進(jìn)?離它最近的正確的then中的回調(diào)函數(shù)作為參數(shù)
前面幾條我們都能懂,第5條什么意思的? 看下面代碼:
const p2 = new Promise((resolve, reject) => {
console.log(1);
resolve();
});
p2.then(() => {
console.log(2);
return 123;
})
.then()
.then("456")
.then((res) => {
console.log(res);
});

發(fā)現(xiàn)只打印了1 2 和 123,return的123進(jìn)入了最后一個(gè).then的回調(diào)函數(shù)中作為參數(shù)。
resolve和reject
至于resolve和reject,我們通過(guò)上面已經(jīng)知道了resolve和reject能夠更改Promise的狀態(tài),而Promise的狀態(tài)是不可逆的,且是私有的。所以我們必須在Promise內(nèi)部調(diào)用resolve或者reject。
當(dāng)然,resolve和reject也能夠傳入?yún)?shù),而傳入的參數(shù),會(huì)變?yōu)?then或.catch的回調(diào)函數(shù)中的參數(shù)。
那如果傳入一個(gè)Promise作為參數(shù)呢???
resolve()
實(shí)際上,如果在resolve中傳入一個(gè)promise,那它的行為就相當(dāng)于是一個(gè)空包裝。Promise.resolve()可以說(shuō)相當(dāng)于是一個(gè)冪等方法,會(huì)保留傳入期約的狀態(tài)。
let p = Promise.resolve(7); setTimeout(console.log, 0, p === Promise.resolve(p)); // true setTimeout(console.log, 0, p === Promise.resolve(Promise.resolve(p))); // true
reject()
會(huì)實(shí)例化一個(gè)拒絕的期約并拋出一個(gè)異步錯(cuò)誤,不能通過(guò)try...catch捕獲,只能通過(guò)拒絕處理程序捕獲。
如果給reject傳入一個(gè)promise,則這個(gè)promise會(huì)成為返回的拒絕promise的理由。
const p1 = new Promise(() => {});
const p2 = Promise.resolve(111);
const r3 = Promise.reject(p1);
const r4 = Promise.reject(p2);
console.log(r3);
console.log(r4);
Promise常用API——all()、allSettled()、any()、race()
all()
假如我們有一個(gè)需求,一個(gè)頁(yè)面需要請(qǐng)求3個(gè)接口才能渲染,并且要求3個(gè)接口必須全部返回。如果我們通過(guò)鏈?zhǔn)秸{(diào)用的方式,接口1請(qǐng)求了再去請(qǐng)求接口2然后去請(qǐng)求接口3,全都成功了再去渲染頁(yè)面。這種就很耗時(shí),所以就有了一個(gè)all的方法來(lái)解決。
Promise.all([promise對(duì)象,promise對(duì)象,...]).then(回調(diào)函數(shù))
Promise.all()的參數(shù)是一個(gè)Promise數(shù)組,只有數(shù)組中所有的Promise的狀態(tài)變成了fulfilled之后才會(huì)執(zhí)行.then回調(diào)的第一個(gè)回調(diào)函數(shù),并且將每個(gè)Promise結(jié)果的數(shù)組變?yōu)榛卣{(diào)函數(shù)的參數(shù)。如果Promise中有一個(gè)rejected,那么就會(huì)觸發(fā).catch()的回調(diào)。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第?個(gè)promise執(zhí)?完畢");
}, 1000);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第?個(gè)promise執(zhí)?完畢");
}, 2000);
});
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第三個(gè)promise執(zhí)?完畢");
}, 3000);
});
Promise.all([p1, p3, p2])
.then((res) => {
console.log(res);
})
.catch(function (err) {
console.log(err);
});
// 3s后打印?['第?個(gè)promise執(zhí)?完畢', '第三個(gè)promise執(zhí)?完畢', '第?個(gè)promise執(zhí)?完畢']race()
race()方法與all()方法的使用格式相同,不同的是,回調(diào)函數(shù)的參數(shù)是promise數(shù)組中最快執(zhí)行完畢的promise的返回值,它的狀態(tài)可能是fulfilled也有可能是rejected,但是是最快返回的。
根據(jù)race這個(gè)單詞就能理解,相當(dāng)于一群promise進(jìn)行比賽,誰(shuí)先到終點(diǎn)第一就是誰(shuí),不管是男是女。
//promise.race()相當(dāng)于將傳?的所有任務(wù)進(jìn)行一個(gè)競(jìng)爭(zhēng)
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第?個(gè)promise執(zhí)?完畢");
}, 5000);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("第?個(gè)promise執(zhí)?完畢");
}, 2000);
});
let p3 = new Promise((resolve) => {
setTimeout(() => {
resolve("第三個(gè)promise執(zhí)?完畢");
}, 3000);
});
Promise.race([p1, p3, p2])
.then((res) => {
console.log(res);
})
.catch(function (err) {
console.error(err);
});
// 2秒后打印第二個(gè)promise執(zhí)行完畢
allSettled()
該方法需要傳入所有不在pendding狀態(tài)的promise數(shù)組,然后通過(guò)該方法可以知道數(shù)組中的promise的當(dāng)前狀態(tài)。
當(dāng)有多個(gè)彼此不依賴的異步任務(wù)成功完成時(shí),或者總是想知道每個(gè)promise的結(jié)果時(shí),通常使用它。
const promise1 = Promise.resolve(3); const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo')); const promises = [promise1, promise2]; Promise.allSettled(promises). then((results) => results.forEach((result) => console.log(result.status))); // "fulfilled" // "rejected"
any()
這個(gè)方法目前還是實(shí)驗(yàn)性的,不是所有的游覽器都能夠支持。
接受一個(gè)promise數(shù)組,只要有一個(gè)promise的狀態(tài)變成了fulfilled,那么這個(gè)方法就會(huì)返回這個(gè)promise;
如果所有的promise的狀態(tài)都是rejected,那么就返回失敗的promise,并且把單一的錯(cuò)誤集合在一起。
const pErr = new Promise((resolve, reject) => {
reject("總是失敗");
});
const pSlow = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "最終完成");
});
const pFast = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "很快完成");
});
Promise.any([pErr, pSlow, pFast]).then((value) => {
console.log(value);
})
// 很快完成到此這篇關(guān)于深入學(xué)習(xí)JavaScript中的promise的文章就介紹到這了,更多相關(guān)JavaScript promise內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript字符串轉(zhuǎn)數(shù)字的簡(jiǎn)單實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于JavaScript字符串轉(zhuǎn)數(shù)字的簡(jiǎn)單實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
JavaScript中Array實(shí)例方法filter的實(shí)現(xiàn)原理
filter() 方法創(chuàng)建一個(gè)新數(shù)組,其中包含通過(guò)所提供函數(shù)實(shí)現(xiàn)的測(cè)試的所有元素,本文將給大家介紹JavaScript中Array實(shí)例方法filter的實(shí)現(xiàn)原理,文中通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下2024-03-03
JavaScript如何將偽數(shù)組轉(zhuǎn)換成數(shù)組?
這篇文章主要介紹了JavaScript如何將偽數(shù)組轉(zhuǎn)換成數(shù)組,?偽數(shù)組的主要特征是一個(gè)對(duì)象,并且該對(duì)象有l(wèi)ength屬性,更多參考內(nèi)容,需要的小伙伴可以參考一下2022-07-07
可視化埋點(diǎn)平臺(tái)元素曝光采集intersectionObserver思路實(shí)踐
這篇文章主要為大家介紹了可視化埋點(diǎn)平臺(tái)元素曝光采集的思路—intersectionObserver的實(shí)戰(zhàn)經(jīng)驗(yàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
JavaScript Window瀏覽器對(duì)象模型方法與屬性匯總
本文給大家匯總分享的是JavaScript Window瀏覽器對(duì)象模型方法與屬性,十分的細(xì)致全面,這里推薦給大家,有需要的小伙伴可以參考下。2015-04-04
JavaScript通過(guò)this變量快速找出用戶選中radio按鈕的方法
這篇文章主要介紹了JavaScript通過(guò)this變量快速找出用戶選中radio按鈕的方法,涉及javascript中this變量的使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-03-03

