摘要:作用域鏈,是由當前環境與上層環境的一系列變量對象組成,它保證了當前執行環境對符合訪問權限的變量和函數的有序訪問。
span的display值,文本example的顏色
example
其實瀏覽器中,這張圖的排列順序,就很好的表示出了這個demo中的優先級關系:
優先級關系:內聯樣式 > ID 選擇器 > 類選擇器 = 屬性選擇器 = 偽類選擇器 > 標簽選擇器 = 偽元素選擇器。 ??!important是個例外,優先級最高。
更詳細的CSS優先級請查看MDN-優先級是如何計算的?
寫一個滿屏的品字這就是考驗一個布局的能力,沒什么好說的,辦法很多。我用的flex打個樣。
如下代碼,寫出執行結果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])
直接寫答案就沒什么意思了,借這個題先扯一下執行上下文、作用域、作用域鏈、閉包。
執行上下文以下demo、圖示、結論絕大部分來自這個網站,推薦閱讀!在這里引用是為了讓大家更好的理解,我確實講不了這么好!!!
一段JavaScript的代碼執行的時候,都會產生一個執行上下文(也就是執行環境)。多段代碼執行就會產生多個執行上下文。
console.log(1); // 這段代碼的執行上下文就是--全局環境
function test() { console.log("test"); } test(); // test() 執行上下文就是test--函數環境
JavaScript中的運行環境大概包括三種情況:
全局環境:JavaScript代碼運行起來會首先進入該環境
函數環境:當函數被調用執行時,會進入當前函數中執行代碼
eval(不建議使用,可忽略)
??JavaScript引擎會以棧的形式來處理這些執行上下文,棧底永遠都是全局上下文,而棧頂就是當前正在執行的上下文。
看下面這個demo,相信大家一看就懂了:
var color = "blue"; function changeColor() { var anotherColor = "red"; function swapColors() { var tempColor = anotherColor; anotherColor = color; color = tempColor; } swapColors(); } changeColor();
這里面有全局上下文(Global Context)、changeColor()上下文、swapColors()上下文,它們進棧出棧如下圖:
每一個執行上下文都有自己的生命周期:
對執行上下文總結一些結論:
單線程
同步執行,只有棧頂的上下文處于執行中,其他上下文需要等待
全局上下文只有唯一的一個,它在瀏覽器關閉時出棧
函數的執行上下文的個數沒有限制
每次某個函數被調用,就會有個新的執行上下文為其創建,即使是調用的自身函數,也是如此。
作用域、作用域鏈與閉包作用域與執行上下文是完全不同的兩個概念。
JavaScript代碼的整個執行過程,分為兩個階段,代碼編譯階段與代碼執行階段。編譯階段由編譯器完成,將代碼翻譯成可執行代碼,這個階段作用域規則會確定。執行階段由引擎完成,主要任務是執行可執行代碼,執行上下文在這個階段創建。
??JavaScript中只有全局作用域與函數作用域(因為eval我們平時開發中幾乎不會用到它,這里不討論)。
作用域鏈,是由當前環境與上層環境的一系列變量對象組成,它保證了當前執行環境對符合訪問權限的變量和函數的有序訪問。
看一個demo:
var a = 20; function test() { var b = a + 10; function innerTest() { var c = 10; return b + c; } return innerTest(); } test();
在上面的例子中,全局,函數test,函數innerTest的執行上下文先后創建。我們設定他們的變量對象分別為VO(global),VO(test), VO(innerTest)。而innerTest的作用域鏈,則同時包含了這三個變量對象,所以innerTest的執行上下文可如下表示。
innerTestEC = { VO: {...}, // 變量對象 scopeChain: [VO(innerTest), VO(test), VO(global)], // 作用域鏈 }
至于這里面的VO AO 有興趣的可以去上面那個網站里看看,這里不提,我覺得不妨礙大家理解。
簡單說就是,在innerTest這個方法內,能拿到test()方法中的變量,也能拿到全局環境中的變量,這就形成了一個作用域鏈。
看到這里相信大家都知道了,閉包不就是這個東東嘛。
它由兩部分組成。執行上下文(代號A),以及在該執行上下文中創建的函數(代號B)。
當B執行時,如果訪問了A中變量對象中的值,那么閉包就會產生。
JavaScript擁有自動的垃圾回收機制,關于垃圾回收機制,有一個重要的行為,那就是,當一個值,在內存中失去引用時,垃圾回收機制會根據特殊的算法找到它,并將其回收,釋放內存。
而我們知道,函數的執行上下文,在執行完畢之后,生命周期結束,那么該函數的執行上下文就會失去引用。其占用的內存空間很快就會被垃圾回收器釋放。可是閉包的存在,會阻止這一過程。
setTimeout繞了一圈回到這個題,這個題中的setTimeout又在何時執行呢?
在這里,將會介紹另外一個特殊的隊列結構,頁面中所有由setTimeout定義的操作,都將放在同一個隊列中依次執行。
而這個隊列執行的時間,需要等待到函數調用棧清空之后才開始執行。即所有可執行代碼執行完畢之后,才會開始執行由setTimeout定義的操作。而這些操作進入隊列的順序,則由設定的延遲時間來決定。
這個題中循環4次,每次往隊列里加入一個console.log(i),它們引用的都是同一個i在循環結束時,i已經變成4了。
console.log(arr[i])就是undefined。
答案: undefined , 4 ,4 ,4 ,4如下代碼,寫出執行結果
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(), 作為函數直接調用,this指向window,this.name = window.name=undefined
person("Jack") ,跟上面一樣,this指向window,this.name = window.name=name="Jack"
new person() ,作為構造函數調用,this指向新生成的對象,在自身沒有找到this.name就會沿著原型鏈查找,所以this.name = person.prototype.name=Tom
new person("Rose"),與上面類似,區別在于傳了name
human.person(),作為對象方法調用,this指向human=>human.name = "Cat"
person.call(window),用call方法將this指向window,??這里最容易錯?,person("Jack") 已經將window.name="Jack"
答案: undefined、Jack、Tom、Rose、Cat、Jack如下代碼,寫出執行結果
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();
這個題比較簡單,主要涉及的就是變量提升,和作用域鏈。坑點就是第一個console.log(a)到底是打印undefined還是finget.github.io。
再看看作用域鏈那張圖:
在hello()方法中定義了一個var a = "hello",雖然在剛執行的時候,根據變量提升原則,a=undefined,但是它還是很有骨氣的,只要自己有絕不往上找。那如果換成let a = "hello"呢?
來來來試一試:
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();暫時性死區
只要塊級作用域內存在let命令,它所聲明的變量就“綁定”(binding)這個區域,不再受外部的影響。
var tmp = 123; if (true) { tmp = "abc"; // ReferenceError let tmp; }
上面代碼中,存在全局變量tmp,但是塊級作用域內let又聲明了一個局部變量tmp,導致后者綁定這個塊級作用域,所以在let聲明變量前,對tmp賦值會報錯。
上面的結果就很清楚了,直接報錯,后面的也不執行。
提取url的參數,以key-value形式返回完全考正則,自己惡補吧。沒辦法!
let getSearch = function(url) { let matched = /^(?:https?://[^?]*?)(.*)/gi.exec(url) return matched ? matched[1] : "" } // 遞歸函數,循環匹配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" }判斷一個字符串中出現最多的字符,統計次數
function maxStr(str) { let map = {} for(let v of str) { map[v] = ~~map[v] + 1 } // 這里就類似這種結構 map={a:1,b:1}, ~~map[v]就類似parseInt(),如果某一個字符第一出現就是0,=> 0+1,以此類推! // Object.values 能將一個對象的value返回成一個數組,再去最大值 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))
按位非運算符“~” 先看看w3c的定義: 位運算 NOT 由否定號(~)表示,它是 ECMAScript 中為數不多的與二進制算術有關的運算符之一。 位運算 NOT 是三步的處理過程: 把運算數轉換成 32 位數字 把二進制數轉換成它的二進制反碼(0->1, 1->0) 把二進制數轉換成浮點數 簡單的理解,對任一數值 x 進行按位非操作的結果為 -(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) 相當于是 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實現一個拷貝函數 JSON.parse()
const newObj = JSON.parse(JSON.stringify(oldObj));
?1.他無法實現對函數 、RegExp等特殊對象的克隆比較完善的深拷貝
2.會拋棄對象的constructor,所有的構造函數會指向Object
3.對象有循環引用,會報錯
我覺得面試手寫這個也太那啥了!
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 => { // 維護兩個儲存循環引用的數組 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")) { // 對數組做特殊處理 child = []; } else if (isType(parent, "RegExp")) { // 對正則對象做特殊處理 child = new RegExp(parent.source, getRegExp(parent)); if (parent.lastIndex) child.lastIndex = parent.lastIndex; } else if (isType(parent, "Date")) { // 對Date對象做特殊處理 child = new Date(parent.getTime()); } else { // 處理對象原型 proto = Object.getPrototypeOf(parent); // 利用Object.create切斷原型鏈 child = Object.create(proto); } // 處理循環引用 const index = parents.indexOf(parent); if (index != -1) { // 如果父數組存在本對象,說明之前已經被引用過,直接返回此對象 return children[index]; } parents.push(parent); children.push(child); for (let i in parent) { // 遞歸 child[i] = _clone(parent[i]); } return child; }; return _clone(parent); };查找素數 試除法
這種方式很傳統理解上也簡單,給定一個范圍,那么就逐個循環去試除小于它數。
現在我們假設 N 等于 120
let N = 120; let primes = []; // 用于存素數結果集 loop:for(let x=2;x<=N;x++){ for(let k=2;k篩法 先把所有2的倍數去掉,然后剩下的那些數里面,最小的是3,3就是素數,然后把3的倍數都去掉,剩下的數里面,最小的是5,所以5也是素數…(可以看出已跳過4的試除,越多到后面跳過的數越多)
上述過程依次進行,但不像試除法逐個進行,就可以把某個范圍內的非素數全都除去,剩下的就是素數了。這種方式的好處在于運算不重復,高效。
有一張很形象的動畫,能直觀地體現出篩法的工作過程。 (非素數就像被篩子篩掉一樣)
let N = 120; let primes = []; // 用于存素數結果集 let nums = []; // 待篩選的數據集 for(let x=2;x<=N;x++){ //hooyes提示:此處初始化的時候,也可直接篩掉2的倍數數據減半。 //if(x%2!==0) nums.push(x); } // 遞歸函數 function PrimeFn(data){ let p = data.shift(); // 數組最前端的一個數即素數,拿出來存起,并作為下次篩除的分母。 primes.push(p); let t = []; for(let v of data){ v%p!==0 ? t.push(v) : "" // 能被 p 整除的都篩除掉,不能整除的放到臨時數組t存起來。 } // t 是下次待篩數組,元素個數會越來越少,若還有就進行一次遞歸。 t.length>0 ? PrimeFn(t) : "" } PrimeFn(nums); console.log(primes.join(",")); /* 得到小于N的素數集合 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 [小數位] * @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)最后創建了一個前端學習交流群,感興趣的朋友,一起來嗨呀!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105114.html
摘要:一基礎接口的意義百度規范擴展回調抽象類的意義想不想通過一線互聯網公司面試文檔整理為電子書掘金簡介谷歌求職記我花了八個月準備谷歌面試掘金原文鏈接翻譯者 【面試寶典】從對象深入分析 Java 中實例變量和類變量的區別 - 掘金原創文章,轉載請務必保留原出處為:http://www.54tianzhisheng.cn/... , 歡迎訪問我的站點,閱讀更多有深度的文章。 實例變量 和 類變量...
摘要:字囊括上百個前端面試題的項目開源了這個項目是什么項目內容這個項目目前在上剛剛開源主要內容如下前端面試題主要整理了高頻且有一定難度的前端面試題對這些面試題進行解讀前端原理詳解針對一些有一定難度面試題涉及的知識點進行詳解比如涉及的編譯原理響應式 20W字囊括上百個前端面試題的項目開源了 這個項目是什么? 項目內容 這個項目目前在GitHub上剛剛開源,主要內容如下: 前端面試題: 主要整...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經常被問到的持久化與恢復實現故障恢復自動化詳解哨兵技術查漏補缺最易錯過的技術要點大掃盲意外宕機不難解決,但你真的懂數據恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經常被問到的持久化與恢復實現故障恢復自動化詳解哨兵技術查漏補缺最易錯過的技術要點大掃盲意外宕機不難解決,但你真的懂數據恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
閱讀 3967·2021-11-16 11:44
閱讀 5189·2021-10-09 09:54
閱讀 2030·2019-08-30 15:44
閱讀 1678·2019-08-29 17:22
閱讀 2753·2019-08-29 14:11
閱讀 3388·2019-08-26 13:25
閱讀 2324·2019-08-26 11:55
閱讀 1595·2019-08-26 10:37