摘要:作用域鏈,是由當(dāng)前環(huán)境與上層環(huán)境的一系列變量對(duì)象組成,它保證了當(dāng)前執(zhí)行環(huán)境對(duì)符合訪問(wèn)權(quán)限的變量和函數(shù)的有序訪問(wèn)。
span的display值,文本example的顏色
example
其實(shí)瀏覽器中,這張圖的排列順序,就很好的表示出了這個(gè)demo中的優(yōu)先級(jí)關(guān)系:
優(yōu)先級(jí)關(guān)系:內(nèi)聯(lián)樣式 > ID 選擇器 > 類選擇器 = 屬性選擇器 = 偽類選擇器 > 標(biāo)簽選擇器 = 偽元素選擇器。 ??!important是個(gè)例外,優(yōu)先級(jí)最高。
更詳細(xì)的CSS優(yōu)先級(jí)請(qǐng)查看MDN-優(yōu)先級(jí)是如何計(jì)算的?
寫一個(gè)滿屏的品字這就是考驗(yàn)一個(gè)布局的能力,沒(méi)什么好說(shuō)的,辦法很多。我用的flex打個(gè)樣。
如下代碼,寫出執(zhí)行結(jié)果class="top"
class="left"
class="right"
var fun = function(arr) { for(var i = 0; i< arr.length;i++) { setTimeout(function() { console.log(i); },0) } console.log(arr[i]) } fun([1,2,3,4])
直接寫答案就沒(méi)什么意思了,借這個(gè)題先扯一下執(zhí)行上下文、作用域、作用域鏈、閉包。
執(zhí)行上下文以下demo、圖示、結(jié)論絕大部分來(lái)自這個(gè)網(wǎng)站,推薦閱讀!在這里引用是為了讓大家更好的理解,我確實(shí)講不了這么好?。。?/pre>一段JavaScript的代碼執(zhí)行的時(shí)候,都會(huì)產(chǎn)生一個(gè)執(zhí)行上下文(也就是執(zhí)行環(huán)境)。多段代碼執(zhí)行就會(huì)產(chǎn)生多個(gè)執(zhí)行上下文。
console.log(1); // 這段代碼的執(zhí)行上下文就是--全局環(huán)境function test() { console.log("test"); } test(); // test() 執(zhí)行上下文就是test--函數(shù)環(huán)境JavaScript中的運(yùn)行環(huán)境大概包括三種情況:
全局環(huán)境:JavaScript代碼運(yùn)行起來(lái)會(huì)首先進(jìn)入該環(huán)境
函數(shù)環(huán)境:當(dāng)函數(shù)被調(diào)用執(zhí)行時(shí),會(huì)進(jìn)入當(dāng)前函數(shù)中執(zhí)行代碼
eval(不建議使用,可忽略)
??JavaScript引擎會(huì)以棧的形式來(lái)處理這些執(zhí)行上下文,棧底永遠(yuǎn)都是全局上下文,而棧頂就是當(dāng)前正在執(zhí)行的上下文。
看下面這個(gè)demo,相信大家一看就懂了:
var color = "blue"; function changeColor() { var anotherColor = "red"; function swapColors() { var tempColor = anotherColor; anotherColor = color; color = tempColor; } swapColors(); } changeColor();這里面有全局上下文(Global Context)、changeColor()上下文、swapColors()上下文,它們進(jìn)棧出棧如下圖:
每一個(gè)執(zhí)行上下文都有自己的生命周期:
對(duì)執(zhí)行上下文總結(jié)一些結(jié)論:
單線程
同步執(zhí)行,只有棧頂?shù)纳舷挛奶幱趫?zhí)行中,其他上下文需要等待
全局上下文只有唯一的一個(gè),它在瀏覽器關(guān)閉時(shí)出棧
函數(shù)的執(zhí)行上下文的個(gè)數(shù)沒(méi)有限制
每次某個(gè)函數(shù)被調(diào)用,就會(huì)有個(gè)新的執(zhí)行上下文為其創(chuàng)建,即使是調(diào)用的自身函數(shù),也是如此。
作用域、作用域鏈與閉包作用域與執(zhí)行上下文是完全不同的兩個(gè)概念。
JavaScript代碼的整個(gè)執(zhí)行過(guò)程,分為兩個(gè)階段,代碼編譯階段與代碼執(zhí)行階段。編譯階段由編譯器完成,將代碼翻譯成可執(zhí)行代碼,這個(gè)階段作用域規(guī)則會(huì)確定。執(zhí)行階段由引擎完成,主要任務(wù)是執(zhí)行可執(zhí)行代碼,執(zhí)行上下文在這個(gè)階段創(chuàng)建。??JavaScript中只有全局作用域與函數(shù)作用域(因?yàn)閑val我們平時(shí)開(kāi)發(fā)中幾乎不會(huì)用到它,這里不討論)。作用域鏈,是由當(dāng)前環(huán)境與上層環(huán)境的一系列變量對(duì)象組成,它保證了當(dāng)前執(zhí)行環(huán)境對(duì)符合訪問(wèn)權(quán)限的變量和函數(shù)的有序訪問(wèn)。
看一個(gè)demo:
var a = 20; function test() { var b = a + 10; function innerTest() { var c = 10; return b + c; } return innerTest(); } test();在上面的例子中,全局,函數(shù)test,函數(shù)innerTest的執(zhí)行上下文先后創(chuàng)建。我們?cè)O(shè)定他們的變量對(duì)象分別為VO(global),VO(test), VO(innerTest)。而innerTest的作用域鏈,則同時(shí)包含了這三個(gè)變量對(duì)象,所以innerTest的執(zhí)行上下文可如下表示。
innerTestEC = { VO: {...}, // 變量對(duì)象 scopeChain: [VO(innerTest), VO(test), VO(global)], // 作用域鏈 }至于這里面的VO AO 有興趣的可以去上面那個(gè)網(wǎng)站里看看,這里不提,我覺(jué)得不妨礙大家理解。簡(jiǎn)單說(shuō)就是,在innerTest這個(gè)方法內(nèi),能拿到test()方法中的變量,也能拿到全局環(huán)境中的變量,這就形成了一個(gè)作用域鏈。
看到這里相信大家都知道了,閉包不就是這個(gè)東東嘛。
它由兩部分組成。執(zhí)行上下文(代號(hào)A),以及在該執(zhí)行上下文中創(chuàng)建的函數(shù)(代號(hào)B)。
當(dāng)B執(zhí)行時(shí),如果訪問(wèn)了A中變量對(duì)象中的值,那么閉包就會(huì)產(chǎn)生。JavaScript擁有自動(dòng)的垃圾回收機(jī)制,關(guān)于垃圾回收機(jī)制,有一個(gè)重要的行為,那就是,當(dāng)一個(gè)值,在內(nèi)存中失去引用時(shí),垃圾回收機(jī)制會(huì)根據(jù)特殊的算法找到它,并將其回收,釋放內(nèi)存。
而我們知道,函數(shù)的執(zhí)行上下文,在執(zhí)行完畢之后,生命周期結(jié)束,那么該函數(shù)的執(zhí)行上下文就會(huì)失去引用。其占用的內(nèi)存空間很快就會(huì)被垃圾回收器釋放??墒情]包的存在,會(huì)阻止這一過(guò)程。
setTimeout繞了一圈回到這個(gè)題,這個(gè)題中的setTimeout又在何時(shí)執(zhí)行呢?
在這里,將會(huì)介紹另外一個(gè)特殊的隊(duì)列結(jié)構(gòu),頁(yè)面中所有由setTimeout定義的操作,都將放在同一個(gè)隊(duì)列中依次執(zhí)行。
而這個(gè)隊(duì)列執(zhí)行的時(shí)間,需要等待到函數(shù)調(diào)用棧清空之后才開(kāi)始執(zhí)行。即所有可執(zhí)行代碼執(zhí)行完畢之后,才會(huì)開(kāi)始執(zhí)行由setTimeout定義的操作。而這些操作進(jìn)入隊(duì)列的順序,則由設(shè)定的延遲時(shí)間來(lái)決定。
這個(gè)題中循環(huán)4次,每次往隊(duì)列里加入一個(gè)console.log(i),它們引用的都是同一個(gè)i在循環(huán)結(jié)束時(shí),i已經(jīng)變成4了。
console.log(arr[i])就是undefined。
答案: undefined , 4 ,4 ,4 ,4如下代碼,寫出執(zhí)行結(jié)果function person(name) { if(name) { this.name = name; } console.log(this.name); } person.prototype.name = "Tom"; var human = { person: person, name: "Cat" } person(); person("Jack"); new person(); new person("Rose"); human.person(); person.call(window)person(), 作為函數(shù)直接調(diào)用,this指向window,this.name = window.name=undefined
person("Jack") ,跟上面一樣,this指向window,this.name = window.name=name="Jack"
new person() ,作為構(gòu)造函數(shù)調(diào)用,this指向新生成的對(duì)象,在自身沒(méi)有找到this.name就會(huì)沿著原型鏈查找,所以this.name = person.prototype.name=Tom
new person("Rose"),與上面類似,區(qū)別在于傳了name
human.person(),作為對(duì)象方法調(diào)用,this指向human=>human.name = "Cat"
person.call(window),用call方法將this指向window,??這里最容易錯(cuò)?,person("Jack") 已經(jīng)將window.name="Jack"
答案: undefined、Jack、Tom、Rose、Cat、Jack如下代碼,寫出執(zhí)行結(jié)果var a = window.a = "finget.github.io" function hello(){ console.log(a); var a = "hello"; console.log(a); console.log(b); let b = "finget"; } hello();這個(gè)題比較簡(jiǎn)單,主要涉及的就是變量提升,和作用域鏈??狱c(diǎn)就是第一個(gè)console.log(a)到底是打印undefined還是finget.github.io。
再看看作用域鏈那張圖:
在hello()方法中定義了一個(gè)var a = "hello",雖然在剛執(zhí)行的時(shí)候,根據(jù)變量提升原則,a=undefined,但是它還是很有骨氣的,只要自己有絕不往上找。那如果換成let a = "hello"呢?
來(lái)來(lái)來(lái)試一試:
var a = window.a = "finget.github.io" function hello(){ console.log(a); let a = "hello"; console.log(a); console.log(b); let b = "finget"; } hello();暫時(shí)性死區(qū)只要塊級(jí)作用域內(nèi)存在let命令,它所聲明的變量就“綁定”(binding)這個(gè)區(qū)域,不再受外部的影響。
var tmp = 123; if (true) { tmp = "abc"; // ReferenceError let tmp; }上面代碼中,存在全局變量tmp,但是塊級(jí)作用域內(nèi)let又聲明了一個(gè)局部變量tmp,導(dǎo)致后者綁定這個(gè)塊級(jí)作用域,所以在let聲明變量前,對(duì)tmp賦值會(huì)報(bào)錯(cuò)。
上面的結(jié)果就很清楚了,直接報(bào)錯(cuò),后面的也不執(zhí)行。
提取url的參數(shù),以key-value形式返回完全考正則,自己惡補(bǔ)吧。沒(méi)辦法!
let getSearch = function(url) { let matched = /^(?:https?://[^?]*?)(.*)/gi.exec(url) return matched ? matched[1] : "" } // 遞歸函數(shù),循環(huán)匹配search let searchFn = function (search, query) { if (search) { let matched = /(w+)=(w*)/g.exec(search) if (matched) { query[matched[1]] = decodeURIComponent(matched[2]) searchFn(search.slice(matched.index + matched[0].length), query) } } } let parseUrl = function (url) { let query = {} searchFn(getSearch(url), query) return query } let url = "http://localhost:3009/h5/test?recordID=161851&order=2" console.log(parseUrl(url)) // => { recordID: "161851", order: "2" }判斷一個(gè)字符串中出現(xiàn)最多的字符,統(tǒng)計(jì)次數(shù)function maxStr(str) { let map = {} for(let v of str) { map[v] = ~~map[v] + 1 } // 這里就類似這種結(jié)構(gòu) map={a:1,b:1}, ~~map[v]就類似parseInt(),如果某一個(gè)字符第一出現(xiàn)就是0,=> 0+1,以此類推! // Object.values 能將一個(gè)對(duì)象的value返回成一個(gè)數(shù)組,再去最大值 let max = Math.max(...Object.values(map)) for (let key in map) { if (map[key] == max){ return {[key]: max} } } } let str = "aasdfasd,asdfjaslkdfjiqjwioaklsdf,asd,lqwejrio1ji3wioqjroiqqewslkasm" console.log(maxStr(str))按位非運(yùn)算符“~” 先看看w3c的定義: 位運(yùn)算 NOT 由否定號(hào)(~)表示,它是 ECMAScript 中為數(shù)不多的與二進(jìn)制算術(shù)有關(guān)的運(yùn)算符之一。 位運(yùn)算 NOT 是三步的處理過(guò)程: 把運(yùn)算數(shù)轉(zhuǎn)換成 32 位數(shù)字 把二進(jìn)制數(shù)轉(zhuǎn)換成它的二進(jìn)制反碼(0->1, 1->0) 把二進(jìn)制數(shù)轉(zhuǎn)換成浮點(diǎn)數(shù) 簡(jiǎn)單的理解,對(duì)任一數(shù)值 x 進(jìn)行按位非操作的結(jié)果為 -(x + 1) console.log("~null: ", ~null); // => -1 console.log("~undefined: ", ~undefined); // => -1 console.log("~0: ", ~0); // => -1 console.log("~{}: ", ~{}); // => -1 console.log("~[]: ", ~[]); // => -1 console.log("~(1/0): ", ~(1/0)); // => -1 console.log("~false: ", ~false); // => -1 console.log("~true: ", ~true); // => -2 console.log("~1.2543: ", ~1.2543); // => -2 console.log("~4.9: ", ~4.9); // => -5 console.log("~(-2.999): ", ~(-2.999)); // => 1 那么, ~~x就為 -(-(x+1) + 1) 相當(dāng)于是 parseInt() console.log("~~null: ", ~~null); // => 0 console.log("~~undefined: ", ~~undefined); // => 0 console.log("~~0: ", ~~0); // => 0 console.log("~~{}: ", ~~{}); // => 0 console.log("~~[]: ", ~~[]); // => 0 console.log("~~(1/0): ", ~~(1/0)); // => 0 console.log("~~false: ", ~~false); // => 0 console.log("~~true: ", ~~true); // => 1 console.log("~~1.2543: ", ~~1.2543); // => 1 console.log("~~4.9: ", ~~4.9); // => 4 console.log("~~(-2.999): ", ~~(-2.999)); // => -2實(shí)現(xiàn)一個(gè)拷貝函數(shù) JSON.parse()const newObj = JSON.parse(JSON.stringify(oldObj));?1.他無(wú)法實(shí)現(xiàn)對(duì)函數(shù) 、RegExp等特殊對(duì)象的克隆比較完善的深拷貝
2.會(huì)拋棄對(duì)象的constructor,所有的構(gòu)造函數(shù)會(huì)指向Object
3.對(duì)象有循環(huán)引用,會(huì)報(bào)錯(cuò)我覺(jué)得面試手寫這個(gè)也太那啥了!const isType = (obj, type) => { if (typeof obj !== "object") return false; const typeString = Object.prototype.toString.call(obj); let flag; switch (type) { case "Array": flag = typeString === "[object Array]"; break; case "Date": flag = typeString === "[object Date]"; break; case "RegExp": flag = typeString === "[object RegExp]"; break; default: flag = false; } return flag; }; const getRegExp = re => { var flags = ""; if (re.global) flags += "g"; if (re.ignoreCase) flags += "i"; if (re.multiline) flags += "m"; return flags; }; const clone = parent => { // 維護(hù)兩個(gè)儲(chǔ)存循環(huán)引用的數(shù)組 const parents = []; const children = []; const _clone = parent => { if (parent === null) return null; if (typeof parent !== "object") return parent; let child, proto; if (isType(parent, "Array")) { // 對(duì)數(shù)組做特殊處理 child = []; } else if (isType(parent, "RegExp")) { // 對(duì)正則對(duì)象做特殊處理 child = new RegExp(parent.source, getRegExp(parent)); if (parent.lastIndex) child.lastIndex = parent.lastIndex; } else if (isType(parent, "Date")) { // 對(duì)Date對(duì)象做特殊處理 child = new Date(parent.getTime()); } else { // 處理對(duì)象原型 proto = Object.getPrototypeOf(parent); // 利用Object.create切斷原型鏈 child = Object.create(proto); } // 處理循環(huán)引用 const index = parents.indexOf(parent); if (index != -1) { // 如果父數(shù)組存在本對(duì)象,說(shuō)明之前已經(jīng)被引用過(guò),直接返回此對(duì)象 return children[index]; } parents.push(parent); children.push(child); for (let i in parent) { // 遞歸 child[i] = _clone(parent[i]); } return child; }; return _clone(parent); };查找素?cái)?shù) 試除法這種方式很傳統(tǒng)理解上也簡(jiǎn)單,給定一個(gè)范圍,那么就逐個(gè)循環(huán)去試除小于它數(shù)。
現(xiàn)在我們假設(shè) N 等于 120
let N = 120; let primes = []; // 用于存素?cái)?shù)結(jié)果集 loop:for(let x=2;x<=N;x++){ for(let k=2;k篩法 先把所有2的倍數(shù)去掉,然后剩下的那些數(shù)里面,最小的是3,3就是素?cái)?shù),然后把3的倍數(shù)都去掉,剩下的數(shù)里面,最小的是5,所以5也是素?cái)?shù)…(可以看出已跳過(guò)4的試除,越多到后面跳過(guò)的數(shù)越多)
上述過(guò)程依次進(jìn)行,但不像試除法逐個(gè)進(jìn)行,就可以把某個(gè)范圍內(nèi)的非素?cái)?shù)全都除去,剩下的就是素?cái)?shù)了。這種方式的好處在于運(yùn)算不重復(fù),高效。
有一張很形象的動(dòng)畫,能直觀地體現(xiàn)出篩法的工作過(guò)程。 (非素?cái)?shù)就像被篩子篩掉一樣)
let N = 120; let primes = []; // 用于存素?cái)?shù)結(jié)果集 let nums = []; // 待篩選的數(shù)據(jù)集 for(let x=2;x<=N;x++){ //hooyes提示:此處初始化的時(shí)候,也可直接篩掉2的倍數(shù)數(shù)據(jù)減半。 //if(x%2!==0) nums.push(x); } // 遞歸函數(shù) function PrimeFn(data){ let p = data.shift(); // 數(shù)組最前端的一個(gè)數(shù)即素?cái)?shù),拿出來(lái)存起,并作為下次篩除的分母。 primes.push(p); let t = []; for(let v of data){ v%p!==0 ? t.push(v) : "" // 能被 p 整除的都篩除掉,不能整除的放到臨時(shí)數(shù)組t存起來(lái)。 } // t 是下次待篩數(shù)組,元素個(gè)數(shù)會(huì)越來(lái)越少,若還有就進(jìn)行一次遞歸。 t.length>0 ? PrimeFn(t) : "" } PrimeFn(nums); console.log(primes.join(",")); /* 得到小于N的素?cái)?shù)集合 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113 */原文地址(https://hooyes.net/p/javascri...[https://hooyes.net/p/javascript-prime-number]處理金額/** * 金額三位一劃分 * @param {[string/number]} money [金額] * @param {[string/number]} round [小數(shù)位] * @param {[any]} flag [是否四舍五入] * @return {[type]} [description] */ function formatMoney(money,round,flag) { money = Number(money); round = Number(round); let formatReg = /(d)(?=(d{3})+.)/g; let sliceReg = new RegExp (`([0-9]+.[0-9]{${round}})[0-9]*`); if(!isNaN(money)&&Object.prototype.toString.call(money).slice(8,-1) === "Number") { if (!isNaN(round)&&flag) { return String(money.toFixed(round)).replace(formatReg,"$1,") } else if(!isNaN(round)){ return String(money).replace(sliceReg,"$1").replace(formatReg,"$1,") } else if(round === "undefined"){ return String(money).replace(formatReg,"$1,") } else { throw new Error("round is not Number!") } } else { throw new Error("money is not Number!") } } let res = formatMoney("1987562.12812",3,true) console.log(res)最后創(chuàng)建了一個(gè)前端學(xué)習(xí)交流群,感興趣的朋友,一起來(lái)嗨呀!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105114.html
摘要:一基礎(chǔ)接口的意義百度規(guī)范擴(kuò)展回調(diào)抽象類的意義想不想通過(guò)一線互聯(lián)網(wǎng)公司面試文檔整理為電子書掘金簡(jiǎn)介谷歌求職記我花了八個(gè)月準(zhǔn)備谷歌面試掘金原文鏈接翻譯者 【面試寶典】從對(duì)象深入分析 Java 中實(shí)例變量和類變量的區(qū)別 - 掘金原創(chuàng)文章,轉(zhuǎn)載請(qǐng)務(wù)必保留原出處為:http://www.54tianzhisheng.cn/... , 歡迎訪問(wèn)我的站點(diǎn),閱讀更多有深度的文章。 實(shí)例變量 和 類變量...
摘要:字囊括上百個(gè)前端面試題的項(xiàng)目開(kāi)源了這個(gè)項(xiàng)目是什么項(xiàng)目?jī)?nèi)容這個(gè)項(xiàng)目目前在上剛剛開(kāi)源主要內(nèi)容如下前端面試題主要整理了高頻且有一定難度的前端面試題對(duì)這些面試題進(jìn)行解讀前端原理詳解針對(duì)一些有一定難度面試題涉及的知識(shí)點(diǎn)進(jìn)行詳解比如涉及的編譯原理響應(yīng)式 20W字囊括上百個(gè)前端面試題的項(xiàng)目開(kāi)源了 這個(gè)項(xiàng)目是什么? 項(xiàng)目?jī)?nèi)容 這個(gè)項(xiàng)目目前在GitHub上剛剛開(kāi)源,主要內(nèi)容如下: 前端面試題: 主要整...
摘要:作為面試官,我是如何甄別應(yīng)聘者的包裝程度語(yǔ)言和等其他語(yǔ)言的對(duì)比分析和主從復(fù)制的原理詳解和持久化的原理是什么面試中經(jīng)常被問(wèn)到的持久化與恢復(fù)實(shí)現(xiàn)故障恢復(fù)自動(dòng)化詳解哨兵技術(shù)查漏補(bǔ)缺最易錯(cuò)過(guò)的技術(shù)要點(diǎn)大掃盲意外宕機(jī)不難解決,但你真的懂?dāng)?shù)據(jù)恢復(fù)嗎每秒 作為面試官,我是如何甄別應(yīng)聘者的包裝程度Go語(yǔ)言和Java、python等其他語(yǔ)言的對(duì)比分析 Redis和MySQL Redis:主從復(fù)制的原理詳...
摘要:作為面試官,我是如何甄別應(yīng)聘者的包裝程度語(yǔ)言和等其他語(yǔ)言的對(duì)比分析和主從復(fù)制的原理詳解和持久化的原理是什么面試中經(jīng)常被問(wèn)到的持久化與恢復(fù)實(shí)現(xiàn)故障恢復(fù)自動(dòng)化詳解哨兵技術(shù)查漏補(bǔ)缺最易錯(cuò)過(guò)的技術(shù)要點(diǎn)大掃盲意外宕機(jī)不難解決,但你真的懂?dāng)?shù)據(jù)恢復(fù)嗎每秒 作為面試官,我是如何甄別應(yīng)聘者的包裝程度Go語(yǔ)言和Java、python等其他語(yǔ)言的對(duì)比分析 Redis和MySQL Redis:主從復(fù)制的原理詳...
閱讀 3979·2021-11-16 11:44
閱讀 5231·2021-10-09 09:54
閱讀 2039·2019-08-30 15:44
閱讀 1691·2019-08-29 17:22
閱讀 2764·2019-08-29 14:11
閱讀 3401·2019-08-26 13:25
閱讀 2332·2019-08-26 11:55
閱讀 1603·2019-08-26 10:37