前端常见编程题

2020-10-04  本文已影响0人  我是jayliang

CSS篇

垂直居中

方式一

<!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>垂直居中</title>
  </head>
  <style>
    .wrapper {
      overflow: hidden;
      width: 1000px;
      height: 500px;
      background: #999;
    }
    .center {
      width: 18em;
      height: 10em;
      text-align: center;
      background-color: orange;
      color: #fff;

      margin: 50vh auto;
      transform: translateY(-50%);
    }
    
  </style>
  <body>
    <div class="wrapper">
      <div class="center">
        基于视口的垂直居中<br />
        不要求原生有固定的宽高。<br />
        但是这种居中是在整个页面窗口内居中,不是基于父元素<br />

      </div>
    </div>
  </body>
</html>

方式二

<!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>垂直居中</title>
    <style>
        .center {
            width: 18em;
            height: 10em;
            position: absolute;
            background: orange;
            text-align: center;
            top: 50%;
            left: 50%;
            margin-left: -9em;
            margin-top: -5em;
            /* 或者
            transform: translate(-9em, -5em); */
        }
    </style>
</head>

<body>
    <div class="center">
        要求原生有固定的宽高。<br />
        position: absolute;<br />
        top和left 为 50%;<br />
        margin上为高的一半<br />
        margin左为宽的一半<br />
    </div>
</body>

</html>

方式三

<!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>垂直居中</title>
  </head>
  <style>
    .center {
      width: 18em;
      height: 10em;
      text-align: center;
      background-color: orange;
      color: #fff;

      position: absolute;
      top: calc(50% - 5em);
      left: calc(50% - 9em);
    }
  </style>
  <body>
    <div class="center">
        要求原生有固定的宽高。<br/>
        position: absolute;<br/>
        top 为 calc(50% 剪 一半高)
        left 为 calc(50% 剪 一半宽)
    </div>

  </body>
</html>

方式四

<!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>垂直居中</title>
  </head>
  <style>
    .center {
      width: 18em;
      height: 10em;
      text-align: center;
      background-color: orange;
      color: #fff;

      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
  </style>
  <body>
    <div class="center">
        不要求原生有固定的宽高。<br/>
        position: absolute;<br/>
        top和left 为 50%;<br/>
        transform: translate(-50%, -50%);
    </div>

  </body>
</html>

方式五

<!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>垂直居中</title>
  </head>
  <style>
    .wrapper {
      width: 1000px;
      height: 600px;
      background: #999;

      display: flex;
    }
    .center {
      width: 18em;
      height: 10em;
      text-align: center;
      background-color: orange;
      color: #fff;

      margin: auto;
    }
  </style>
  <body>
    <div class="wrapper">
      <div class="center">
        使用flex居中<br/>
        父元素 display: flex; <br/>
        居中块: margin: auto;
      </div>
    </div>
  </body>
</html>

方式六

<!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>垂直居中</title>
  </head>
  <style>
    .wrapper {
      width: 1000px;
      height: 600px;
      background: #999;

      display: flex;
      justify-content: center;
      align-items: center;
    }
    .center {
      width: 18em;
      height: 10em;
      text-align: center;
      background-color: orange;
      color: #fff;
    }
  </style>
  <body>
    <div class="wrapper">
      <div class="center">
        使用flex居中<br/>
        父元素 display: flex; <br/>
        justify-content: center;<br/>
        align-items: center;<br/>
      </div>
    </div>
  </body>
</html>

水平居中

方式一

<!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>水平居中</title>
    <style>
        .wrapper {
            text-align: center;
            height: 200px;
            background: orange;
        }

        .center {
            display: inline-block;
            width: 100px;
            height: 100px;
        }
    </style>
</head>

<body>
    <div class="wrapper">
        <div class="center">如果需要居中的元素为常规流中 inline / inline-block 元素,为父元素设置 text-align: center;</div>
    </div>
</body>

</html>

方式二

<!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>水平元素居中</title>
  </head>
  <style>
    .wrapper {
      width: 100%;
      height: 500px;

      text-align: center; /* 3 */
    }
    .center {
      width: 500px;
      text-align: left; 
      margin: 0 auto; 

      background-color: orange;
    }
  </style>
  <body>
    <div class="wrapper">
      <div class="center">
          父元素上设置 text-align: center;<br />
          居中元素上margin 为 auto。
      </div>
    </div>
  </body>
</html>

方式三

<!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>水平元素居中</title>
  </head>
  <style>
    .wrapper {
      width: 80%;
      height: 500px;
      background: #888;
      
      position: relative;
    }
    .center {
      width: 500px;
      position: absolute;
      left: 50%;
      margin-left: -250px;

      background-color: orange;
    }
  </style>
  <body>
    <div class="wrapper">
      <div class="center">如果元素positon: absolute; 那么 0)设置父元素postion: relative 1)为元素设置宽度,2)偏移量设置为 50%,3)偏移方向外边距设置为元素宽度一半乘以-1</div>
    </div>
  </body>
