JavaScript三大重點(diǎn)同步異步與作用域和閉包及原型和原型鏈詳解
如圖所示,JS的三座大山:
- 同步、異步
- 作用域、閉包
- 原型、原型鏈
1. 同步、異步
JavaScript執(zhí)行機(jī)制,重點(diǎn)有兩點(diǎn):
- JavaScript是一門單線程語言
- Event Loop(事件循環(huán))是JavaScript的執(zhí)行機(jī)制
JS為什么是單線程
最初設(shè)計(jì)JS是用來在瀏覽器驗(yàn)證表單操控DOM元素的是一門腳本語言,如果js是多線程的,那么兩個(gè)線程同時(shí)對(duì)一個(gè)DOM元素進(jìn)行了相互沖突的操作,那么瀏覽器的解析器是無法執(zhí)行的。
js為什么需要異步
如果js中不存在異步,只能自上而下執(zhí)行,如果上一行解析時(shí)間很長(zhǎng),那么下面的代碼就會(huì)被阻塞。
對(duì)于用戶而言,阻塞就以為著“卡死”,這樣就導(dǎo)致了很差的用戶體驗(yàn)。比如在進(jìn)行ajax請(qǐng)求的時(shí)候如果沒有返回?cái)?shù)據(jù)后面的代碼就沒辦法執(zhí)行
JS的事件循環(huán)(eventloop)是怎么運(yùn)作的
- 首先判斷JS是同步還是異步,同步就進(jìn)入主線程運(yùn)行,異步就進(jìn)入event table.
- 異步任務(wù)在event table中注冊(cè)事件,當(dāng)滿足觸發(fā)條件后,(觸發(fā)條件可能是延時(shí)也可能是ajax回調(diào)),被推入event queue
- 同步任務(wù)進(jìn)入主線程后一直執(zhí)行,直到主線程空閑時(shí),才會(huì)去event queue中查看是否有可執(zhí)行的異步任務(wù),如果有就推入主線程中。
如圖所示:
那怎么知道主線程執(zhí)行棧為空呢?js引擎存在monitoring process進(jìn)程,會(huì)持續(xù)不斷的檢查 主線程執(zhí)行棧是否為空,一旦為空,就會(huì)去event queue那里檢查是否有等待被調(diào)用的函數(shù)
宏任務(wù) 包含整個(gè)script代碼塊,setTimeout, setIntval
微任務(wù) Promise , process.nextTick
在劃分宏任務(wù)、微任務(wù)的時(shí)候并沒有提到async/ await的本質(zhì)就是Promise
setTimeout(function() { console.log('4') }) new Promise(function(resolve) { console.log('1') // 同步任務(wù) resolve() }).then(function() { console.log('3') }) console.log('2')
執(zhí)行結(jié)果: 1-2-3-4
1. 這段代碼作為宏任務(wù),進(jìn)入主線程。
2. 先遇到setTimeout,那么將其回調(diào)函數(shù)注冊(cè)后分發(fā)到宏任務(wù)event queue.
3. 接下來遇到Promise, new Promise立即執(zhí)行,then函數(shù)分發(fā)到微任務(wù)event queue
4. 遇到console.log(), 立即執(zhí)行
5. 整體代碼script作為第一個(gè)宏任務(wù)執(zhí)行結(jié)束, 查看當(dāng)前有沒有可執(zhí)行的微任務(wù),執(zhí)行then的回調(diào)。(第一輪事件循環(huán)結(jié)束了,我們開始第二輪循環(huán))
6. 從宏任務(wù)的event queue開始,我們發(fā)現(xiàn)了宏任務(wù)event queue中setTimeout對(duì)應(yīng)的回調(diào)函數(shù),立即執(zhí)行。
console.log('1') setTimeout(function() { console.log('2') process.nextTick(function() { console.log('3') }) new Promise(function(resolve) { console.log('4') resolve() }).then(function() { console.log('5') }) }) process.nextTick(function() { console.log('6') }) new Promise(function(resolve) { console.log('7') resolve() }).then(function() { console.log('8') }) setTimeout(function() { console.log('9') process.nextTick(function() { console.log('10') }) new Promise(function(resolve) { console.log('11') resolve() }).then(function() { console.log('12') }) })
1.整體script作為第一個(gè)宏任務(wù)進(jìn)入主線程,遇到console.log(1)輸出1
遇到setTimeout, 其回調(diào)函數(shù)被分發(fā)到宏任務(wù)event queue中。我們暫且記為setTimeout1
3.遇到process.nextTick(),其回調(diào)函數(shù)被分發(fā)到微任務(wù)event queue中,我們記為process1
4.遇到Promise, new Promise直接執(zhí)行,輸出7.then被分發(fā)到微任務(wù)event queue中,我們記為then1
又遇到setTimeout,其回調(diào)函數(shù)被分發(fā)到宏任務(wù)event queue中,我們記為setTimeout2.
現(xiàn)在開始執(zhí)行微任務(wù), 我們發(fā)現(xiàn)了process1和then1兩個(gè)微任務(wù),執(zhí)行process1,輸出6,執(zhí)行then1,輸出8, 第一輪事件循環(huán)正式結(jié)束, 這一輪的結(jié)果輸出1,7,6,8.那么第二輪事件循環(huán)從setTimeout1宏任務(wù)開始
5. 首先輸出2, 接下來遇到了process.nextTick(),統(tǒng)一被分發(fā)到微任務(wù)event queue,記為process2
8new Promise立即執(zhí)行,輸出4,then也被分發(fā)到微任務(wù)event queue中,記為then2
6. 現(xiàn)在開始執(zhí)行微任務(wù),我們發(fā)現(xiàn)有process2和then2兩個(gè)微任務(wù)可以執(zhí)行輸出3,5. 第二輪事件循環(huán)結(jié)束,第二輪輸出2,4,3,5. 第三輪事件循環(huán)從setTimeout2哄任務(wù)開始
10。 直接輸出9,跟第二輪事件循環(huán)類似,輸出9,11,10,12
7. 完整輸出是1,7,6,8,2,4,3,5,9,11,10,12(請(qǐng)注意,node環(huán)境下的事件監(jiān)聽依賴libuv與前端環(huán)境不完全相同,輸出順序可能會(huì)有誤差)
async/await用來干什么
用來優(yōu)化promise的回調(diào)問題,被稱為是異步的終極解決方案
async/await內(nèi)部做了什么
async函數(shù)會(huì)返回一個(gè)Promise對(duì)象,如果在函數(shù)中return一個(gè)直接量(普通變量),async會(huì)把這個(gè)直接量通過Promise.resolve()封裝成Promise對(duì)象。如果你返回了promise那就以你返回的promise為準(zhǔn)。await是在等待,等待運(yùn)行的結(jié)果也就是返回值。await后面通常是一個(gè)異步操作(promise),但是這不代表await后面只能跟異步才做,await后面實(shí)際是可以接普通函數(shù)調(diào)用或者直接量。
async相當(dāng)于 new Promise,await相當(dāng)于then
await的等待機(jī)制
如果await后面跟的不是一個(gè)promise,那await后面表達(dá)式的運(yùn)算結(jié)果就是它等到的東西,如果await后面跟的是一個(gè)promise對(duì)象,await它會(huì)’阻塞’后面的diamante,等著promise對(duì)象resolve,
然后得到resolve的值作為await表達(dá)式的運(yùn)算結(jié)果。但是此"阻塞"非彼“阻塞”,這就是await必須用在async函數(shù)中的原因。 async函數(shù)調(diào)用不會(huì)造成"阻塞",它內(nèi)部所有的“阻塞”都被封裝在一個(gè)promise對(duì)象中異步執(zhí)行(這里的阻塞理解成異步等待更合理)
async/await在使用過程中有什么規(guī)定
每個(gè)async方法都返回一個(gè)promise, await只能出現(xiàn)在async函數(shù)中
async/await在什么場(chǎng)景使用
單一的promise鏈并不能發(fā)現(xiàn)async/await的有事,但是如果需要處理由多個(gè)promise組成的then鏈的時(shí)候,優(yōu)勢(shì)就能體現(xiàn)出來了(Promise通過then鏈來解決多層回調(diào)的問題,現(xiàn)在又用async/awai來進(jìn)一步優(yōu)化它)
2. 作用域、閉包
閉包
- 閉包是指有權(quán)訪問另外一個(gè)函數(shù)作用域中的變量的函數(shù)(紅寶書)
- 閉包是指那些能夠訪問自由變量的函數(shù)。(MDN)其中自由變量, 指在函數(shù)中使用的,但既不是函數(shù)參數(shù)arguments也不是函數(shù)的局部變量的變量,其實(shí)就是另外一個(gè)函數(shù)作用域中的變量。)
作用域
說起閉包,就必須要說說作用域,ES5種只存在兩種作用域:
- 函數(shù)作用域。
- 全局作用域 當(dāng)訪問一個(gè)變量時(shí),解釋器會(huì)首先在當(dāng)前作用域查找標(biāo)示符,如果沒有找到, 就去父作用域找, 直到找到該變量的標(biāo)示符或者不在父作用域中
- 這就是作用域鏈,每一個(gè)子函數(shù)都會(huì)拷貝上級(jí)的作用域, 形成一個(gè)作用域的鏈條。
let a = 1; function f1() { var a = 2 function f2() { var a = 3; console.log(a); //3 } }
在這段代碼中,
- f1的作用域指向有全局作用域(window) 和它本身
- 而f2的作用域指向全局作用域(window)、 f1和它本身
- 而且作用域是從最底層向上找, 直到找到全局作用域window為止
- 如果全局還沒有的話就會(huì)報(bào)錯(cuò)。閉包產(chǎn)生的本質(zhì)就是
- 當(dāng)前環(huán)境中存在指向父級(jí)作用域的引用
function f2() { var a = 2 function f3() { console.log(a); //2 } return f3; } var x = f2(); x();
這里x會(huì)拿到父級(jí)作用域中的變量, 輸出2。
因?yàn)樵诋?dāng)前環(huán)境中,含有對(duì)f3的引用, f3恰恰引用了window、 f3和f3的作用域。
因此f3可以訪問到f2的作用域的變量。那是不是只有返回函數(shù)才算是產(chǎn)生了閉包呢?回到閉包的本質(zhì),只需要讓父級(jí)作用域的引用存在即可。
var f4; function f5() { var a = 2 f4 = function () { console.log(a); } } f5(); f4();
讓f5執(zhí)行,給f4賦值后,等于說現(xiàn)在f4擁有了window、f5和f4本身這幾個(gè)作用域的訪問權(quán),還是自底向上查找,最近是在f5中找到了a,因此輸出2。在這里是外面的變量f4存在著父級(jí)作用域的引用,
因此產(chǎn)生了閉包,形式變了,本質(zhì)沒有改變。
場(chǎng)景
- 返回一個(gè)函數(shù)
- 作為函數(shù)參數(shù)傳遞
- 在定時(shí)器、 事件監(jiān)聽、 Ajax請(qǐng)求、 跨窗口通信、 Web Workers或者任何異步中,只要使用了回調(diào)函數(shù), 實(shí)際上就是在使用閉包。
IIFE(立即執(zhí)行函數(shù)表達(dá)式) 創(chuàng)建閉包, 保存了全局作用域window和當(dāng)前函數(shù)的作用域。
var b = 1; function foo() { var b = 2; function baz() { console.log(b); } bar(baz); } function bar(fn) { // 這就是閉包 fn(); } // 輸出2,而不是1 foo(); // 以下的閉包保存的僅僅是window和當(dāng)前作用域。 // 定時(shí)器 setTimeout(function timeHandler() { console.log('111'); }, 100) // 事件監(jiān)聽 // document.body.click(function () { // console.log('DOM Listener'); // }) // 立即執(zhí)行函數(shù) var c = 2; (function IIFE() { // 輸出2 console.log(c); })();
經(jīng)典的一道題
for (var i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i) }, 0) } // 6 6 6 6 6 6 // 為什么會(huì)全部輸出6? 如何改進(jìn), 讓它輸出1, 2, 3, 4, 5?
解析:
- 因?yàn)閟etTimeout為宏任務(wù), 由于JS中單線程eventLoop機(jī)制, 在主線程同步任務(wù)執(zhí)行完后才去執(zhí)行宏任務(wù)。
- 因此循環(huán)結(jié)束后setTimeout中的回調(diào)才依次執(zhí)行, 但輸出i的時(shí)候當(dāng)前作用域沒有。
往上一級(jí)再找,發(fā)現(xiàn)了i,此時(shí)循環(huán)已經(jīng)結(jié)束,i變成了6,因此會(huì)全部輸出6。
利用IIFE(立即執(zhí)行函數(shù)表達(dá)式)當(dāng)每次for循環(huán)時(shí),把此時(shí)的i變量傳遞到定時(shí)器中
for (var i = 0; i < 5; i++) { (function (j) { setTimeout(() => { console.log(j) }, 1000); })(i) }
給定時(shí)器傳入第三個(gè)參數(shù), 作為timer函數(shù)的第一個(gè)函數(shù)參數(shù)
for (var i = 0; i < 5; i++) { setTimeout(function (j) { console.log(j) }, 1000, i); }
使用ES6中的let
- let使JS發(fā)生革命性的變化, 讓JS有函數(shù)作用域變?yōu)榱藟K級(jí)作用域
- 用let后作用域鏈不復(fù)存在。 代碼的作用域以塊級(jí)為單位,
for (let i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i) }, 2000) }
3. 原型、原型鏈
原型(prototype)
JS中所有函數(shù)都會(huì)有prototype屬性,只有函數(shù)才有
其所有的屬性和方法都能被構(gòu)造函數(shù)的實(shí)例對(duì)象共享訪問
代碼如下:
function Person(name){ this.name = name } Person.prototype.sayHello(){ console.log('sayHello') } let p1 = new Person(); let p2 = new Person(); console.log(p1.sayHello) //sayHello console.log(p2.sayHello) //sayHello
構(gòu)造函數(shù)(constructor)
JS中constructor存在每個(gè)函數(shù)的prototype屬性中,其保存了指向該函數(shù)的引用
Person.prototype.constructor ==Person //true
原型鏈(_ _ proto _ _)
JS中對(duì)象都會(huì)有個(gè)內(nèi)置屬性,即__proto__,(隱式原型鏈的屬性),一般情況下執(zhí)行創(chuàng)建它的構(gòu)造函數(shù)的prototype的屬性,另外函數(shù)比較特殊,也會(huì)有該屬性
p1.__proto__ == Person.prototype
JS 引擎查找摸個(gè)屬性時(shí),先查找對(duì)象本身是否存在該屬性,如果不存在就會(huì)在原型鏈上一層一層進(jìn)行查找
有幾個(gè)面試經(jīng)常會(huì)問的幾個(gè)問題
如何精確地判斷短數(shù)組的類型
[] instanceof Array //[].__proto__ == Array.prototype Object.prototype.toString.call([]) //[Object Array] Array.isArray([]) //true [].constructor ==Array
下面代碼輸出什么
Object instanceof Function //true Function instanceof Object // true
實(shí)現(xiàn)一個(gè)原型鏈繼承
function Person(name){ this.name = name } Person.prototype.sayHello(){ console.log('sayHello') } function Boy(){}; Boy.prototype = new Person(); let b1 = new Boy(); b1.sayHello() //sayHello
原型、原型鏈、構(gòu)造函數(shù)、實(shí)例的關(guān)系
1.instanceof檢測(cè)構(gòu)造函數(shù)與實(shí)例的關(guān)系:
function Person () {.........} person = new Person () res = person instanceof Person res // true
2.實(shí)例繼承原型上的定義的屬性:
function Person () {........} Person.prototype.type = 'object n' person = new Person () res = person.type res // object n
3.實(shí)例訪問 ===> 原型
實(shí)例通過__proto__訪問到原型 person.proto=== Person.prototype
4.原型訪問 ===> 構(gòu)造函數(shù)
原型通過constructor屬性訪問構(gòu)造函數(shù) Person.prototype.constructor === Person
5.實(shí)例訪問===>構(gòu)造函數(shù)
person.proto.constructor === Person
原型鏈
在讀取一個(gè)實(shí)例的屬性的過程中,如果屬性在該實(shí)例中沒有找到,那么就會(huì)循著 proto 指定的原型上去尋找,如果還找不到,則尋找原型的原型:
實(shí)例上尋找
function Person() {} Person.prototype.type = "object name Person"; person = new Person(); person.type = "我是實(shí)例的自有屬性"; res = Reflect.ownKeys(person); //嘗試獲取到自有屬性 console.log(res); res = person.type; console.log(res); //我是實(shí)例的自有屬性(通過原型鏈向上搜索優(yōu)先搜索實(shí)例里的)
原型上尋找
function Person() {} Person.prototype.type = "object name Person"; person = new Person(); res = Reflect.ownKeys(person); //嘗試獲取到自有屬性 console.log(res); res = person.type; console.log(res); //object name Person
原型的原型上尋找
function Person() {} Person.prototype.type = "object name Person"; function Child() {} Child.prototype = new Person(); p = new Child(); res = [p instanceof Object, p instanceof Person, p instanceof Child]; console.log(res); //[true, true, true] p同時(shí)屬于Object,Person, Child res = p.type; //層層搜索 console.log(res); //object name Person (原型鏈上搜索) console.dir(Person); console.dir(Child);
原型鏈上搜索
原型同樣也可以通過 proto 訪問到原型的原型,比方說這里有個(gè)構(gòu)造函數(shù) Child 然后“繼承”前者的有一個(gè)構(gòu)造函數(shù) Person,然后 new Child 得到實(shí)例 p;
當(dāng)訪問 p 中的一個(gè)非自有屬性的時(shí)候,就會(huì)通過 proto 作為橋梁連接起來的一系列原型、原型的原型、原型的原型的原型直到 Object 構(gòu)造函數(shù)為止;
原型鏈搜索搜到 null 為止,搜不到那訪問的這個(gè)屬性就停止:
function Person() {} Person.prototype.type = "object name Person"; function Child() {} Child.prototype = new Person(); p = new Child(); res = p.__proto__; console.log(res); //Person {} res = p.__proto__.__proto__; console.log(res); //Person {type:'object name Person'} res = p.__proto__.__proto__.__proto__; console.log(res); //{.....} res = p.__proto__.__proto__.__proto__.__proto__; console.log(res); //null
繼承
- JS 中一切皆對(duì)象(所有的數(shù)據(jù)類型都可以用對(duì)象來表示),必須有一種機(jī)制,把所有的對(duì)象聯(lián)系起來,實(shí)現(xiàn)類似的“繼承”機(jī)制。
- 不同于大部分面向?qū)ο笳Z言,ES6 之前并沒有引入類(class)的概念,JS 并非通過類而是通過構(gòu)造函數(shù)來創(chuàng)建實(shí)例,javascript中的繼承是通過原型鏈來體現(xiàn)的。
- 其基本思想是利用原型讓一個(gè)引用類型繼承另一個(gè)引用繼承的屬性和方法。
什么是繼承
- js中,繼承是一種允許我們?cè)谝延蓄惖幕A(chǔ)上創(chuàng)建新類的機(jī)制;它可以使用現(xiàn)有類的所有功能,并在無需重新編寫
- 原來的類的情況下對(duì)這些功能進(jìn)行擴(kuò)展。
為什么要有繼承
提高代碼的重用性、較少代碼的冗余
目前我總結(jié)的一共有6種繼承方式
- 原型鏈繼承
- 借用構(gòu)造函數(shù)繼承
- 組合式繼承(原型鏈+構(gòu)造函數(shù))
- 原型式繼承
- 寄生式繼承
- 寄生組合式繼承
function Person(name){ this.name = name; this.sum=function(){ alert('this.name',this.name) } } Person.prototype.age = 100
原型鏈繼承
實(shí)現(xiàn)方式: 利用原型鏈的特點(diǎn)繼承,讓實(shí)例的原型等于父類的實(shí)例
優(yōu)點(diǎn): 實(shí)例可以繼承父類的構(gòu)造個(gè)函數(shù),實(shí)例的構(gòu)造函數(shù),父類的原型
缺點(diǎn): 不能向父類傳遞參數(shù),由于實(shí)例的原型等于父類的實(shí)例,那么改變父類的屬性,實(shí)例的屬性也會(huì)跟著改變
function child(){ this.name="xiaoming" } child.prototype = new Person() let child1 = new Child() child1.name //xiaoming child1.age //100 child1 instanceof Person //true
借用構(gòu)造函數(shù)繼承
實(shí)現(xiàn)方式: 使用call/apply將父類的構(gòu)造函數(shù)引入子類函數(shù)
優(yōu)點(diǎn): 可以禰補(bǔ)原型鏈繼承的缺點(diǎn),可以向父類傳遞參數(shù),只繼承父類構(gòu)造函數(shù)的屬性
缺點(diǎn): 不能復(fù)用,每次使用需要重新調(diào)用,每個(gè)實(shí)例都是父類構(gòu)造函數(shù)的副本,比較臃腫
function child(){ Person.call(this,'xiaoming') } let child1 = new child() child1.name //xiaoming child1.age //100 child1 instanceof Person //false
組合式繼承
實(shí)現(xiàn)方式: 復(fù)用+可傳遞參數(shù)
優(yōu)點(diǎn): 基于原型鏈的優(yōu)點(diǎn)和借用構(gòu)造函數(shù)的優(yōu)點(diǎn)
缺點(diǎn): 調(diào)用兩遍父類函數(shù)
function child(){ Person.call(this,'xiaoming') } child.prototype = new Person let child1 = new child() child1.name //xiaoming child1.age //100 child1 instanceof Person //true child instanceof Person //false
原型式繼承
實(shí)現(xiàn)方式: 函數(shù)包裝對(duì)象,返回對(duì)象的引用,這個(gè)函數(shù)就變成可以隨時(shí)添加實(shí)例或者對(duì)象,Object.create()就是這個(gè)原理
優(yōu)點(diǎn): 復(fù)用一個(gè)對(duì)象用函數(shù)包裝
缺點(diǎn): 所有實(shí)例都繼承在原型上面 無法復(fù)用
function child(obj){ function F(){} F.prototype = obj return new F() } let child1 = new Person() let child2 = child(child1) child2.age //100
寄生式繼承
實(shí)現(xiàn)方式: 在原型式繼承外面包了一個(gè)殼子
優(yōu)點(diǎn): 創(chuàng)建一個(gè)新對(duì)象
缺點(diǎn): 沒有用到實(shí)例 無法復(fù)用
function child(obj){ function F(){} F.prototype = obj return new F() } let child1 = new Person() function subObject(){ let sub =child(child1) sub.name='xiaoming' return sub } let child2 = subObject(child1) typeof subObject //function typeof child2 //object child2.age //100
寄生組合式繼承
實(shí)現(xiàn)方式: 在函數(shù)內(nèi)返回對(duì)象的調(diào)用
優(yōu)點(diǎn): 函數(shù)的實(shí)例等于另外的一個(gè)實(shí)例,使用call/apply引入另一個(gè)構(gòu)造函數(shù),可傳遞參數(shù),修復(fù)了組合繼承的問題
缺點(diǎn): 無法復(fù)用
function child(obj){ function F(){} F.prototype = obj return new F() } let child1 = child(Person.prototype) function Sub(){ Person.call(this) } Sub.prototype = child child.constructor = Sub let sub1 = new Sub() sub1.age //100
到此這篇關(guān)于JavaScript三大重點(diǎn)同步異步與作用域和閉包及原型和原型鏈詳解的文章就介紹到這了,更多相關(guān)JavaScript 同步異步內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
webpack項(xiàng)目使用eslint建立代碼規(guī)范實(shí)現(xiàn)
這篇文章主要介紹了webpack項(xiàng)目使用eslint建立代碼規(guī)范實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05js自定義方法通過隱藏iframe實(shí)現(xiàn)文件下載
通過隱藏iframe實(shí)現(xiàn)文件下載,不可思議吧,但確實(shí)是可以實(shí)現(xiàn)的,不懂的看下代碼,或許可以幫助到你,而且此功能是用js實(shí)現(xiàn)的,感興趣的你可不要錯(cuò)過了哈2013-02-02JS 打印功能代碼可實(shí)現(xiàn)打印預(yù)覽、打印設(shè)置等
一個(gè)不錯(cuò)的JS 打印功能代碼,包括打印預(yù)覽、打印設(shè)置等,里面整合了很多知識(shí),是一個(gè)不錯(cuò)的學(xué)習(xí)案例2014-10-10微信小程序使用for循環(huán)動(dòng)態(tài)渲染頁(yè)面操作示例
這篇文章主要介紹了微信小程序使用for循環(huán)動(dòng)態(tài)渲染頁(yè)面操作,結(jié)合實(shí)例形式分析了微信小程序使用for語句獲取data數(shù)據(jù)渲染頁(yè)面相關(guān)操作技巧,需要的朋友可以參考下2018-12-12如何基于filter實(shí)現(xiàn)網(wǎng)站整體變灰功能
這篇文章主要介紹了如何基于filter實(shí)現(xiàn)網(wǎng)站整體變灰功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04基于Marquee.js插件實(shí)現(xiàn)的跑馬燈效果示例
這篇文章主要介紹了基于Marquee.js插件實(shí)現(xiàn)的跑馬燈效果,結(jié)合實(shí)例形式給出了Marquee.js插件的定義及具體使用方法,需要的朋友可以參考下2017-01-01