<div class="outside"> <span id="passage" style="color:blue;" data-color="red">example</span> </div> <style> #passage { color: yellow;} .outside span{ color: green; display: block;} span { display: inline;} [data-color="red"] { color: red;} </style>
其實瀏覽器中,這張圖的排列順序,就很好的表示出了這個demo中的優先級關係:javascript
優先級關係:內聯樣式 > ID 選擇器 > 類選擇器 = 屬性選擇器 = 僞類選擇器 > 標籤選擇器 = 僞元素選擇器。 ⚠️
!important
是個例外,優先級最高。
更詳細的CSS優先級請查看MDN-優先級是如何計算的?html
這就是考驗一個佈局的能力,沒什麼好說的,辦法不少。我用的flex打個樣。前端
<div class="main"> <div class="top"><h1>class="top"</h1></div> <div class="bottom"> <div class="left"> <h1>class="left"</h1> </div> <div class="right"> <h1>class="right"</h1> </div> </div> </div> <style> .main{ display: flex; flex-direction: column; } .top,.bottom{ height: 300px; } .top{ border: 1px solid red; } .bottom{ display: flex; border: 1px solid green; } .left,.right{ flex: 1; height: 100%; } .left{ border-right: 1px solid blue; } </style>
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])
直接寫答案就沒什麼意思了,借這個題先扯一下執行上下文
、做用域
、做用域鏈
、閉包
。java
如下demo、圖示、結論絕大部分來自 這個網站,推薦閱讀!在這裏引用是爲了讓你們更好的理解,我確實講不了這麼好!!!
一段JavaScript的代碼執行的時候,都會產生一個執行上下文(也就是執行環境)。多段代碼執行就會產生多個執行上下文。git
console.log(1); // 這段代碼的執行上下文就是--全局環境
function test() { console.log('test'); } test(); // test() 執行上下文就是test--函數環境
JavaScript中的運行環境大概包括三種狀況:github
⚠️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()上下文
,它們進棧出棧以下圖:segmentfault
每個執行上下文都有本身的生命週期:數組
對執行上下文總結一些結論:
做用域與執行上下文是徹底不一樣的兩個概念。
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定義的操做。而這些操做進入隊列的順序,則由設定的延遲時間來決定。
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
賦值會報錯。
上面的結果就很清楚了,直接報錯,後面的也不執行。
徹底考正則,本身惡補吧。沒辦法!
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
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<x;k++){ if(x%k==0) continue loop; //一旦有被小於它的數整除,則退出試下一個數 } //能走到這一步的就是素數了 primes.push(x); } console.log(primes.join(','))
先把全部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)
建立了一個前端學習交流羣,感興趣的朋友,一塊兒來嗨呀!