</html>

方式四

<!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>水平元素居中</title>
  </head>
  <style>
    .wrapper {
      width: 80%;
      height: 500px;
      background: #888;
    }
    .center {
      width: 500px;
      position: relative;
      left: 50%;
      margin-left: -250px;

      background-color: orange;
    }
  </style>
  <body>
    <div class="wrapper">
      <div class="center">如果元素positon: relative。 那么 1)为元素设置宽度,2)偏移量设置为 50%,3)偏移方向外边距设置为元素宽度一半乘以-1</div>
    </div>
  </body>
</html>

布局

绝对定位

<!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>绝对定位</title>
    <style>
        html,
        body {
            margin: 0;
            padding: 0;
        }

        * {
            margin: 0;
            padding: 0;
        }

        aside {
            position: absolute;
            width: 300px;
            min-height: 100px;
        }

        left {
            left: 0;
            background-color: red;
        }

        .right {
            right: 0;
            background-color: blue;
        }

        .center {
            position: absolute;
            left: 300px;
            right: 300px;
            background-color: orange;
        }
    </style>
</head>

<body>
    <aside class="left"></aside>
    <aside class="right"></aside>
    <main class="center">
        <h1>绝对定位解决方案</h1>
        <p>左右区域分别postion:absolute,固定到左右两边</p>
        <p>中间区域postion:absolute;left:300px; right: 300px</p>
        <p>给总的宽度加一个min-width,不然缩小窗口会有毛病</p>
    </main>
</body>

</html>

三栏-表格布局

<!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>表格布局</title>
    <style>
        html,
        body {
            margin: 0;
            padding: 0;
        }

        .wrapper {
            display: table;
            width: 100%;
        }

        .left,
        .main,
        .right {
            min-height: 100px;
            display: table-cell;
        }

        .left {
            width: 100px;
            background-color: red;
        }

        .main {
            background-color: orange;
        }

        .right {
            width: 200px;
            background-color: blue;
        }
    </style>
</head>

<body>
    <div class="wrapper">
        <aside class="left"></aside>
        <main class="main">
            <h1>表格布局</h1>
            <p>父元素display:table并且宽度为100%</p>
            <p>每一个子元素display:table-cell</p>
            <p>左右两侧添加宽度,中间不加宽度</p>
        </main>
        <aside class="right"></aside>
    </div>
</body>

</html>

三栏-浮动方案

<!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>浮动方案</title>
    <style>
        html,
        body {
            padding: 0;
            margin: 0;
        }

        .left,
        .right,
        .main {
            height: 200px;
        }

        .left {
            float: left;
            width: 100px;
            background-color: blue;
        }

        .main {
            background-color: seagreen;
            width: 100%;
        }

        .right {
            float: right;
            width: 100px;
            background-color: red;
        }
    </style>
</head>

<body>
    <aside class="left"></aside>
    <aside class="right"></aside>
    <main class="center">
        <h1>浮动解决方案</h1>
        <p>方法:left和right都写在center前面,并且分别左右浮动</p>
        <p>中间的这个div因为是块级元素,所以在水平方向按照他的包容快自动撑开</p>
        <p>简单,但是中心部分过长下面会溢出,然后文字就会跑到两边去。</p>
    </main>
</body>

</html>

三栏-网格布局

<!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>网格布局</title>
    <style>
        html,
        body {
            padding: 0;
            margin: 0;
        }

        .wrapper {
            display: grid;
            width: 100%;
            grid-template-columns: 100px 1fr 100px;
        }

        .left {
            background-color: red;
        }

        .center {
            background-color: orange;
        }

        .right {
            background-color: blue;
        }
    </style>
</head>

