web知识点-记录

技术点: 不定期更新补充

  • 页面引用svg symbol标签创建icon
  • p:nth-child(2) 与 p:nth-of-type(2)的作用不完全相同:同属于伪类,还有:first-of-type, :last-of-type, :nth-last-of-type 以及 :only-of-type等,参考nth-child和nth-of-type
  • charCodeAt,String.fromCharCode:将字符转为unicode码值和从unicode码值转为字符串."wwmin".charCodeAt(0);//119;,String.fromCharCode(119)//w.一般的charAt(i) 返回在指定位置的字符.
  • 刷新页面window.location.reload()
  • form表单 input type[text, search, url, telephone, email, password, date pickers, number, checkbox, radio 以及 file],required="required",为必填
  • vue中<input v-model="something">仅仅是<input v-bind:value="something" v-on:input="something = $event.target.value">的语法糖. @input 事件触发.
  • async await方式异步方法的同步写法
  • html figure 标签用于显示图片<figure><p>黄浦江上的的卢浦大桥</p>[图片]</figure>
  • 文字显示包裹,超出时用...省略号显示 css:
  .ellipsis{
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
  }
  • contentEditable=true在chrme控制台中输入此命令可使该元素可编辑(文字)
  • Vue中img的src属性绑定与static文件夹:在.vue中添加时src的路径是相对路径,可以直接写相对路径.如果想使用此路径需要把图片放到static路径下面,不能直接引用assets路径,如果想引用assets路径下的资源,就需要传入图片编码,即:imgUrl = require('./assets/logo.png'),这样就可以读到项目路径下的图片.
  • 在元素上添加pointer-events: none;可是此元素失去鼠标事件,即可穿透改元素
  • vue中鼠标事件有@click ,同理有@mouseover @mouseout @mouseenter
  • ajax请求时cache默认为true,只有在IE浏览器下支持,chrome,safari等不支持,故应将ajax的cache显式指为false,防止ie浏览器第二次请求从cache获取.
  • c#中Session将数据保存至服务器端(可用redis代替此方案)
  • css知识:transition: all .1s ease;将所有变换添加动画
  • rem单位是root em的缩写,em用来自适应用户所使用的字体,1em相当于当前的字体尺寸(font-size属性),2em相当于当前字体尺寸的2倍。可见em用作特定字体的相对大小。字面上指的是根元素的em大小。在Web文档的上下文中,根元素就是你的html元素。如果没有重置,html默认font-size:16px。
  • justify-content内容对齐(justify-content)属性应用在弹性容器上,把弹性项沿着弹性容器的主轴线(main axis)对齐。justify-content: flex-start | flex-end | center | space-between | space-around
  • transform: scale(1.1);扩大元素 transform:rotate(7deg)旋转元素 translate(x,y) skew(x-angle,y-angle)
  • text-transform: uppercase;将小写字母转换成大写:capitalize首字母大写:lowercase转换成小写
  • word-spacing:1em单词之间的间隔.letter-spacing:1em字母之间的间隔
  • document.querySelector(div[data-key="1"]).classList.add('playing'),原生dom查找方式并添加class.click点击事件获取的dom=>e.target.classList.remove('playing')
  • 添加监听,并监听trasition事件(在css中定义的transition属性在js中监听结束事件)
const keys = Array.from(document.querySelectorAll('.key'));
  keys.forEach(key => key.addEventListener('transitionend', removeTransition));
  • css3原生变量var的使用关于css3var
    CSS中原生的变量定义语法是:--*,变量使用语法是:var(--*),其中*表示我们的变量名称
