<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)
建立了一个前端学习交流群,感兴趣的朋友,一块儿来嗨呀!