<body>
    <div class="wrapper">
        <aside class="left"></aside>
        <main class="center">
            <h1>网格布局</h1>
            <p>父元素display:grid</p>
        </main>
        <aside class="right"></aside>
    </div>
</body>

</html>

三栏-flex布局

<!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>flex布局</title>
    <style>
        html,
        body {
            padding: 0;
            margin: 0;
        }

        .wrapper {
            display: flex;
            height: 200px;
        }

        .left {
            width: 100px;
            background-color: seagreen;
        }

        .right {
            width: 200px;
            background-color: sienna;
        }

        .main {
            flex: 1;
            background-color: springgreen;
        }
    </style>
</head>

<body>
    <div class="wrapper">
        <aside class="left"></aside>
        <main class="main">
            <h1>flex布局</h1>
        </main>
        <aside class="right"></aside>
    </div>
</body>

</html>

圣杯布局

<!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>圣杯布局</title>
    <style>
        html,
        body {
            padding: 0;
            margin: 0;
        }

        .left {
            width: 100px;
            background-color: springgreen;
        }

        .right {
            width: 200px;
            background-color: steelblue;
        }

        .main {
            width: 100%;
            background-color: red;

        }



        /* 关键代码 */
        .left,
        .right,
        .main {
            float: left;
            position: relative;
            height: 200px;

        }

        .left {
            margin-left: -100%;
            left: -100px;
        }

        .container {
            padding-left: 100px;
            padding-right: 200px;
        }

        .right {
            margin-left: -200px;
            right: -200px;
        }
    </style>
</head>

<body>
    <div class="container">
        <div class="main"></div>
        <div class="left"></div>
        <div class="right"></div>
    </div>
</body>

</html>

双飞翼布局

<!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>双飞翼布局</title>
    <style>
        html,
        body {
            padding: 0;
            margin: 0;
        }

        .left,
        .right,
        .main {
            min-height: 200px;
        }

        .left {
            width: 200px;
            background-color: thistle;
        }

        .main {
            background: #999;
            width: 100%;
        }

        .right {
            width: 300px;
            background-color: violet;
        }

        /*关键代码*/
        .left,
        .right,
        .main {
            float: left;
        }

        .main-inner {
            margin-left: 200px;
            margin-right: 300px;
        }

        .left {
            margin-left: -100%;
        }

        .right {
            margin-left: -300px;
        }
    </style>
</head>

<body>
    <div class="main">
        <div class="main-inner">中心区</div>
    </div>
    <div class="left">left</div>
    <div class="right">right</div>
</body>

</html>

JS篇

闭包问题

循环中赋值为引用的问题

for (var i = 1; i < 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}

解决方法有3种

第一种,使用立即执行函数方式

for (var i = 1; i < 5; i++) {
  (function(j){
    setTimeout(function timer() {
      console.log(j)
    }, j * 1000)
  })(i)
}

第二种,使用ES6的let

for (let i = 1; i < 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}

第三种,使用setTimeout的第三个参数

for (var i = 1; i < 5; i++) {
  setTimeout(function timer(j) {
    console.log(j)
  }, i * 1000, i)
}

计数器

实现一个foo函数 可以这么使用:

a = foo();
b = foo();
c = foo();
// a === 1;b === 2;c === 3;
foo.clear();d = foo(); //d === 1;
function myIndex() {
    var index = 1;

    function foo(){
        return index++;
    }

    foo.clear = function() {
        index = 1;
    }
    return foo;
}

var foo = myIndex();

防抖节流

防抖 debounce

函数防抖就是在函数需要频繁触发的情况下,只有足够的空闲时间,才执行一次。

典型应用