:root { --color: purple; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
body {
  --size: 20;   
  font-size: calc(var(--size) * 1px);
}
选择器 例子 例子描述 CSS
.class .intro 选择 class="intro" 的所有元素。 1
#id #firstname 选择 id="firstname" 的所有元素。 1
* * 选择所有元素。 2
element p 选择所有 <p> 元素。 1
element,element div,p 选择所有 <div> 元素和所有 <p> 元素。 1
element element div p 选择 <div> 元素内部的所有 <p> 元素。 1
element>element div>p 选择父元素为 <div> 元素的所有 <p> 元素。 2
element+element div+p 选择紧接在 <div> 元素之后的所有 <p> 元素。 2
[attribute] [target] 选择带有 target 属性所有元素。 2
[attribute=value] [target=_blank] 选择 target="_blank" 的所有元素。 2
[attribute~=value] [title~=flower] 选择 title 属性包含单词 "flower" 的所有元素。 2
[attribute|=value] [lang|=en] 选择 lang 属性值以 "en" 开头的所有元素。 2
:link a:link 选择所有未被访问的链接。 1
:visited a:visited 选择所有已被访问的链接。 1
:active a:active 选择活动链接。 1
:hover a:hover 选择鼠标指针位于其上的链接。 1
:focus input:focus 选择获得焦点的 input 元素。 2
:first-letter p:first-letter 选择每个 <p> 元素的首字母。 1
:first-line p:first-line 选择每个 <p> 元素的首行。 1
:first-child p:first-child 选择属于父元素的第一个子元素的每个 <p> 元素。 2
:before p:before 在每个 <p> 元素的内容之前插入内容。 2
:after p:after 在每个 <p> 元素的内容之后插入内容。 2
:lang(language) p:lang(it) 选择带有以 "it" 开头的 lang 属性值的每个 <p> 元素。 2
element1~element2 p~ul 选择前面有 <p> 元素的每个 <ul> 元素。 3
[attribute^=value] a[src^="https"] 选择其 src 属性值以 "https" 开头的每个 <a> 元素。 3
[attribute$=value] a[src$=".pdf"] 选择其 src 属性以 ".pdf" 结尾的所有 <a> 元素。 3
[attribute*=value] a[src*="abc"] 选择其 src 属性中包含 "abc" 子串的每个 <a> 元素。 3
:first-of-type p:first-of-type 选择属于其父元素的首个 <p> 元素的每个 <p> 元素。 3
:last-of-type p:last-of-type 选择属于其父元素的最后 <p> 元素的每个 <p> 元素。 3
:only-of-type p:only-of-type 选择属于其父元素唯一的 <p> 元素的每个 <p> 元素。 3
:only-child p:only-child 选择属于其父元素的唯一子元素的每个 <p> 元素。 3
:nth-child(n) p:nth-child(2) 选择属于其父元素的第二个子元素的每个 <p> 元素。 3
:nth-last-child(n) p:nth-last-child(2) 同上,从最后一个子元素开始计数。 3
:nth-of-type(n) p:nth-of-type(2) 选择属于其父元素第二个 <p> 元素的每个 <p> 元素。 3
:nth-last-of-type(n) p:nth-last-of-type(2) 同上,但是从最后一个子元素开始计数。 3
:last-child p:last-child 选择属于其父元素最后一个子元素每个 <p> 元素。 3
:root :root 选择文档的根元素。 3
:empty p:empty 选择没有子元素的每个 <p> 元素(包括文本节点)。 3
:target #news:target 选择当前活动的 #news 元素。 3
:enabled input:enabled 选择每个启用的 <input> 元素。 3
:disabled input:disabled 选择每个禁用的 <input> 元素 3
:checked input:checked 选择每个被选中的 <input> 元素。 3
:not(selector) :not(p) 选择非 <p> 元素的每个元素。 3
::selection ::selection 选择被用户选取的元素部分。 3
  • 与A B的区别在于,A B选择所有后代元素,而A>B只选择一代,A+B表示HTML中紧随A的B元素,nth-child是个伪类的用法,如p:nth-child(2)就表示在p的父元素中选择位居第二位的p
  • text-decoration: none;
  • panel class下的第一个元素.panel > *:first-child,panel class下的第二个p元素.panel p:nth-child(2),panel class下的最后一个元素.panel > *:last-child,panel class下的奇数项.panel > *:nth-child(odd),panel class下的偶数项.panel > *:nth-child(even)
  • transition: transform 0.5s;将transform进行转换动画transform: translateY(-100%);
  • "12ab34".match(/\d+/gi)//["12","34"]
  • js中forEachmap,forEach没有返回值,对原数组没有影响,map返回什么那个项就是什么,返回新的数组;
  • array 数组方法spliceslice的区别:splice(index,howmany,item1,...,item2)其中index和howmany都是必须,item是要替换的项可省略,返回被删除的的项,对原数组进行修改.slice(start,end)其中start必须,end可选.可从已有的数组中返回新数组.两个方法都支持负向查找.
  • array 数组方法flat():
    flat()接收一个数组,数组的成员有时还是数组,flat()用于将嵌套的数组"拉平",变成一维的数组,并返回新数组.
[1,2,[3,4]].flat();//[1,2,3,4]

flat()只会拉平一层,如果是多层可使用参数

[1, 2, [3, [4, 5]]].flat();// [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2);// [1, 2, 3, 4, 5]

如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数

[1, [2, [3]]].flat(Infinity);// [1, 2, 3]

如果原数组有空位,flat()方法会跳过空位。

[1, 2, , 4, 5].flat();// [1, 2, 4, 5]
  • array 数组方法flat():
    flatMap()方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。flatMap()只能展开一层数组。
// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
  • Array.from
    数组迭代器
Array.from({length:4},(v,k)=>k+1)
//[1, 2, 3, 4]
  • :before或:after添加内容方法:使用content
.plates input + label:before {
   content: '⬜️'
   margin-right: 10px;
   }
.plates input:checked + label:before {
   content: '🌮';
   }
  • this.querySelector('[name=item]')获取name=item元素
  • localstorage和sessionstorage:
localstorage.setItem("key","value");
localstorage.getItem("key");
localstorage.removeItem("key");//删除键
localstorage.clear();//清除所有键值对
localstorage.key(index);//获取localstorage的属性名称
localstorage.length;//localstorage键值对数量
  • ES6const const {a:a,b:b}={a:"aaa",b:"bbb",c:"ccc"};===const {a,b}={a:"aaa",b:"bbb",c:"ccc"};
  • ul样式:list-style: inside square;在内部且标记为方块
  • webpack同步代码编写脚本:
    添加package.json文件,内容为
{
  "name": "gum",
  "version": "1.0.0",
  "description": "",
  "main": "scripts.js",
  "scripts": {
    "start" : "browser-sync start --server --files '*.css, *.html, *.js'"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "browser-sync": "^2.12.5"
  }
}
  • position:fexed;固定位置
  • npoi导出excel表问题,当创建的窗格cell比较多时,如果每个cell都设置调用了一次createFont()函数,将导致打开excel表时警告或报错,解决方案:将用到的字体按类别创建几个,然后设置字体时只调用生成的cellstyle变量即可.
  • dom事件的捕获,以及事件只触发一次的方法
const divs = document.querySelectorAll('div');
  const button = document.querySelector('button');

  function logText(e) {
    console.log(this.classList.value);
    // e.stopPropagation(); // stop bubbling!
    // console.log(this);
  }

  divs.forEach(div => div.addEventListener('click', logText, {
    capture: false,
    once: false
  }));

  button.addEventListener('click', () => {
    console.log('Click!!!');
  }, {
    once: true
  });
  • css属性变换显示transition 方法提高渲染速度的方案:will-change:transform;
  • css移动一个div可以用transform:translateY(100px);transform: translate(313.225px, 80px);方案实现.
  • transition对transform的移动有动画效果,对top,left移动位置没有效果.
  • transition对opacity显示有效果,对display:none,block没有效果
  • box-shadow: h-shadow v-shadow blur spread color inset;也可添加多个,用逗号隔开
  • transform-origin 属性允许您改变被转换元素的位置。transform-origin: -15% 100%;transform-origin: 50% 50% 100px;
  • css属性模糊匹配选择<a class="button" href="http://twitter.com/wesbos">Twiter</a>css.button[href*=twitter] { background: #019FE9; } , 匹配开头css.button[href^=twitter] { background: #019FE9; }
  • nav>ul只选择nav下一级里面的ul元素。nav ul选择nav内所包含的所有ul元素。
    nav>ulnav ul限定更严格,必须后面的元素只比前面的低一个级别。
  • 用after添加内容的方法
.hole:after {
  display: block;
  background: url(dirt.svg) bottom center no-repeat;
  background-size:contain;
  content:'';
  width: 100%;
  height:70px;
  position: absolute;
  z-index: 2;
  bottom:-30px;
}
  • async defer 异步加载script 并在加载完之后执行方法的方式:<script async src="js/gauge.js" onload="initScriptedGauges()"></script>另一种是用defer,区别在于执行时机,都是在dom加载完之后在加载script,但是defer按顺序加载,所有多个script有依赖的话需要将依赖用defer并放到上面先加载
  • callee arguments.callee相当于重新调用了函数,多用于递归
function chen(x){
 if(x<=1)return 1;
 else arguments.callee(x-1); //此处相当于else chen(x-1);
  • caller 是函数对象的一个属性,该属性保存着调用当前函数的函数的引用(指向当前函数的直接父函数)
(function a(){
  b();
 })();
function b(){
console.log(arguments.callee.caller); //相当于b.caller ==>输出函数a
};
  • text-indent段落首行缩进控制.p{text-indent:2em}
  • scss 中&的用法 &代表父级
nav a{
     color:red;
   }
head nav a{
      color:green;
 }
head nav a + a{
    color:black;
}

scss:

nav {
    a{
        color:red;
        head & { //&代表父级
            color:green;
            head & + a{ //+ 代表下一个
                color:black;
            }
        }
    }
}
  • 执行n次的hack一点的方法:Array.apply(null,{length:20}).map(()=>console.log('1'));

  • 规定段落中的文本不进行换行:设置如何处理元素内的空白。

p  {  white-space: nowrap  }
normal  默认。空白会被浏览器忽略。
pre 空白会被浏览器保留。其行为方式类似 HTML 中的 <pre> 标签。
nowrap  文本不会换行,文本会在在同一行上继续,直到遇到 <br> 标签为止。
pre-wrap    保留空白符序列,但是正常地进行换行。
pre-line    合并空白符序列,但是保留换行符。
inherit 规定应该从父元素继承 white-space 属性的值。
  • javascript 正则表达式中的g修饰符,会导致两次执行reg.test(string)结果不一样,原因是lastIndex的问题导致的,解决方法是可以把reg的g修饰符去除 或者第二次及之后test前执行reg.lastIndex =0
var reg = /^https?:\/\/[a-zA-Z0-9-._\/]+/gi;
reg.test("http://www.baidu.com");//true
reg.test("http://www.baidu.com");//false
//方法一:去掉g
var reg = /^https?:\/\/[a-zA-Z0-9-._\/]+/i;
//方法二: test之后,执行reg.lastIndex=0
reg.test("http://www.baidu.com");//true
reg.lastIndex=0;
  • 页面下载资源方法
    动态插入dom法
let elemIF = document.createElement("iframe");
                        elemIF.src = 'http://a.b.com/1.';
                        elemIF.style.display = "none";
                        document.body.appendChild(elemIF);

a标签的href 加上download属性方式:

<a href="/images/myw3schoolimage.jpg" download="w3logo">

URL.createObjectURL的参数是File对象或者Blob对象,File对象也就是通过input[type=file]选择的文件,Blob对象是二进制大对象

function downloadFile(fileName, content){
    var aLink = document.createElement('a');
    var blob = new Blob([content]);
    //创建点击事件
    var evt = document.createEvent("HTMLEvents");
    evt.initEvent("click", false, false);//initEvent 不加后两个参数在FF下会报错
    aLink.download = fileName;
    aLink.href = URL.createObjectURL(blob);
    aLink.dispatchEvent(evt);
}
  • 汉字和unicode相互转换
function tounicode(data) {
  if (data == '') return '请输入汉字';
  var str = '';
  for (var i = 0; i < data.length; i++) {
    str += "\\u" + parseInt(data[i].charCodeAt(0), 10).toString(16);
  }
  return str;
}
function tohanzi(data) {
  if (data == '') return '请输入十六进制unicode';
  data = data.split("\\u");
  var str = '';
  for (var i = 0; i < data.length; i++) {
    str += String.fromCharCode(parseInt(data[i], 16).toString(10));
  }
  return str;
}
  • scroll js 事件会受到css height:100%;overflow:auto;影响,监听不到scroll事件.
    可以单独监听div,只操作该div的偏移量即可,完整组件见下(修复了双击事件的bug):
<template>
  <transition :name="transitionName">
    <div class="back-to-top" @click="backToTop" v-show="visible" :style="customStyle" title="返回顶部">
      <svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg"
           class="Icon Icon--backToTopArrow" aria-hidden="true" style="height: 16px; width: 16px;">
        <title>回到顶部</title>
        <g>
          <path
            d="M12.036 15.59c0 .55-.453.995-.997.995H5.032c-.55 0-.997-.445-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29c.39-.39 1.026-.385 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z"
            fill-rule="evenodd"></path>
        </g>
      </svg>
    </div>
  </transition>
</template>
<script>
  let dom = null;//定义dom 默认为window
  let isWindow = false;//判断是否为window
  export default {
    name: 'BackToTop',
    props: {
      //dom id , 如果不给值,则整个默认为window
      domID: {
        type: String,
        default: ""
      },
      visibilityHeight: {
        type: Number,
        default: 300
      },
      backPosition: {
        type: Number,
        default: 0
      },
      customStyle: {
        type: Object,
        default: () => ({
          right: '50px',
          bottom: '50px',
          width: '40px',
          height: '40px',
          'border-radius': '4px',
          'line-height': '45px',
          background: '#e7eaf1'
        })
      },
      transitionName: {
        type: String,
        default: 'fade'
      }
    },
    data() {
      return {
        visible: false,
        interval: null
      }
    },
    mounted() {
      //判断是否为window 后面会分别采用不同的方法
      let d = document.getElementById(this.domID);
      if (!!d) {
        dom = d;
        isWindow = false;
      } else {
        isWindow = true;
        dom = window;
      }
      dom.addEventListener('scroll', this.handleScroll)
    },
    beforeDestroy() {
      dom.removeEventListener('scroll', this.handleScroll);
      if (this.interval) {
        clearInterval(this.interval);
        this.interval = null;
      }
    },
    methods: {
      handleScroll(e) {
        this.visible = isWindow ? dom.pageYOffset > this.visibilityHeight : dom.scrollTop > this.visibilityHeight;
      },
      backToTop() {
        if (this.interval !== null) return;
        const start = isWindow ? window.pageYOffset : dom.scrollTop;
        let i = 0;
        this.interval = setInterval(() => {
          const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 200));
          if (next <= this.backPosition) {
            isWindow ? window.scrollTo(0, this.backPosition) : dom.scrollTop = this.backPosition;
            clearInterval(this.interval);
            this.interval = null;
          } else {
            isWindow ? window.scrollTo(0, next) : dom.scrollTop = next;
          }
          i++
        }, 16.7);
      },
      easeInOutQuad(t, b, c, d) {
        if ((t /= d / 2) < 1) return c / 2 * t * t + b;
        return -c / 2 * (--t * (t - 2) - 1) + b
      }
    }
  }
</script>
<style scoped>
  .back-to-top {
    position: fixed;
    display: inline-block;
    text-align: center;
    cursor: pointer;
  }
  .back-to-top:hover {
    background: #d5dbe7;
  }
  .fade-enter-active,
  .fade-leave-active {
    transition: opacity .5s;
  }
  .fade-enter,
  .fade-leave-to {
    opacity: 0
  }
  .back-to-top .Icon {
    fill: #9aaabf;
    background: none;
  }
</style>

  • JavaScript的双重按位非运算符~~巧用 , 效果等同与Math.floor()
~~true === 1
~~false === 0
~~"" === 0
~~[] === 0
~~undefined === 0
~~!undefined === 1
~~null === 0
~~!null === 1
~~4.9===4
  • 取整 | 0
1.3 | 0         // 1
-1.9 | 0        // -1
  • 判断奇偶数 & 1
const num=3;
!!(num & 1)                 // true
!!(num % 2)                 // true
  • JavaScript数组(基础值型)扁平化简单方法
var a=[1,2,[1,2,3],[2,3,[2,3,4]]];
var result=a.toString().split(',');// ["1", "2", "1", "2", "3", "2", "3", "2", "3", "4"]

function flatten(arr){
    return arr.reduce(function(prev,item){
        return prev.concat(Array.isArray(item)?flatten(item):item);
    },[]);
}

function flatten(arr){
    while(arr.some(item=>Array.isArray(item)){
        arr = [].concat(...arr);
    }
    return arr;
}
  • JavaScript数组平铺到指定深度
const flatten = (arr, depth = 1) =>
  depth != 1
    ? arr.reduce((a, v) => a.concat(Array.isArray(v) ? flatten(v, depth - 1) : v), [])
    : arr.reduce((a, v) => a.concat(v), []);
flatten([1, [2], 3, 4]);                             // [1, 2, 3, 4]
flatten([1, [2, [3, [4, 5], 6], 7], 8], 2);           // [1, 2, 3, [4, 5], 6, 7, 8]
  • sleep 方法
function sleep(delay){
  return new Promise(reslove=>{
           setTimeout(reslove,delay)
    })
}
!async function test(){
    const t1=+new Date();
    await sleep(3000);
    const t2=+new Date()
    console.log(t2-t1);
}()
  • 获取时间戳
+new Date() ;//最简洁
ToNumber(new Date());
new Date().getTime();
  • 获取n秒前/后时间
function getBeforeOrAfterTime(n=0){
 var now=new Date();
 return new Date(now.setSeconds(now.getSeconds()+n))
}
  • 数组去重
var a=[1,1,'1','2',1];
const unique=a=>[...new Set(a)];
  • 伪数组转数组方法
//伪数组:无法调用数组的方法,但是有length属性,又可以索引获取内部项的数据结构
//比如:arguments、getElementsByTagName等一系列dom获取的NodeList对象
//f1 f2 f3 f4 f5都执行在函数内,获取函数的arguments
var f1= [].slice.call(arguments);
var f2= Array.prototype.slice.call(arguments);
var f3 = Array.from(arguments);
var f4= [...arguments];

//f5 循环
var arr1 = [];
len1 = arguments.length;
for (var i = 0; i < len1; i++) {
   arr1.push(arguments[i]);
}
  • 数字格式化 如1234567 -> 1,234,567
const a=1234567;
//正则方式
function formatNumber(str){
    return str.replace(/\B(?=(\d{3})+(?!\d))/g,',')
}
//正则方式2
function formatNumber(num) {
  num = parseFloat(num.toFixed(3));
  let [integer, decimal] = String.prototype.split.call(num, '.');
  integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,');
  return integer + '.' + (decimal ? decimal : '');
}
//api版
a.toLocaleString('en-US');
a.toLocaleString(10);


  • 交换两个数
var a=1,b=2;
[a,b]=[b,a];
  • 代理:
    正向代理: 代理端代理的是客户端,服务端认代理不认客户端
    反向代理: 代理端代理的服务端,客户端认代理端不认服务端

  • 生成6位的数字验证码

let qrcode=('000000' + Math.floor(Math.random() * 999999)).slice(-6);
  • 大/小驼峰命名转下划线/连接符命名
'componentMapModelRegistry'.match(/^[a-z][a-z0-9]+|[A-Z][a-z0-9]*/g).join('_').toLowerCase();  
//component_map_model_registry
  • url参数转换为json
//query("?a=1&b=2") ==> {a:"1",b:"2"}
const query = (search = '') => ((querystring = '') => (q => (querystring.split('&').forEach(item => (kv => kv[0] && (q[kv[0]] = kv[1]))(item.split('='))), q))({}))(search.split('?')[1])
//去掉箭头函数写法
const qeury = function (search = '') {
  return function (querystring = '') {
    return function (q) {
      return querystring.split('&').forEach(function (item) {
        return function (kv) {
          return kv[0] && (q[kv[0]] = kv[1])
        }(item.split('='))
      }), q
    }({})
  }(search.split('?')[1])
};
//正则表达式写法
function qeury(url) {
  const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
  const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
  let paramsObj = {};
  // 将 params 存到对象中
  paramsArr.forEach(param => {
    if (/=/.test(param)) { // 处理有 value 的参数
      let [key, val] = param.split('='); // 分割 key 和 value
      val = decodeURIComponent(val); // 解码
      val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字

      if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
        paramsObj[key] = [].concat(paramsObj[key], val);
      } else { // 如果对象没有这个 key,创建 key 并设置值
        paramsObj[key] = val;
      }
    } else { // 处理没有 value 的参数
      paramsObj[param] = true;
    }
  })

  return paramsObj;
}
  • 计算直角三角形斜边
//Math.hypot([x[,y[,…]]])函数返回它的所有参数的平方和的平方根
Math.hypot(3,4) //=> 5
  • js 中 数组和字符串都可以用indexOf搜索
//字符串
var s="abc";
s.indexOf("a");  //0
//数组
var a=["aa","bb","cc"];
a.indexOf("aa");//0
  • 位移运算<< , >>
// 1. <<
1<<1  // 2
1<<2 //4
1<<3 //8
...
1<<n //等价于=> 1*2^n

// 2.>>
8>>1 //4
8>>2 //2
8>>3 //1
...
8>>n // 等级于 => 8%2^n
  • 对象object属性遍历方法
var person={name:"aaa",age:1};
Object.keys(person);
Object.getOwnPropertyNames(person);
Reflect.ownKeys(person);
for(let iten in person){console.log(item)};
Object.getOwnPropertySymbols(person);//获取对象的symbol键值
  • Array数组中遍历空值情况
    Array中forEach ,map ,produce,filter,every,some,不会遍历null或empty空值(如new Array(2)定义出的数组) 元素 (undefined仍会遍历)
    find,findIndex 会遍历null,empty空值,
    includes,indexOf不遍历数组
    Array.entries() 的next() 方法会遍历null及empty空值
var a=new Array(4);
a.forEach(p=>console.log(p));//没有打印
a=[1, null, NaN, empty];
var ae=a.entries();
ae.next().value; //[0,1];
ae.next().value; //[1,null];
ae.next().value; //[2,NaN];
ae.next().value; //[3,undefined];
  • JavaScript种子随机数(得到的随机数为同一组随机数)
  Math.seed = 5;
  Math.seededRandom = function (max, min) {
    max = max || 1;
    min = min || 0;
    Math.seed = (Math.seed * 9301 + 49297) % 233280;
    var rnd = Math.seed / 233280.0;
    return min + rnd * (max - min);
  };
  for (var i = 0; i < 5; i++) {
    console.log(Math.seededRandom());
  }
//结果如下
  0.4106738683127572
  0.8889703360768175
  0.5244170096021948
  0.8139274691358025
  0.5507115912208504
  • css实现hover显示菜单选项.
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <title> 页面名称 </title>
  <style type="text/css">
    .main dd {
      display: none;
    }
    .main dt:hover+dd, .main dd:hover {
      display: block;
    }
  </style>
</head>
<body>
<div class="main">
  <dt><a href="#">中心信息</a></dt>
  <dd><a href="#">中心介绍</a>
    <a href="#" >荣誉奖项</a>
    <a href="#" >中心环境</a>
    <a href="#" >中心地址</a>
    <a href="#" >联系我们</a> </dd>
</div>
</body>
</html>
  • padding和width的关系
    盒子大小最为重要的2个部分就是padding和内容content。当我们设置width是在设置content的大小,百分比的时候它参照的是父元素的content的大小。父元素content的大小(即width)为100px,子元素用百分比的时候100%就是100px。如果父元素width设置为100%刚好撑满整个设备屏幕,如果这个时候子元素width还是设置为100%;同时,父元素用了padding,此时会有横向滚动条或者整个元素的边界扩张了。
    想用padding来限制内容大小或者子元素的时候,父元素不要设置width,如果width设置了就意味着content肯定是不变的,完全和我们的想法矛盾了,所以父元素自身设置好padding就行了,里面子元素的宽度设置为100%。

  • 动态添加显示内容到html

  var logger = value => {
    if (Object.prototype.toString.call(value) !== "[object String]") {
      value = JSON.stringify(value);
    }
    let div = document.createElement("div");
    div.innerText = value;
    document.body.appendChild(div);
  };
  • Vue 数据劫持原理
  // 定义一个observe方法
  function defineReactive(data, key, val) {
    Object.defineProperty(data, key, {
      enumerable: true,
      configurable: true,
      get: function defineGet() {
        console.log(`get key: ${key} val: ${val}`);
        return val
      },
      set: function defineSet(newVal) {
        console.log(`set key: ${key} val: ${newVal}`);
        // 此处将新的值赋给val,保存在内存中,从而达到赋值的效果
        val = newVal
      }
    })
  }

  // 遍历对象的属性,使之观察每个数值值
  function observe(data) {
    Object.keys(data).forEach(key => {
      defineReactive(data, key, data[key]);
    })
  }

var person={name:"wwmin"};
observe(person);
person.name="hello world.";// => set key: name val: hello world.
  • 2进制 8进制 10进制 16进制 -- 相互转换
//十进制转其他  
var x=110;  
console.log(x);    //110
console.log(x.toString(8));  //156
console.log(x.toString(32));  //3e
console.log(x.toString(16));  //6e
//其他转十进制  
var x='110';  
console.log(parseInt(x,2));  //6
console.log(parseInt(x,8));  //72
console.log(parseInt(x,16));  //272
//其他转其他  
//先用parseInt转成十进制再用toString转到目标进制  
console.log(String.fromCharCode(parseInt(141,8))) ; //a
console.log(parseInt('ff',16).toString(2)); //11111111
  • Map对象--> 键/值对的集合
var m=new Map();
//或者直接初始话
var m=new Map([[1,"a"],[2,"b"],[3,"c"]]);
m.constructor===Map;//true
//Map属性和方法
m.set(1,"aa");
m.set(1,"a");//会覆盖上面的键值
m.set(2,"b");
m.set(3,"c");
m.size;//3
m.get(1);//a
m.has(1);//true
m.keys();//MapIterator {1, 2, 3}
console.log([...m.keys()]);//[1,2,3]  ,转换成标准数组
m.values();//MapIterator {"a", "b", "c"}
console.log([...m.values()]);//["a", "b", "c"]
m.entries();//MapIterator {1 => "a", 2 => "b", 3 => "c"}
console.log([...m.entries()]);//[[1, "a"], [2, "b"], [3, "c"]]
m.forEach((v,k,p)=>console.log(v,k,p));//a 1 Map(3) {1 => "a", 2 => "b", 3 => "c"},b 2 Map(3) {1 => "a", 2 => "b", 3 => "c"},c 3 Map(3) {1 => "a", 2 => "b", 3 => "c"}
m.delete(1);//true;
m.has(1);//false
m.clear();
  • Set 集合 和Map 类似,但是没有value
var s=new Set();
//或者直接初始化
var s=new Set([1,2,3]);
s.constructor===Set;//true
s.add(1);
s.size;//3
s.has(1);//true
s.keys();//SetIterator {1, 2, 3}
console.log([...s.keys()]);//[1,2,3]
s.values();//等同于s.keys();
s.entries();//SetIterator {1, 2, 3}
console.log([...m.entries()]);//[[1, 1], [2, 2], [3, 3]]
s.forEach((v,k,p)=>console.log(v,k,p));//1 1 Set(3){1,2,3},2 2 Set(3){1,2,3},3 4 Set(3){1,2,3}
s.delete(1);//true
s.has(1);//false
s.clear();
  • 数组对象解构
const s = '1997,John Doe,US,john@doe.com,New York';
const {2:country,4:state}=s.split(","); //2为split之后的数组下标,country为指定的变量
console.log(country);//US
console.log(state);//New York
  • 使用解构删除不必要属性
let {_internal, tooBig, ...cleanObject} = {el1: '1', _internal:"secret", tooBig:{}, el2: '2', el3: '3'};

console.log(cleanObject);                         // {el1: '1', el2: '2', el3: '3'}
  • 合并对象
let object1 = { a:1, b:2,c:3 }
let object2 = { b:30, c:40, d:50}
let merged = {…object1, …object2} //spread and re-add into merged
console.log(merged) // {a:1, b:30, c:40, d:50}

注意:第二个对象中的属性 b 和 c 的值把第一个对象中属性 b 与 c 的值覆盖掉了

  • 在函数参数中使用嵌套对象解构
var car = {
  model: 'bmw 2018',
  engine: {
    v6: true,
    turbo: true,
    vin: 12345
  }
}
const modelAndVIN = ({model, engine: {vin}}) => {
  console.log(`model: ${model} vin: ${vin}`);
}
modelAndVIN(car); // => model: bmw 2018  vin: 12345
  • switch语句中使用范围
function getWaterState(tempInCelsius) {
  let state;
  switch (true) {
    case (tempInCelsius <= 0): 
      state = 'Solid';
      break;
    case (tempInCelsius > 0 && tempInCelsius < 100): 
      state = 'Liquid';
      break;
    default: 
      state = 'Gas';
  }
  return state;
}
  • await 多个 async 函数
//await 多个 async 函数并等待他们执行完成,我们可以使用 Promise.all:
await Promise.all([anAsyncCall(), thisIsAlsoAsync(), oneMore()])
  • 创建纯对象
    你可以创建一个 100% 的纯对象,这个对象不会继承 Object 的任何属性和方法(比如 constructor,toString() 等):
const pureObject = Object.create(null);
console.log(pureObject); //=> {}
console.log(pureObject.constructor); //=> undefined
console.log(pureObject.toString); //=> undefined
console.log(pureObject.hasOwnProperty); //=> undefined
  • 格式化 JSON 代码
const obj = { 
 foo: { bar: [11, 22, 33, 44], baz: { bing: true, boom: 'Hello' } } 
};

// 第三个参数为格式化需要的空格数目
JSON.stringify(obj, null, 4); 
// =>"{
// =>    "foo": {
// =>        "bar": [
// =>            11,
// =>            22,
// =>            33,
// =>            44
// =>        ],
// =>        "baz": {
// =>            "bing": true,
// =>            "boom": "Hello"
// =>        }
// =>    }
// =>}"
  • 使用对象解构模拟命名参数
//对象解构,且有默认值,并且参数可选
function doSomething({ foo = 'Hi', bar = 'Yo!', baz = 13 } = {}) {
  // ...
}

注意,传入参数为undefined或者不传入的时候会使用默认参数,但是传入null还是会覆盖默认参数。

  • 接收函数返回的多个结果
async function getFullPost(){
  return await Promise.all([
     fetch('/post'),
     fetch('/comments')
  ]);
}
const [post, comments] = getFullPost();
  • 强制参数
mandatory = ( ) => {
  throw new Error('Missing parameter!');
}
foo = (bar = mandatory( )) => {            // 这里如果不传入参数,就会执行manadatory函数报出错误
  return bar;
}
  • 隐式返回值,返回对象写法
function a(n){
  return {value:n}
}
//简写为
let a=n=>({value:n})
  • 惰性载入函数
function foo(){
    if(a !== b){
        console.log('aaa')
    }else{
        console.log('bbb')
    }
}

// 优化后
//方法1: 在函数第一次被调用时指定适当函数
function foo(){
    if(a != b){
        foo = function(){
            console.log('aaa')
        }
    }else{
        foo = function(){
            console.log('bbb')
        }
    }
    return foo();
}

//方法2: 在声明函数时就指定适当函数
 var foo = (function() {
        if (a != b) {
          return function() {
            console.log("aaa");
          };
        } else {
          return function() {
            console.log("bbb");
          };
        }
      })();
  • 一次性函数
var sca = function() {
    console.log('msg')
    sca = function() {
        console.log('foo')
    }
}
sca()        // msg
sca()        // foo
sca()        // foo
  • input type="number"maxlength="3"不起作用解决方法
    在html5中input新增类型:color, date, datetime, datetime-local, month, week, time, email, number, range, search, tel , url.
<input type="text" placeholder="请输入手机号码" maxlength="11" />

上面的maxlength是有效的,但是将type="number"后将失去效果,此时可以使用type="tel" 解决

<input type="tel" placeholder="请输入手机号码" maxlength="11" />

不过此时数字键盘会存在一个'-'的固话符号,此影响较小可忽略,如果为了可拓展性,可用input事件写JavaScript取解决,即在oninput onpropertychange事件中取截取字符串长度即可.

  • input file选择图片后显示(FileReader)
 <input value="上传图片" type="file" id="file" name="file" onchange="show(this.files)" />
 <br />
 <img height="50" width="50" id="img"  />
    function show(f) {
        var str = "";
        for (var i = 0; i < f.length; i++) {
            var reader = new FileReader();
            reader.readAsDataURL(f[i]);
            reader.onload = function (e) {
                str += "<img  height='50' width='50' id='img' src='" + e.target.result + "'/>";
                $("#img")[0].outerHTML = str;
            }
        }        
    }
  • 数字转换
'12' * 1            // 12
'aa' * 1            // NaN
null * 1            // 0
undefined * 1    // NaN
1  * { valueOf: ()=>'2' }        // 2
+ '12'            // 12
+ 'aa'               // NaN
+ ''                    // 0
+ null              // 0
+ undefined    // NaN
+ { valueOf: ()=>'2' }    // 2
'' + {toString:()=>'S',valueOf:()=>'J'}// J  //toString()和valueOf同时存在则用valueOf()
  • 使用Boolean过滤数组中的所有假值
    JS中有一些假值:false,null,0,"",undefined,NaN
const compact = arr => arr.filter(Boolean);
compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34])             // [ 1, 2, 3, 'a', 's', 34 ]
  • 精确到指定位数的小数
    原理:先将小数扩大10*decimals倍并取整,然后在将整数取小数即除10*decimals