function debounce(handler, delay){

  delay = delay || 300;
  var timer = null;

  return function(){

    var _self = this,
        _args = arguments;

    clearTimeout(timer);
    timer = setTimeout(function(){
      handler.apply(_self, _args);
    }, delay);
  }

为啥要记录this

// 频繁触发时,清楚对应的定时器,然后再开一个定时器,delay秒后执行
function debounce(handler, delay){

  delay = delay || 300;
  var timer = null;

  return function(){

    var _self = this,
        _args = arguments;

    clearTimeout(timer);
    timer = setTimeout(function(){
      handler.apply(_self, _args);
    }, delay);
  }
}

// 不希望被频繁调用的函数
function add(counterName) {
  console.log(counterName + ":  " + this.index ++);
}

// 需要的上下文对象
let counter = {
  index: 0
}

// 防抖的自增函数,绑定上下文对象counter
let db_add = debounce(add, 10).bind(counter)

// 每隔500ms频繁调用3次自增函数,但因为防抖的存在,这3次内只调用一次
setInterval(function() {
  db_add("someCounter1");
  db_add("someCounter2");
  db_add("someCounter3");
}, 500)


/**
 * 预期效果:
 * 
 * 每隔500ms,输出一个自增的数
 * 即打印:
    someCounter3:  0
    someCounter3:  1
    someCounter3:  2
    someCounter3:  3
 */

节流 throttle

一个函数只有在大于执行周期时才执行,周期内调用不执行。好像水滴积攒到一定程度才会触发一次下落一样。

典型应用:

function throttle(fn,wait=300){
    var lastTime = 0
    return function(){
        var that = this,args=arguments
        var nowTime = new Date().getTime()
        if((nowTime-lastTime)>wait){
            fn.apply(that,args)
            lastTime = nowTime
        }
    }
}

观察者模式

JS观察者模式

观察者模式:观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。而js中最常见的观察者模式就是事件触发机制。

先来个完整的

class EventEmitter {
  constructor () {
    this.eventPool = {
    //  'eventName': []
    }
  }
  listen(eventName, callback) {
    if(this.eventPool[eventName]) {
      if(this.eventPool[eventName].indexOf(callback) === -1) {
        this.eventPool[eventName].push(callback)
      }
    } else {
      this.eventPool[eventName] = [callback]
    }
  }
  // trigger是有参数的
  trigger(eventName, ...args) {
    if(this.eventPool[eventName]) {
      this.eventPool[eventName].forEach(cb => cb(...args))
    }
  }
  remove(eventName, callback) {
    if(this.eventPool[eventName]) {
        let cbIndex = this.eventPool[eventName].indexOf(callback)
        this.eventPool[eventName].splice(cbIndex, 1)
    }
  }
  once(eventName, callback) {
    this.listen(eventName, function _cb(...args) {
      callback(...args);
      this.remove(eventName, _cb)
    })  
  }
}

ES5/ES6实现观察者模式(自定义事件) - 简书

先搭架子

  1. 要有一个对象,存储着它自己的触发函数。而且这个对象的触发函数可能有很多种,比如一个onclick可能触发多个事件,那么handler的属性应该是一个数组,每个数组的值都是一个函数。
handler={
  type1:[func1,func2...],
  type2:[func3,func4...],
  ...
}

现在这个对象的主体部分已经思考好了,现在就是要它‘动起来’,给它添加各种动作。
一个事件可能有哪些动作呢?

现在,自定义事件的架子已经搭建好了

eventOb={
  //函数储存
  handler:{
    type1:[func1,func2...],
    type2:[func2,func4...],
    ...
  },

  //主要事件
  add:function(){},
  remove:function(){},
  fire:function(){},
  once:function(){},
}

add

添加一个事件监听,首先传入参数应该是 事件类型type,和触发函数 func,传入的时候检测有没有这个函数,有了就不重复添加。

add:function (type,func) {
  //检测type是否存在
  if(eventOb.handleFunc[type]){
    //检测事件是否存在,不存在则添加
    if(eventOb.handleFunc[type].indexOf(func)===-1){
      eventOb.handleFunc[type].push(func);
    }
  }
  else{
    eventOb.handleFunc[type]=[func];
  }
},

remove

remove有一个潜在的需求,就是如果你的事件不存在,它应该会报错。而这里不会报错,index在func不存在的时候是-1;这时候要报错。

remove:function (type,func) {
  try{
    let target = eventOb.handleFunc[type];
    let index = target.indexOf(func);
    if(index===-1) throw error;
    target.splice(index,1);
  }catch (e){
      console.error('别老想搞什么飞机,删除我有的东西!');
  }
},

fire

触发一个点击事件肯定是要触发它全部的函数,这里也是一样,所以只需要传入type,然后事件可能不存在,像上面一样处理。

fire:function (type,func) {
  try{
    let target = eventOb.handleFunc[type];
    let count = target.length;
    for (var i = 0; i < count; i++) {
      //加()使立即执行
      target[i]();
    }    
  }
  catch (e){
    console.error('别老想搞什么飞机,触发我有的东西!');
  }
},

但会有问题,我只想触发并且删除某个事件怎么办,fire一下就全触发了呀。
所以fire的问题就显现出来了。我们还是要给它一个func,但是可选。

fire:function (type,func) {
  try{
    let target = eventOb.handleFunc[type];
    if(arguments.length===1) {
      //不传func则全部触发
      let count = target.length;
      for (var i = 0; i < count; i++) {
          target[i]();
      }
    }else{
      //传func则触发func
      let index=target.indexOf(func);
      if(index===-1)throw error;
      func();
    }
    //need some code
  }catch (e){
    console.error('别老想搞什么飞机,触发我有的东西!');
    //need some code
  }
},

once

fire,然后remove


once (event, callback) {
  this.fire(event, (...args) => {
      callback(...args);
      this.remove(event)
  })
}

完整代码

class eventObs {
  constructor(){
    this.handleFunc={}
  }

  add(type,func){
    if(this.handleFunc[type]){
        if(this.handleFunc[type].indexOf(func)===-1){
            this.handleFunc[type].push(func);
        }
    }else{
        this.handleFunc[type]=[func];
    }
  };

  fire(type,func){
    try{
      if(arguments.length===1) {
          let target = this.handleFunc[type];
          let count = target.length;
          for (var i = 0; i < count; i++) {
              target[i]();
          }
      }else{
          let target = this.handleFunc[type];
          let index=target.indexOf(func);
          if(index===-1)throw error;
          func();
      }
      return true;
    }catch (e){
        console.error('别老想搞什么飞机,触发我有的东西!');
        return false;
    }
  };

  remove(type,func){
      try{
          let target = this.handleFunc[type];
          let index=target.indexOf(func);
          if(index===-1)throw error;
          target.splice(index,1);
      }catch (e){
          console.error('别老想搞什么飞机,删除我有的东西!');
      }

  };

  once(type,func) {

    this.fire(type, func)
    ? this.remove(type, func)
    : null;
  }
}

尽早顺序打印Ajax请求

/**
 * 接受一个URL数组做参数,并行请求,尽可能块的按照顺序打印内容
 */


const urlList = [1, 2, 3, 4, 5]
loadData(urlList)

function fetchData(url, succCallback) {
    setTimeout(() => {
        succCallback('ok: ' + url);
    }, (Math.random() * 5 * 1000) >> 0);
}

function loadData(urlList) {
    let resArr = [],
        doneId = 0
    for (let i = 0; i < urlList.length; i++) {
        fetchData(urlList[i], res => {
            console.log(`${i+1} is done`)
            resArr[i] = res
            outPutRes(resArr)
        })
    }

    function outPutRes(resArr) {
        for (let i = doneId; i < resArr.length; i++) {
            if (resArr[i]) {
                console.log(resArr[i]);
                doneId++;
            } else {
                break;
            }
        }
    }
}

curry

柯里化(英语:Currying),又称为部分求值,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回一个新的函数的技术,新函数接受余下参数并返回运算结果。

实现一个add方法,使计算结果能够满足如下预期:

add(1)(2)(3) = 6
add(1, 2)(3) = 10

实现方法: 做一个闭包,返回一个函数,这个函数每次执行会改写闭包里面记录参数的数组。当这个函数判断参数个数够了,就去执行它。

function curry(func) {
    // 存储已传入参数
    let _args = []

    // 做一个闭包
    function _curry(...args) {
        // 把参数合并
        _args = _args.concat(args)

        // 如果参数够了就执行
        if (_args.length >= func.length) {
            const result = func(..._args)
            _args = []
            return result;
        }
        // 继续返回此函数 
        else {
            return _curry
        }
    }
    return _curry
}
// 测试代码
function add1(a, b, c) {
    return a + b + c
}
let testAdd = curry(add1)
console.log(testAdd(1)(2)(3))
console.log(testAdd(1, 2)(3))
console.log(testAdd(1)(2, 3))

实现一个类型判断函数

  1. 判断null
  2. 判断基础类型
  3. 使用Object.prototype.toString.call(target)来判断引用类型

注意: 一定是使用call来调用,不然是判断的Object.prototype的类型
之所以要先判断是否为基本类型是因为:虽然Object.prototype.toString.call()能判断出某值是:number/string/boolean,但是其实在包装的时候是把他们先转成了对象然后再判断类型的。 但是JS中包装类型和原始类型还是有差别的,因为对一个包装类型来说,typeof的值是object

/**
 * 类型判断
 */
function getType(target) {
  //先处理最特殊的Null
  if(target === null) {
    return 'null';
  }
  //判断是不是基础类型
  const typeOfT = typeof target
  if(typeOfT !== 'object') {
    return typeOfT;
  }
  //肯定是引用类型了
  const template = {
    "[object Object]": "object",
    "[object Array]" : "array",
    "[object Function]": "function",
    // 一些包装类型
    "[object String]": "object - string",
    "[object Number]": "object - number",
    "[object Boolean]": "object - boolean"
  };
  const typeStr = Object.prototype.toString.call(target);
  return template[typeStr];
}

实现一个 sleep 函数

比如 sleep(1000) 意味着等待1000毫秒,可从 Promise、Generator、Async/Await 等角度实现

Promise

const sleep = time =>{
    new Promise((resolve) => {
        setTimeout(resolve, time)
    })
}

sleep(1000).then(() => {
    console.log(1)
})

Generator

function *sleep(time) {
    yield new Promise(resolve => {
        setTimeout(resolve, time)
    })
}
sleep(1000).next().value.then(() => {
    console.log(1)
})

async

async function sleep(time, func) {
    await new Promise(resolve => setTimeout(resolve, time))
    return func()
}
sleep(1000, () => {
    console.log(1)
})

ES5

function sleep(callback,time) {
  if(typeof callback === 'function')
    setTimeout(callback,time)
}

function output(){
  console.log(1);
}
sleep(output,1000);

异步编程

promise与setTimeout 判断执行顺序

promise和setTimeout都会将事件放入异步队列,但setTimeout即便是写0,也会有4ms的延迟

console.log('begin');

setTimeout(() => {

  console.log('setTimeout 1');

  Promise.resolve()
    .then(() => {
      console.log('promise 1');
      setTimeout(() => {
          console.log('setTimeout2');
      });
    })
    .then(() => {
      console.log('promise 2');
    });

  new Promise(resolve => {
    console.log('a');
    resolve();
  }).then(() => {
    console.log('b');
  });

}, 0);
console.log('end');

答案

begin
end
setTimeout 1
a
promise 1
b
promise 2
setTimeout2

async函数的使用

function repeat(func, times, wait) {
    
}
// 输入
const repeatFunc = repeat(alert, 4, 3000);

// 输出
// 会alert4次 helloworld, 每次间隔3秒
repeatFunc('hellworld');
// 会alert4次 worldhellp, 每次间隔3秒
repeatFunc('worldhello')

我自己的实现,没有成功。这种实现是setTimeout新建了两个,然而只清理了一个。

function repeat(func, times, wait) {
  var timer = null;
  var count = 0;
  return function(...args) {
    timer = setInterval(function() {
      func.apply(null, args);
      count ++;
      console.log('count', count, "times", times)
      if( count >= times) {
        clearInterval(timer);
      }
    }, wait);
  }
}
// 输入
const repeatFunc = repeat(console.log, 4, 3000);
// 输出
// 会alert4次 helloworld, 每次间隔3秒
repeatFunc('hellworld');
// 会alert4次 worldhellp, 每次间隔3秒
repeatFunc('worldhello');

正确解法:使用 async/await来实现

async function wait(seconds) {
    return new Promise((res) => {
        setTimeout(res, seconds);
    });
}

function repeat(func, times, s) {
    return async function (...args) {
        for (let i = 0; i < times; i++) {
            func.apply(null, args);
            await wait(s);
        }
    };
}

let log = console.log
let repeatFunc = repeat(log,4,3000)
repeatFunc('HelloWorld')
repeatFunc('WorldHello')

async执行练习

async function async1() {
  console.log('async1 start');  // 2
  await async2();
  console.log('async1 end');    // 6
}

async function async2() {     
  console.log('async2');        // 3
}

console.log('script start');    //  1

setTimeout(function() {
  console.log('setTimeout');    // 8
}, 0);

async1();

new Promise(function(resolve) {
  console.log('promise1');      // 4
    resolve();
  }).then(function() {
      console.log('promise2');  // 7
  });

console.log('script end');      // 5

bind、apply实现

自封装bind方法

/**
 * 简单版本 
 */
Function.prototype.myBind = (that, ...args) => {
  const funcThis = this;
  return function(..._args) {
    return funcThis.apply(that, args.concat(_args));
  }
}

Function.prototype.mybind = function(ctx) {
    var _this = this;
    var args = Array.prototype.slice.call(arguments, 1);
    return function() {
        return _this.apply(ctx, args.concat(args, Array.prototype.slice.call(arguments)))
    }
}
/**
 * 自封装bind方法
 * @param  {对象} target [被绑定的this对象, 之后的arguments就是被绑定传入参数]
 * @return {[function]}  [返回一个新函数,这个函数就是被绑定了this的新函数]
 */
Function.prototype.myBind = function (target){
    target = target || window;
    var self = this;
    var args = [].slice.call(arguments, 1);
    var temp = function(){};
    var F = function() {
    var _args = [].slice.call(arguments, 0);
        return self.apply(this instanceof temp ? this: target, args.concat(_args));
    }
    temp.prototype = this.prototype;    //当函数是构造函数时,维护原型关系
    F.prototype = new temp();
    return F;
}

自封装一个apply

Function.prototype.myApply = function(context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  context = context || window
  context.fn = this
  let result
  // 处理参数和 call 有区别
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}

deepclone

const typeMap = {
    '[object Array]': 'array',
    '[object Object]': 'object',
    '[object Function]': 'function',
    '[object Symbol]': 'symbol',
    '[object RegExp]': 'regexp'
}

function deepClone(target, map = new WeakMap()) {
    let cloneTarget
    let type = typeMap[getType(target)]
    if (type === 'symbol') {
        //处理symbol
        return Object(Symbol.prototype.valueOf.call(target));
    } else if (type === 'function') {
        //处理function

        return cloneFunction(target)
    } else if (type === 'object' || type === 'array') {
        cloneTarget = getInit(target)
    } else {
        return target
    }
    //避免循环引用
    if (map.get(target)) {
        return map.get(target)
    } else {
        map.set(target, cloneTarget)
    }
    //遍历
    for (const key in target) {
        cloneTarget[key] = deepClone(target[key], map)
    }
    return cloneTarget

    function getInit(target) {
        const constructor = target.constructor
        return new constructor()
    }

    function getType(target) {
        return Object.prototype.toString.call(target)
    }

    function cloneFunction(func) {
        const bodyReg = /\{([\s\S]*)\}$/;
        const paramReg = /(?<=\().+(?=\)\s+{)/;
        const funcString = func.toString();
        if (func.prototype) {
            console.log('普通函数');
            const param = paramReg.exec(funcString);
            const body = bodyReg.exec(funcString);
            console.log(body)
            if (body) {
                console.log('匹配到函数体:', body[0]);
                if (param) {
                    const paramArr = param[0].split(',');
                    console.log('匹配到参数:', paramArr);
                    return new Function(...paramArr, body[0]);
                } else {
                    return new Function(body[0]);
                }
            } else {
                return null;
            }
        } else {
            return eval(funcString);
        }
    }
}


const target = {
    //待处理正则
    field1: 1,
    field2: undefined,
    field3: {
        child: 'child'
    },
    field4: [2, 4, 8],
    field6: function (age, w) {
        console.log(age, w)
    },
    field7: Symbol('www')
};

target.target = target;
let t = deepClone(target)
// t.field3.child = '2'
// console.log(target)
// console.log(t)
console.log(deepClone(target))

flat

//数组摊平为一维
let arr = [1, 2, [3, 4, 5, [6, 7], 8], 9, 10, [11, [12, 13]]]

let result = []

function flat(arr) {
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            flat(arr[i])
        } else {
            result.push(arr[i])
        }
    }
}

flat(arr)
console.log(result)

jsonp

//http://www.baidu.com?aa=11&callback=my_jsonp04349289664328899
var jsonp = function (url, param, callback) {
    //处理url地址,查找?,如果没有?这个变量就有一个"?",有?这个变量接收一个&
    var querystring = url.indexOf("?") == -1 ? "?" : "&";
    //处理参数{xx:xx}
    for (var k in param) {
        querystring += k + "=" + param[k] + '&'; //?k=para[k]
    }
    //处理回调函数名
    var random = Math.random().toString().replace(".", "");
    var cbval = "my_jsonp" + random;
    var cb = "callback=" + cbval;

    querystring += cb;

    var script = document.createElement("script");
    script.src = url + querystring;

    //把回调函数的名字附给window
    window[cbval] = function (param) {
        //这里执行回调的操作,用回调来处理参数
        callback(param);
        //拿到了就删掉这个script
        document.body.removeChild(script);
    };
    document.body.appendChild(script);
}

jsonp(
    "https://www.baidu.com", {
        aa: 11
    },
    function () {
        console.log(param);
    }
);

reduce

reduce

累加累乘

function Accumulation(...vals) {
    return vals.reduce((t, v) => t + v, 0);
}

function Multiplication(...vals) {
    return vals.reduce((t, v) => t * v, 1);
}