const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`)
round(1.345, 2)                 // 1.35
round(1.345, 1)                 // 1.3
  • 统计数组中相同项的个数
var cars = ['BMW','Benz', 'Benz', 'Tesla', 'BMW', 'Toyota'];
var carsObj = cars.reduce(function (obj, name) {
  obj[name] = obj[name] ? ++obj[name] : 1;
  return obj;
}, {});
carsObj; // => { BMW: 2, Benz: 2, Tesla: 1, Toyota: 1 }
  • 位数不足两位补零
const addZero = (num, len = 2) => (`0${num}`).slice(-len);
  • 使用"reduce"来判断括号是否匹配
    常规的做法是使用栈来匹配,但是这里我们使用reduce 就可以做到,我们只需要一个变量 counter ,这个变量的初始值是0, 当遇到 ( 的时候,counter ++ 当遇到 ) 的时候, counter -- 。 如果括号是匹配的,那么这个 counter 最终的值是0
//Returns 0 if balanced.
const isParensBalanced = (str) => {
  return str.split('').reduce((counter, char) => {
    if(counter < 0) { //matched ")" before "("
      return counter;
    } else if(char === '(') {
      return ++counter;
    } else if(char === ')') {
      return --counter;
    }  else { //matched some other char
      return counter;
    }
    
  }, 0); //<-- starting value of the counter
}
isParensBalanced('(())') // 0 <-- balanced
isParensBalanced('(asdfds)') //0 <-- balanced
isParensBalanced('(()') // 1 <-- not balanced
isParensBalanced(')(') // -1 <-- not balanced
  • 计算数组中元素出现的次数(将数组转为对象)
    如果你想计算数组中元素出现的次数或者想把数组转为对象,那么你可以使用 reduce 来做到。
var cars = ['BMW','Benz', 'Benz', 'Tesla', 'BMW', 'Toyota'];
var carsObj = cars.reduce(function (obj, name) { 
   obj[name] = obj[name] ? ++obj[name] : 1;
  return obj;
}, {});
carsObj; // => { BMW: 2, Benz: 2, Tesla: 1, Toyota: 1 }
  • js 实现 linq中groupBy方法
/**
 * js 实现 linq groupBy
 *
 * @param {*} array
 * @param {*} f =>item.name || f=>[item.name,item.Average]
 * @returns
 */
function groupBy(array, f) {
  let groups = {};
  array.forEach(o => {
    let group = JSON.stringify(f(o));
    groups[group] = groups[group] || [];
    groups[group].push(o);
  });
  return Object.keys(groups).map(group => groups[group])
}

// 使用
let list = [
{ "name": "John", "Average": 10, "High": 10, "DtmStamp": 1358226000000 },
{"name": "Jane","Average":10,"High":92,"DtmStamp":1358226000000},
{"name": "Jane","Average":10,"High":45,"DtmStamp":1358226000000},
{"name": "John","Average":10,"High":87,"DtmStamp":1358226000000},
{"name": "Jane","Average":10,"High":10,"DtmStamp":1358226060000},
{"name": "John","Average":15,"High":87,"DtmStamp":1358226060000},
{"name": "John","Average":15,"High":45,"DtmStamp":1358226060000},
{"name": "Jane","Average":15,"High":92,"DtmStamp":1358226060000}
];
// 使用1
let sort1 = groupBy(list, item => {
  return [item.name, item.Average];
})
// 结果
/** 
[ [ { name: 'John', Average: 10, High: 10, DtmStamp: 1358226000000 },
    { name: 'John', Average: 10, High: 87, DtmStamp: 1358226000000 },
    { name: 'John', Average: 15, High: 87, DtmStamp: 1358226060000 },
    { name: 'John', Average: 15, High: 45, DtmStamp: 1358226060000 } ],
  [ { name: 'Jane', Average: 10, High: 92, DtmStamp: 1358226000000 },
    { name: 'Jane', Average: 10, High: 45, DtmStamp: 1358226000000 },
    { name: 'Jane', Average: 10, High: 10, DtmStamp: 1358226060000 },
    { name: 'Jane', Average: 15, High: 92, DtmStamp: 1358226060000 } ] ]
**/

// 使用2
let sort2 = groupBy(list, item => {
  return item.name;
})
// 结果
/** 
[ [ { name: 'John', Average: 10, High: 10, DtmStamp: 1358226000000 },
    { name: 'John', Average: 10, High: 87, DtmStamp: 1358226000000 } ],
  [ { name: 'Jane', Average: 10, High: 92, DtmStamp: 1358226000000 },
    { name: 'Jane', Average: 10, High: 45, DtmStamp: 1358226000000 },
    { name: 'Jane', Average: 10, High: 10, DtmStamp: 1358226060000 } ],
  [ { name: 'John', Average: 15, High: 87, DtmStamp: 1358226060000 },
    { name: 'John', Average: 15, High: 45, DtmStamp: 1358226060000 } ],
  [ { name: 'Jane', Average: 15, High: 92, DtmStamp: 1358226060000 } ] ]
**/
  • 在移动端禁用长按选中文本功能
* {
-webkit-touch-callout:none;
-webkit-user-select:none;
-khtml-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
}
 input {      
   -webkit-user-select:auto; /*webkit浏览器*/     
}  
  • try catch finally
 try { 
        if(x == "")  throw "为空";
    }catch(err) {
        message.innerHTML = "输入的值 " + err;
    }finally {//总是会会执行到此处
        document.getElementById("demo").value = "";
    }
  • 数组求和
    reduce
let a=[1,2,3];
let sum=a.reduce((p,t)=>p+t,0);

eval

let a=[1,2,3];
let sum=eval(a.join('+'));
  • 获取文件后缀名
function getExt(fileName){
  return fileName.substring(fileName.lastIndexOf('.')+1);
}
  • 生成GUID
// 生成guid,主要用于生成随机文件名
function newGuid() {
    function S4() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }
    return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}
  • 字符串首字母大写
const firstUpperCase = ([first, ...rest]) => first.toUpperCase() + rest.join('');
  • css3 :empty
<ul></ul>
<ul>
  <li>item1</li>
  <li>item2</li>
  <li>item3</li>
  <li>item4</li>
</ul>
<style lang="scss">
ul {
  &:empty {
    width: 140px;
    height: 226px;
    background: url(https://ws3.sinaimg.cn/large/006tNbRwly1fwln1ad006g303w04sdlq.gif) no-repeat;
    background-size: cover; 
  }
}
</style>
  • 获取括号内内容
  matchParenthesisValue(string: string, size: "small" | "middle" | "large" = "small"): Array<string> {
    var regex1 = /\((.+?)\)/g;   // () 小括号
    var regex2 = /\[(.+?)\]/g;   // [] 中括号
    var regex3 = /\{(.+?)\}/g;  // {} 花括号,大括号
    switch (size) {
      case "small":
        return string.match(regex1).map(p => p.replace(/[\(|\)]/ig, ''));
      case "middle":
        return string.match(regex2).map(p => p.replace(/[\[|\\]/ig, ''));
      case "large":
        return string.match(regex3).map(p => p.replace(/[\{|\}]/ig, ''));
      default:
        return [];
    }
  }
  • 获取当天日期的前后天数日期
 /**
   *获取addDateCount前后的日期
   *
   * @param {number} addDateCount 日期天数,正值为之后天数,负值为之前天数
   * @returns date string
   */
  getFrontBackDate(addDateCount: number) {
    let date = new Date();
    date.setDate(date.getDate() + addDateCount);//获取addDayCount天后的日期
    return this.formatDate(date);
  },
 /**
   *格式化时间 型为: 2017-01-01
   *
   * @param {(string | Date)} date
   * @returns {string}
   */
  formatDate(date: string | Date): string {
    if (!date) return "";
    if (typeof date === "string") date = date.replace("T", " "); // 服务器返回的时间中含有T,部分浏览器解释不正确
    date = new Date(date);
    const year = date.getFullYear();
    const month = date.getMonth() + 1;
    const day = date.getDate();
    return [year, month, day].map(this.formatNumber).join("-");
  },
  /**
   *将个位数补上0
   *
   * @param {*} n
   * @returns
   */
  formatNumber(n) {
    n = n.toString();
    return n[1] ? n : "0" + n;
  },
  • chrome dev tools
    $0,$1,$2,$3,$4 ,获取选中后的节点,$0为当前选中$1为前一次,$2...依此类推
    $,$$,$console中是冗长的函数document.querySelector的一个别名。$$能节省更多的时间,因为它不仅仅执行document.QuerySelectorAll并且返回的是一个节点的数组,而不是一个Node list,从本质上说:Array.from(document.querySelectorAll('div')) === $$('div'),但是$$('div')要简短太多了!
    $_变量是关联上次执行的结果。
    copy:拷贝资源
    设置全局变量,只需要右击变量,并且选择"Store as global variable"
    console.assert:console.assert(assertion,obj1[,obj2,...,objN]);
    console.table: 数组(或者是类数组的对象,或者就是一个对象)使用console.table方法可以将它以一个漂亮的表格的形式打印出来.当列太多的时候,使用第二个参数,传入你想要展示的列对应的名字
    console.dir:用直接的表现形式来展示你的数据

  • vue自动注册组件,使用webpack require.context

import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
 
// Require in a base component context
const requireComponent = require.context(
 './components', false, /base-[\w-]+\.vue$/
)
 
requireComponent.keys().forEach(fileName => {
 // Get component config
 const componentConfig = requireComponent(fileName)
 
 // Get PascalCase name of component
 const componentName = upperFirst(
  camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, ''))
 )
 
 // Register component globally
 Vue.component(componentName, componentConfig.default || componentConfig)
})
import auth from './modules/auth'
import posts from './modules/posts'
import comments from './modules/comments'
// ...
 
export default new Vuex.Store({
 modules: {
  auth,
  posts,
  comments,
  // ...
 }
})

精简的做法和上面类似,也是运用 require.context()读取文件,代码如下:

import camelCase from 'lodash/camelCase'
const requireModule = require.context('.', false, /\.js$/)
const modules = {}
requireModule.keys().forEach(fileName => {
 // Don't register this file as a Vuex module
 if (fileName === './index.js') return
 
 const moduleName = camelCase(
  fileName.replace(/(\.\/|\.js)/g, '')
 )
 modules[moduleName] = {
        namespaced: true,
        ...requireModule(fileName),
       }
})
export default modules

这样我们只需如下代码就可以了:

import modules from './modules'
export default new Vuex.Store({
 modules
})
  • 原型链
    实例的__proto__属性(原型)等于其构造函数的prototype属性。
function Person(name) {
    this.name = name;
}
let p = new Person('Tom');
//问题:
p.__proto__===Person.prototype //true
Person.__proto__===Function.prototype ;//true

var foo={};
var F=function(){};
Object.prototype.a='value a';
Function.prototype.b = 'value b';

console.log(foo.a)   ;//value a
console.log(foo.b)    ;//undefined
console.log(F.a)     ;//value a
console.log(F.b)    ;//value b
  • 构造函数
    构造函数不需要显示的返回值。使用new来创建对象(调用构造函数)时,如果return的是非对象(数字、字符串、布尔类型等)会忽略返回值;如果return的是对象,则返回该对象(注:若return null也会忽略返回值)。
function Person(name) {
    this.name = name
    return name;
}
let p = new Person('Tom');//{name: 'Tom'}
//若改为:
function Person(name) {
    this.name = name
    return {}
}
let p = new Person('Tom');//{}
  • typeof和instanceof的区别
    typeof :在 JavaScript 中,判断一个变量的类型尝尝会用 typeof 运算符,在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 “object”.
    instanceof : 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
    语法:object instanceof constructor
    参数:object(要检测的对象.)constructor(某个构造函数)
    描述:instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
function Person (name) {
    this.name = name;
}
function Student () {}
Student.prototype = Person.prototype;
Student.prototype.constructor = Student;
let s = new Student('Tom');
console.log(s instanceof Person); // 返回 true
  • new和instanceof的内部机制
    创建一个新对象,同时继承对象类的原型,即Person.prototype;
    执行对象类的构造函数,同时该实例的属性和方法被this所引用,即this指向新构造的实例;
    如果构造函数return了一个新的“对象”,那么这个对象就会取代整个new出来的结果。如果构造函数没有return对象,那么就会返回步骤1所创建的对象,即隐式返回this。(一般情况下构造函数不会返回任何值,不过在一些特殊情况下,如果用户想覆盖这个值,可以选择返回一个普通的对象来覆盖。)
// let p = new Person()
let p = (function () {
    let obj = {};
    obj.__proto__ = Person.prototype;
    // 其他赋值语句...
    return obj;
})(); //p={}

下面通过代码阐述instanceof的内部机制,假设现在有x instanceof y一条语句,则其内部实际做了如下判断:

while(x.__proto__!==null) {
    if(x.__proto__===y.prototype) {
        return true;
        break;
    }
    x.__proto__ = x.__proto__.proto__;
}
if(x.__proto__==null) {return false;}

x会一直沿着隐式原型链proto向上查找直到x.proto.proto......===y.prototype为止,如果找到则返回true,也就是x为y的一个实例。否则返回false,x不是y的实例。

function F() {}
function O() {}

O.prototype = new F();
var obj = new O();

console.log(obj instanceof O); // true
console.log(obj instanceof F); // true
console.log(obj.__proto__ === O.prototype); // true
console.log(obj.__proto__.__proto__ === F.prototype); // true

根据new 的内部机制改写上面代码

function F() {}
function O() {}
var obj = (function () {
    var obj1 = {};
    obj1.__proto__ = F.prototype; // new F();
    O.prototype = obj1; // O.prototype = new F();
    obj.__proto__ = O.prototype; // new O();
    obj.__proto__ = obj1;
    return obj;
})

结合instanceof内部机制很容易得出正确答案。
如果稍微调整一下代码顺序,结果将迥然不同

function F() {};
function O() {};

var obj = new O();
O.prototype = new F();

console.log(obj instanceof O); // false
console.log(obj instanceof F); // false
console.log(obj.__proto__ === O.prototype); // false
console.log(obj.__proto__.__proto__ === F.prototype); // false

上个图,压压惊:


prototype
  • for...in... 和for...of...
    for..of适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象,因为没有迭代器对象.与forEach()不同的是,它可以正确响应break、continue和return语句.
    for-of循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用for-in循环(这也是它的本职工作)或内建的Object.keys()方法:
for (var key of Object.keys(someObject)) {
  console.log(key + ": " + someObject[key]);
}

遍历map对象时适合用解构,例如;

for (var [key, value] of phoneBookMap) {
   console.log(key + "'s phone number is: " + value);
}

当你为对象添加myObject.toString()方法后,就可以将对象转化为字符串,同样地,当你向任意对象添加myObject[Symbol.iterator]方法,就可以遍历这个对象了。
for...of的步骤
for-of循环首先调用集合的Symbol.iterator方法,紧接着返回一个新的迭代器对象。迭代器对象可以是任意具有.next()方法的对象;for-of循环将重复调用这个方法,每次循环调用一次。举个例子

var zeroesForeverIterator = {
 [Symbol.iterator]: function () {
   return this;
  },
  next: function () {
  return {done: false, value: 0};
 }
};
  • Generator
    Generator 有什么用? 它能够中断执行代码的特性,可以帮助我们来控制异步代码的执行顺序:
    定义了一个这样的 Generator 叫做 gen
function* gen() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
}

打印这个函数后发现了一个熟悉的函数,next() 方法,我们把 gen 实例化一下,执行一下它的 next() 来看看结果:

let g=gen();
g.next();// => {value: 1, done: false}
g.next();// => {value: 2, done: false}
g.next();// => {value: 3, done: false}
g.next();// => {value: 4, done: false}
g.next();// => {value: undefined, done: true

到这里,我们已经知道,Generator 可以实例化出一个 iterator ,并且这个 yield 语句就是用来中断代码的执行的,也就是说,配合 next() 方法,每次只会执行一个 yield 语句。

  • macrotask 和 microtask
console.log('a');
setTimeout(() => {
    console.log('b');
}, 0);
console.log('c');
Promise.resolve().then(() => {
    console.log('d');
})
.then(() => {
    console.log('e');
});

console.log('f');
//=> acfdeb
macrotask|microtask
  • Http请求中的keep-alive
    在http早期,每个http请求都要求打开一个tpc socket连接,并且使用一次之后就断开这个tcp连接。
    使用keep-alive可以改善这种状态,即在一次TCP连接中可以持续发送多份数据而不会断开连接。通过使用keep-alive机制,可以减少tcp连接建立次数,也意味着可以减少TIME_WAIT状态连接,以此提高性能和提高httpd服务器的吞吐率(更少的tcp连接意味着更少的系统内核调用,socket的accept()和close()调用)。
    但是,keep-alive并不是免费的午餐,长时间的tcp连接容易导致系统资源无效占用。配置不当的keep-alive,有时比重复利用连接带来的损失还更大。所以,正确地设置keep-alive timeout时间非常重要。

  • iis7 https站点服务 发生413错误 未显示页面,因为请求实体过大
    分析原因:因为https站点限制了上传文件的大小,所以上传的文件出现超时未上传到服务器上。
    解决方法:修改IIS上的UploadReadAheadSize值
    1.打开IIS管理器,在需要配置的网站主页打开“配置管理器”
    2.在最上边的“节”下拉框中选择“system.webServer/serverRuntime”
    3.可以看到“UploadReadAheadSize”默认值为49152,修改成你所需要的限制大小,这我修改了为10M

  • web端截取视频图片

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <video src="./static/video.mp4" controls="controls" width="360" height="360" id="video" preload="metadata" autoplay></video>
    <script>
      (() => {
        let captureInterval;
        let captureImage = stay => {
          let canvas = document.createElement("canvas");
          canvas.width = 360;
          canvas.height = 360;
          let ctx = canvas.getContext("2d");
          captureInterval = setInterval(() => {
            ctx.drawImage(video, 0, 0, 360, 360);
            // document.body.appendChild(canvas);
            //let img = document.createElement("img");
            let img = new Image();
            img.src = canvas.toDataURL("image/png");
            document.body.appendChild(img);
          }, stay * 1000);
        };
        let video = document.getElementById("video");
        video.addEventListener("loadeddata", captureImage(2));
        video.addEventListener("ended", () => {
          clearInterval(captureInterval);
        });
      })();
    </script>
  </body>
</html>
  • 实现防抖函数(debounce)
    防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
    那么与节流函数的区别直接看这个动画实现即可。
    点击查看图片
// 防抖函数
const debounce = (fn, delay) => {
  let timer = null;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
};

适用场景:
按钮提交场景:防止多次提交按钮,只执行最后提交的一次
服务端验证场景:表单验证需要服务端配合,只执行一段连续的输入事件的最后一次,还有搜索联想词功能类似
生产环境请用lodash.debounce

  • 实现节流函数(throttle)
    防抖函数原理:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
// 节流函数
const throttle = (fn, delay = 500) => {
  let flag = true;
  return (...args) => {
    if (!flag) return;
    flag = false;
    setTimeout(() => {
      fn.apply(this, args);
      flag = true;
    }, delay);
  };
};

适用场景:
拖拽场景:固定时间内只执行一次,防止超高频次触发位置变动
缩放场景:监控浏览器resize
动画场景:避免短时间内多次触发动画引起性能问题
生产环境请使用lodash.throttle

  • 深克隆(deepclone)

简单版

const newObj = JSON.parse(JSON.stringify(oldObj));
//局限性:
//他无法实现对函数 、RegExp等特殊对象的克隆
//会抛弃对象的constructor,所有的构造函数会指向Object
//对象有循环引用,会报错

递归版

/*
 * 对象深拷贝 局限同简单版
 * */
export const copyDeep = function (obj) {
  if(obj===null)return null;
  let str;
  let newobj = obj.constructor === Array ? [] : {};
  if (typeof obj !== 'object') {
    return;
  } else if (window.JSON) {
    str = JSON.stringify(obj); // 序列化对象
    newobj = JSON.parse(str); // 还原
  } else {
    for (const i in obj) {
      newobj[i] = typeof obj[i] === 'object'
        ? copyDeep(obj[i]) : obj[i];
    }
  }
  return newobj;
};

常规版

/**
 * deep clone
 * @param  {[type]} parent object 需要进行克隆的对象
 * @return {[type]}        深克隆后的对象
 */
const clone = parent => {
  // 判断类型
  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 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);
};
//局限性:
//一些特殊情况没有处理: 例如Buffer对象、Promise、Set、Map
//另外对于确保没有循环引用的对象,我们可以省去对循环引用的特殊处理,因为这很消耗时间

生产环境推荐使用lodash.cloneDeep

  • 实现Event(event bus)

event bus既是node中各个模块的基石,又是前端组件通信的依赖手段之一,同时涉及了订阅-发布设计模式,是非常重要的基础。
简单版:

class EventEmeitter {
  constructor() {
    this._events = this._events || new Map(); // 储存事件/回调键值对
    this._maxListeners = this._maxListeners || 10; // 设立监听上限
  }
}

// 触发名为type的事件
EventEmeitter.prototype.emit = function(type, ...args) {
  let handler;
  // 从储存事件键值对的this._events中获取对应事件回调函数
  handler = this._events.get(type);
  if (args.length > 0) {
    handler.apply(this, args);
  } else {
    handler.call(this);
  }
  return true;
};

// 监听名为type的事件
EventEmeitter.prototype.addListener = function(type, fn) {
  // 将type事件以及对应的fn函数放入this._events中储存
  if (!this._events.get(type)) {
    this._events.set(type, fn);
  }
};

常规版

class EventEmeitter {
  constructor() {
    this._events = this._events || new Map(); // 储存事件/回调键值对
    this._maxListeners = this._maxListeners || 10; // 设立监听上限
  }
}

// 触发名为type的事件
EventEmeitter.prototype.emit = function(type, ...args) {
  let handler;
  // 从储存事件键值对的this._events中获取对应事件回调函数
  handler = this._events.get(type);
  if (args.length > 0) {
    handler.apply(this, args);
  } else {
    handler.call(this);
  }
  return true;
};

// 监听名为type的事件
EventEmeitter.prototype.addListener = function(type, fn) {
  // 将type事件以及对应的fn函数放入this._events中储存
  if (!this._events.get(type)) {
    this._events.set(type, fn);
  }
};

// 触发名为type的事件
EventEmeitter.prototype.emit = function(type, ...args) {
  let handler;
  handler = this._events.get(type);
  if (Array.isArray(handler)) {
    // 如果是一个数组说明有多个监听者,需要依次此触发里面的函数
    for (let i = 0; i < handler.length; i++) {
      if (args.length > 0) {
        handler[i].apply(this, args);
      } else {
        handler[i].call(this);
      }
    }
  } else {
    // 单个函数的情况我们直接触发即可
    if (args.length > 0) {
      handler.apply(this, args);
    } else {
      handler.call(this);
    }
  }
  return true;
};

// 监听名为type的事件
EventEmeitter.prototype.addListener = function(type, fn) {
  const handler = this._events.get(type); // 获取对应事件名称的函数清单
  if (!handler) {
    this._events.set(type, fn);
  } else if (handler && typeof handler === "function") {
    // 如果handler是函数说明只有一个监听者
    this._events.set(type, [handler, fn]); // 多个监听者我们需要用数组储存
  } else {
    handler.push(fn); // 已经有多个监听者,那么直接往数组里push函数即可
  }
};

EventEmeitter.prototype.removeListener = function(type, fn) {
  const handler = this._events.get(type); // 获取对应事件名称的函数清单

  // 如果是函数,说明只被监听了一次
  if (handler && typeof handler === "function") {
    this._events.delete(type, fn);
  } else {
    let postion;
    // 如果handler是数组,说明被监听多次要找到对应的函数
    for (let i = 0; i < handler.length; i++) {
      if (handler[i] === fn) {
        postion = i;
      } else {
        postion = -1;
      }
    }
    // 如果找到匹配的函数,从数组中清除
    if (postion !== -1) {
      // 找到数组对应的位置,直接清除此回调
      handler.splice(postion, 1);
      // 如果清除后只有一个函数,那么取消数组,以函数形式保存
      if (handler.length === 1) {
        this._events.set(type, handler[0]);
      }
    } else {
      return this;
    }
  }
};
  • 实现instanceOf
// 模拟 instanceof
function instance_of(L, R) {
  //L 表示左表达式,R 表示右表达式
  var O = R.prototype; // 取 R 的显示原型
  L = L.__proto__; // 取 L 的隐式原型
  while (true) {
    if (L === null) return false;
    if (O === L)
      // 这里重点:当 O 严格等于 L 时,返回 true
      return true;
    L = L.__proto__;
  }
}
  • 模拟new
    new操作符做了这些事:
    它创建了一个全新的对象
    它会被执行[[Prototype]](也就是proto)链接
    它使this指向新创建的对象
    通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上
    如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用
// objectFactory(name, 'cxk', '18')
function objectFactory() {
  const obj = new Object();
  const Constructor = [].shift.call(arguments);
  obj.__proto__ = Constructor.prototype;
  const ret = Constructor.apply(obj, arguments);
  return typeof ret === "object" ? ret : obj;
}
  • 实现一个call
    call做了什么:
    将函数设为对象的属性
    执行&删除这个函数
    指定this到函数并传入给定参数执行函数
    如果不传入参数,默认指向为 window
// 模拟 call bar.mycall(null);
//实现一个call方法:
Function.prototype.myCall = function(context) {
  //此处没有考虑context非object情况
  context.fn = this;
  let args = [];
  for (let i = 1, len = arguments.length; i < len; i++) {
    args.push(arguments[i]);
  }
  context.fn(...args);
  let result = context.fn(...args);
  delete context.fn;
  return result;
};
  • 实现apply方法
    apply原理与call很相似,不多赘述
// 模拟 apply
Function.prototype.myapply = function(context, arr) {
  var context = Object(context) || window;
  context.fn = this;

  var result;
  if (!arr) {
    result = context.fn();
  } else {
    var args = [];
    for (var i = 0, len = arr.length; i < len; i++) {
      args.push("arr[" + i + "]");
    }
    result = eval("context.fn(" + args + ")");
  }

  delete context.fn;
  return result;
};
  • 实现bind
    实现bind要做什么
    返回一个函数,绑定this,传递预置参数
    bind返回的函数可以作为构造函数使用。故作为构造函数时应使得this失效,但是传入的参数依然有效
// mdn的实现
if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          // this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
          return fToBind.apply(this instanceof fBound
                 ? this
                 : oThis,
                 // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // 维护原型关系
    if (this.prototype) {
      // Function.prototype doesn't have a prototype property
      fNOP.prototype = this.prototype; 
    }
    // 下行的代码使fBound.prototype是fNOP的实例,因此
    // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
    fBound.prototype = new fNOP();
    return fBound;
  };
}
  • 模拟Object.create
    Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
// 模拟 Object.create
function create(proto) {
  function F() {}
  F.prototype = proto;

  return new F();
}
  • 实现类的继承
    类的继承在几年前是重点内容,有n种继承方式各有优劣,es6普及后越来越不重要,那么多种写法有点『回字有四样写法』的意思,如果还想深入理解的去看红宝书即可,我们目前只实现一种最理想的继承方式。
function Parent(name) {
    this.parent = name
}
Parent.prototype.say = function() {
    console.log(`${this.parent}: 你打篮球的样子像kunkun`)
}
function Child(name, parent) {
    // 将父类的构造函数绑定在子类上
    Parent.call(this, parent)
    this.child = name
}

/** 
 1. 这一步不用Child.prototype =Parent.prototype的原因是怕共享内存,修改父类原型对象就会影响子类
 2. 不用Child.prototype = new Parent()的原因是会调用2次父类的构造方法(另一次是call),会存在一份多余的父类实例属性
3. Object.create是创建了父类原型的副本,与父类原型完全隔离
*/
Child.prototype = Object.create(Parent.prototype);
Child.prototype.say = function() {
    console.log(`${this.parent}好,我是练习时长两年半的${this.child}`);
}

// 注意记得把子类的构造指向子类本身
Child.prototype.constructor = Child;

var parent = new Parent('father');
parent.say() // father: 你打篮球的样子像kunkun

var child = new Child('cxk', 'father');
child.say() // father好,我是练习时长两年半的cxk
  • 实现JSON.parse
var json = '{"name":"cxk", "age":25}';
var obj = eval("(" + json + ")");

此方法属于黑魔法,极易容易被xss攻击,还有一种new Function大同小异。

  • 模板引擎实现
let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let data = {
  name: '姓名',
  age: 18
}
render(template, data); // 我是姓名,年龄18,性别undefined
function render(template, data) {
  const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
  if (reg.test(template)) { // 判断模板里是否有模板字符串
    const name = reg.exec(template)[1]; // 查找当前模板里第一个模板字符串的字段
    template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
    return render(template, data); // 递归的渲染并返回渲染后的结构
  }
  return template; // 如果模板没有模板字符串直接返回
}
  • 查找字符串中出现最多的字符和个数
    例: abbcccddddd -> 字符最多的是d,出现了5次
let str = "abcabcabcbbccccc";
let num = 0;
let char = '';

 // 使其按照一定的次序排列
str = str.split('').sort().join('');
// "aaabbbbbcccccccc"

// 定义正则表达式
let re = /(\w)\1+/g;
str.replace(re,($0,$1) => {
    if(num < $0.length){
        num = $0.length;
        char = $1;        
    }
});
console.log(`字符最多的是${char},出现了${num}次`);
  • 网站地址保存到桌面

有的网站都有一个按钮,点一下就能把网站下载到本地桌面上,在本地桌面会出现一个.url的文件
制作方法介绍一种:
本地创建一个.txt文本文件,在文件里添加如下内容

[InternetShortcut]

URL=https://cn.bing.com/

其中URL=后面添加要跳转的网址,然后保存.
给这个文本文档命名为bing.url,再点击打开,就会跳转到https://cn.bing.com/这个网站上面了

var search = window.location.search;
var param = new URLSearchParams(window.location.search).get("tn");//80035161_1_dg
  • spellcheck
    html属性,规定输入的内容是否检查英文拼写
<textarea spellcheck="true"></textarea>
  • classList
    封装了操作元素类名的方法
<p class="title"></p>
let elem = document.querySelector("p");

//增加类名
elem.classList.add("title-new");//title title-new

//删除类名
elem.classList.remove("title");//title-new

//类名切换,有则删,无则增
elem.classList.toggle("title");

//替换类名
elem.classList.replace("title","title-0ld");//title-new title-old

//是否包含指定类名
elem.classList.contains("title");
  • getBoundingClientRect
    可以获取指定元素在当前页面的空间信息
elem.getBoundingClientRect();
//返回
{
  x:100,
  y:1200,//距离可是窗口(浏览器屏幕)的顶部距离,浏览器滚动,y值会变
  width:122,
  height:34,
  top:123,//距离文档顶部距离, 浏览器滚动,top值不变
  right:100,
  bottom:100,
  left:100
}
  • contains
    可以判断指定元素是否包含了指定的子元素
<div>
    <p></p>
</di>
document.querySelector("div").contains(document.querySelector("p"));//true
  • online state
window.addEventListener("online", xxx);

window.addEventListener("offline", () => {
  alert("你断网啦!");
});
  • battery state
    获取设备的电池状态:
navigator.getBattery().then(battery => console.log(battery));

// 返回
{
  charging, // 是否在充电
  chargingTime, // 充满电所需时间
  dischargingTime, // 当前电量可使用时间
  level, 剩余电量

  onchargingchange, // 监听充电状态变化
  onchargingtimechange, // 监听充满电所需时间变化
  ondischargingtimechange, // 监听当前电量可使用时间变化
  onlevelchange // 监听电量变化
}
  • vibration
    嘻嘻,使设备进行震动:
// 震动一次
navigator.vibrate(100);

// 连续震动,震动200ms、暂停100ms、震动300ms
navigator.vibrate([200, 100, 300]);
  • page visibility
    顾名思义,这个API是用来监听页面可见性变化的,在PC端标签栏切换、最小化会触发、在移动端程序切到后台会触发,简单说就是页面消失了
document.addEventListener("visibilitychange", () => {
  console.log(`页面可见性:${document.visibilityState}`);
});

使用场景:当程序切到后台的时候,如果当前有视频播放或者一些动画执行,可以先暂停

  • deviceOrientation
    陀螺仪,也就是设备的方向,又名重力感应,该API在IOS设备上失效的解决办法,将域名协议改成https;
window.addEventListener("deviceorientation", event => {
  let {
    alpha,
    beta,
    gamma
  } = event;

  console.log(`alpha:${alpha}`);
  console.log(`beta:${beta}`);
  console.log(`gamma:${gamma}`);
});

使用场景:页面上的某些元素需要根据手机摆动进行移动,达到视差的效果,比如王者荣耀进入游戏的那个界面,手机转动背景图会跟着动

  • toDataURL
    这个canvas的API,作用是将画布的内容转换成一个base64的图片地址:
let canvas = document.querySelector("canvas");
let context = canvas.getContext("2d");

// 画东西
...

let url = canvas.toDataURL("image/png"); // 将画布内容转换成base64地址

将图片地址封装成点击直接下载

const downloadImage = (url, name) => {
  // 实例化画布
  let canvas = document.createElement("canvas");
  let context = canvas.getContext("2d");

  // 实例化一个图片对象
  let image = new Image();
  image.crossOrigin = "Anonymous";
  image.src = url;

  // 当图片加载完毕
  image.onload = () => {
    // 将图片画在画布上
    canvas.height = image.height;
    canvas.width = image.width;
    context.drawImage(image, 0, 0);

    // 将画布的内容转换成base64地址
    let dataURL = canvas.toDataURL("image/png");

    // 创建a标签模拟点击进行下载
    let a = document.createElement("a");
    a.hidden = true;
    a.href = dataURL;
    a.download = name;

    document.body.appendChild(a);
    a.click();
  }
}
  • customEvent
    自定义事件,就跟vue里面的on跟emit一样;
    监听自定义事件:
window.addEventListener("follow", event => {
  console.log(event.detail); // 输出 {name: "前端宇宙情报局"}
});

派发自定义事件:

window.dispatchEvent(new CustomEvent("follow", {
  detail: {
    name: "前端宇宙情报局"
  }
}));
  • notification
    PC端的桌面通知,如网页端的微信,当收到消息时,右下角会出现一个通知(尽管你把浏览器最小化),因为这个通知时独立于浏览器的,是系统的一个原生控件:
const notice = new Notification("前端宇宙情报局", {
  body: "web 端 常用api",
  icon: "",
  data: {
    url: "https://www.baidu.com"
  }
});

// 点击回调
notice.onclick = () => {
  window.open(notice.data.url); // 当用户点击通知时,在浏览器打开百度网站
}

注意:想要成功的调起通知,首先要用户的授权

Notification.requestPermission(prem => {
  prem == "granted" // 同意
  prem == "denied" // 拒绝
})

所以,再调用之前先向用户发起请求:

let permission = Notification.permission;

if (permission == "granted") {
  // 已同意,开始发送通知
  ...
} else if (permission == "denied") {
  // 不同意,发不了咯
} else {
  // 其他状态,可以重新发送授权提示
  Notification.requestPermission();
}
  • fullScreen
    全屏不了? 之前的一个项目刚好用上,不仅仅可以作用在documentElement上,还可以作用在指定元素:
/**
 * @method launchFullScreen 开启全屏
 * @param {Object} elem = document.documentElement 作用的元素
 */
const launchFullScreen = (elem = document.documentElement) => {
  if(elem.requestFullScreen) {
    elem.requestFullScreen();
  } else if(elem.mozRequestFullScreen) {
    elem.mozRequestFullScreen();
  } else if(elem.webkitRequestFullScreen) {
    elem.webkitRequestFullScreen();
  }
}

关闭全屏的时候需要注意的是,统一用document对象:

/**
 * @method exitFullScreen 关闭全屏
 */
const exitFullScreen = () => {
  if (document.exitFullscreen) {
    document.exitFullscreen();
  } else if (document.mozCancelFullScreen) {
    document.mozCancelFullScreen();
  } else if (document.webkitCancelFullScreen) {
    document.webkitCancelFullScreen();
  }
}
  • orientation
    可以监听用户手机设备的旋转方向变化:
window.addEventListener("orientationchange", () => {
  document.body.innerHTML += `<p>屏幕旋转后的角度值:${window.orientation}</p>`;
}, false);

也可以使用css的媒体查询:

/* 竖屏时样式 */
@media all and (orientation: portrait) {
  body::after {
    content: "竖屏"
  }
}

/* 横屏时样式 */
@media all and (orientation: landscape) {
  body::after {
    content: "横屏"
  }
}
  • 关于vue嵌套路由
    当需要嵌套路由时,父级路由也需要组件,然后在父级组件中添加<router-view></router-view>即可继续渲染子路由,这里面的注意点就是必须要在父级上加上<router-view></router-view>,否则无法渲染页面
    相关链接:https://router.vuejs.org/zh/guide/essentials/nested-routes.html

  • css insertRule 动态插入 参考insertRule

/**
 * 在文档中添加一条样式表规则(这可能是动态改变 class 名的更好的实现方法,
 * 使得 style 样式内容可以保留在真正的样式表中,以斌面添加额外的元素到 DOM 中)。
 * 注意这里有必要声明一个数组,因为 ECMAScript 不保证对象按预想的顺序遍历,
 * 并且 CSS 也是依赖于顺序的。
 * 类型为数组的参数 decls 接受一个 JSON 编译的数组。
 * @example
addStylesheetRules([
  ['h2', // 还接受第二个参数作为数组中的数组
    ['color', 'red'],
    ['background-color', 'green', true] // 'true' for !important rules 
  ], 
  ['.myClass', 
    ['background-color', 'yellow']
  ]
]);
 */
//与官网有改动
function addStylesheetRules(decls, ruleName = null) {
    var style = document.createElement('style');
    document.getElementsByTagName('head')[0].appendChild(style);
    if (!window.createPopup) { /* For Safari */
        style.appendChild(document.createTextNode(''));
    }
    var s = document.styleSheets[document.styleSheets.length - 1];
    if (ruleName !== null && ruleName.length > 0) {
        s.ruleName = ruleName;
    }
    for (var i = 0, dl = decls.length; i < dl; i++) {
        var j = 1, decl = decls[i], selector = decl[0], rulesStr = '';
        if (Object.prototype.toString.call(decl[1][0]) === '[object Array]') {
            decl = decl[1];
            j = 0;
        }
        for (var rl = decl.length; j < rl; j++) {
            var rule = decl[j];
            rulesStr += rule[0] + ':' + rule[1] + (rule[2] ? ' !important' : '') + ';\n';
        }

        if (s.insertRule) {
            s.insertRule(selector + '{' + rulesStr + '}', s.cssRules.length);
        }
        else { /* IE */
            s.addRule(selector, rulesStr, -1);
        }
    }
}


/*
* 从当前样式表中删除指定的样式规则(自己实现,不一定没有问题)
*/
function deleteStylesheetRule(index, ruleName = null) {
    if (ruleName === null) return;
    let myStyles = Array.from(document.styleSheets).filter(p => p.ruleName === ruleName);
    myStyles.forEach(style => {
        if (style.cssRules.length > index)
            style.deleteRule(index);
    });
}

实现效果:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>css insertRule</title>
    <script src="./util.js"></script>
</head>

<body>
    <div>
        <p>css insertRule</p>
        <div id="dom_1">测试css insertRule</div>
    </div>
    <script>
        addStylesheetRules([['#dom_1', ['color', "black"], ["background-color", "green"]], ["#dom_1", ['font-size', '2rem'], ['margin', '20px']]],'dom_1');
        addStylesheetRules([['#dom_1', ['width', "200px"]]]);
        setTimeout(() => {
            console.log("delete rules")
            deleteStylesheetRule(1,'dom_1');
        }, 3000);

    </script>
</body>
</html>
insertRules效果

插入后的style样式是这样的


insertRule样式
  • cookie
    如果设置了httpOnly = true 则在js端 使用document.cookie 是获取不到该cookie的, 需要在后台的http

  • 下载文件方法:

//下载文件流
export function downloadStreamFile(data, fileName, blobOption = null,) {
  const blob = new Blob([data], blobOption);//处理文档流
  const alink = document.createElement('a');
  alink.download = fileName;
  alink.style.display = 'none';
  alink.href = URL.createObjectURL(blob);
  document.body.appendChild(alink);
  alink.click();
  URL.revokeObjectURL(alink.href); // 释放URL 对象
  document.body.removeChild(alink);
}
  • 下载图片方法:
//下载图片
export function downloadImage(data = "base64 or link", fileName) {
  const a = document.createElement("a");
  a.setAttribute("download", fileName || "下载");
  a.setAttribute("href", data);
  const evt = document.createEvent("MouseEvents");
  evt.initEvent("click", true, true);
  a.dispatchEvent(evt);
}
  • 创建对象时, 对象key由变量值确定的处理方法, 变量加[var]的方式
let a=[1,2,3];
let o=a.map(p=>({p}));  
// 结果: [{"p":1},{"p":2},{"p":3}]  , key值直接用的变量字面量字符串, value使用了变量的值
let oo = a.map(p=>({[p]:p}));
//结果: [{"1":1},{"2":2},{"3":3}]   ,  key和value同时使用了变量的值
  • 下标和上标的一些字符
₊₋₌₀₁₂₃₄₅₆₇₈₉₍₎⁺⁻⁼⁰¹²³⁴⁵⁶⁷⁸⁹⁽⁾

可在这个网站https://unicode-table.com/cn/search/上查询

  • css样式 - 鼠标滚动只影响子集不影响父级
    场景: 在弹出的dialog中滚动鼠标时会影响到父级滚动, 这不是我们想要的.
    解决方案有多种:
  1. 可以使用js捕获滚动事件, 使用然后作用到子级dom上, 阻止父级dom的事件即可
  2. 动态修改父级dom的overflow css属性值为hidden
  3. 在子元素上应用css属性, 也是最简单的方式,如下
overscroll-behavior: contain;

简单介绍如下:

overscroll-behavior CSS 属性是 overscroll-behavior-x 和 overscroll-behavior-y 属性的合并写法, 让你可以控制浏览器过度滚动时的表现——也就是滚动到边界。
属性介绍: auto - 默认。元素的滚动会传播给祖先元素。 contain - 阻止滚动链接,滚动不会传播给祖先。 none - 和 contain 效果一样
相关链接: https://developer.mozilla.org/zh-CN/docs/Web/CSS/overscroll-behavior

  • js读取SVG源码
var svgDomString = new XMLSerializer().serializeToString(document.getElementById("svg"))
  • 关闭 favicon 请求
    只需要将如下请求favicon改成如下即可
<link rel="icon" href="data:,">
  • JavaScript生成UUID
function getUUID() {
  const temp_url = URL.createObjectURL(new Blob());
  const uuid = temp_url.toString();
  URL.revokeObjectURL(temp_url);
  return uuid.substring(uuid.lastIndexOf('/') + 1);
}

three.js中的生成UUID方法

const _lut = [ '00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff' ];

/**
 * three.js 中的生成UUID方法
 * @returns {string}
 */
export function generateUUID() {

  const d0 = Math.random() * 0xffffffff | 0;
  const d1 = Math.random() * 0xffffffff | 0;
  const d2 = Math.random() * 0xffffffff | 0;
  const d3 = Math.random() * 0xffffffff | 0;
  const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' +
    _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' +
    _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] +
    _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ];

  // .toLowerCase() here flattens concatenated strings to save heap memory space.
  return uuid.toLowerCase();
}
<input id="js_input3" class="weui-input" placeholder="填写联系电话" type="number" pattern="[0-9]*"/>
  • 阻止number输入框滚动滚轮改变数值问题 (vue3)
// 阻止number输入框滚动滚轮改变数值问题
const stopMouseWheel = {
  updated:function (el) {
    el.addEventListener('mousewheel',() => {
      const elem = el.tagName === 'INPUT' ? el : el.querySelector('input');
      elem.blur();
    })
  }
};
export default stopMouseWheel;

main.js 中

import stopMouseWheel from "@/utils/stopMouseWheel";
const app = createApp(App);
app.directive("stopMouseWheel",stopMouseWheel);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容