Accumulation(1, 2, 3, 4, 5); // 15
Multiplication(1, 2, 3, 4, 5); // 120

权重求和

const scores = [{
        score: 90,
        subject: "chinese",
        weight: 0.5
    },
    {
        score: 95,
        subject: "math",
        weight: 0.3
    },
    {
        score: 85,
        subject: "english",
        weight: 0.2
    }
];

const result = scores.reduce((total, cur) => total + cur.score * cur.weight, 0)

reverse

let arr = [1, 2, 3, 4, 5]

let result

result = arr.reduceRight(function (total, cur) {
    return total.concat(cur)
}, [])
console.log(result);//[ 5, 4, 3, 2, 1 ]

实现map

let arr = [0, 2, 4, 6]

let result

let map = v => v * 2
//map
result = arr.reduce((total, cur) => {
    return total.concat(map(cur))
}, [])
console.log(result);


扁平化

let arr = [0, 1, [2, 3],
    [4, 5, [6, 7]],
    [8, [9, 10, [11, 12]]]
];

let result

function flat(arr) {
    return arr.reduce((total, cur) => Array.isArray(cur) ? total.concat(flat(cur)) : total.concat(cur), [])
}
console.log(flat(arr)) //[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]

去重

let arr = [2, 1, 0, 3, 2, 1, 2];

let result

function uniq(arr) {
    return arr.reduce((total, cur) => total.includes(cur) ? total : total.concat(cur), [])
}
console.log(uniq(arr)) // [2, 1, 0, 3]

this指向

this指向

面试题目

请分别写出下面题目的答案。

function Foo() {
    getName = function() {
        console.log(1);
    };
    return this;
}
Foo.getName = function() {
    console.log(2);
};
Foo.prototype.getName = function() {
    console.log(3);
};
var getName = function() {
    console.log(4);
};

function getName() {
    console.log(5);
}

//请写出以下输出结果:
Foo.getName();      //-> 2    Foo对象上的getName() ,这里不会是3,因为只有Foo的实例对象才会是3,Foo上面是没有3的
getName();          //-> 4    window上的getName,console.log(5)的那个函数提升后,在console.log(4)的那里被重新赋值
Foo().getName();    //-> 1    在Foo函数中,getName是全局的getName,覆盖后输出 1
getName();          //-> 1    window中getName();
new Foo.getName();  //-> 2    Foo后面不带括号而直接 '.',那么点的优先级会比new的高,所以把 Foo.getName 作为构造函数
new Foo().getName();//-> 3    此时是Foo的实例,原型上会有输出3这个方法

箭头函数中的this 判断

箭头函数里面的this是继承它作用域父级的this, 即声明箭头函数处的this

let a = {
  b: function() { 
    console.log(this) 
  },
  c: () => {
    console.log(this)
  }
}

a.b()   // a
a.c()   // window

let d = a.b
d()     // window

this判断 下面输出为多少?

var name1 = 1;

function test() {
    let name1 = 'kin';
    let a = {
        name1: 'jack',
        fn: () => {
      var name1 = 'black'
      console.log(this.name1)
    }
  }
    return a;
}

test().fn() // ?

答案: 输出1

因为fn处绑定的是箭头函数,箭头函数并不创建this,它只会从自己的作用域链的上一层继承this。这里它的上一层是test(),非严格模式下test中this值为window。

最后

行文至此,感谢阅读,如果您喜欢的话,可以帮忙点个like哟~

欢迎转载,但要注明出处哟~​

上一篇 下一篇

猜你喜欢

热点阅读