前端面试手写

原理实现

call()

  • 不传入第一个参数,那么上下文默认为 window
  • 改变了 this 指向,让新的对象可以执行该函数,并能接受参数

Function.prototype.myCall = function(context) {
  if (typeof this !== 'function') {
    throw Error("not a function")
  }
  context = context || window
  context.fn = this
  const args = [...arguments].slice(1)
  const result = context.fn(...args)
  delete context.fn
  return result
}```
- 首先 `context` 为可选参数,如果不传的话默认上下文为 `window`
- 接下来给 `context` 创建一个 `fn` 属性,并将值设置为需要调用的函数
- 因为 `call` 可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来
- 然后调用函数并将对象上的函数删除

​```
let foo = { value: 1 }
function bar(name, age) {
  console.log(name)
  console.log(age)
  console.log(this.value);
}
bar.call2(foo, 'black', '18') // black 18 1

apply()

apply 的实现也类似,区别在于对参数的处理


Function.prototype.myApply = function(context) {
  if (typeof this !== 'function') {
    throw Error("not a function")
  }
  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
}

bind()

bind 需要返回一个函数,需要判断一些边界问题

Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  const _this = this
  const args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

分析区别:

  • bind 返回了一个函数,对于函数来说有两种方式调用,一种是直接调用,一种是通过 new 的方式,我们先来说直接调用的方式
  • 对于直接调用来说,这里选择了 apply 的方式实现,但是对于参数需要注意以下情况:因为 bind 可以实现类似这样的代码 f.bind(obj, 1)(2),所以我们需要将两边的参数拼接起来,于是就有了这样的实现 args.concat(...arguments)
  • 最后来说通过 new 的方式,在之前的章节中我们学习过如何判断 this,对于 new 的情况来说,不会被任何方式改变 this,所以对于这种情况我们需要忽略传入的 this

防抖

即短时间内大量触发同一事件,只会执行一次函数,防抖常用于搜索框/滚动条的监听事件处理,如果不做防抖,每输入一个字/滚动屏幕,都会触发事件处理,造成性能浪费;实现原理为设置一个定时器,约定在xx毫秒后再触发事件处理,每次触发事件都会重新设置计时器,直到xx毫秒内无第二次操作。

// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 50) => {
  // 缓存一个定时器id
  let timer = 0
  // 这里返回的函数是每次用户实际调用的防抖函数
  // 如果已经设定过定时器了就清空上一次的定时器
  // 开始一个新的定时器,延迟执行用户传入的方法
  return function(...args) {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      func.apply(this, args)
    }, wait)
  }
}

节流

防抖是延迟执行,而节流是间隔执行,和防抖的区别在于,防抖每次触发事件都重置定时器,而节流在定时器到时间后再清空定时器,函数节流即每隔一段时间就执行一次,实现原理为设置一个定时器,约定xx毫秒后执行事件,如果时间到了,那么执行函数并重置定时器

// func是用户传入需要防抖的函数
// wait是等待时间
const throttle = (func, wait = 50) => {
  // 上一次执行该函数的时间
  let lastTime = 0
  return function(...args) {
    // 当前时间
    let now = +new Date()
    // 将当前时间和上一次执行函数时间对比
    // 如果差值大于设置的等待时间就执行函数
    if (now - lastTime > wait) {
      lastTime = now
      func.apply(this, args)
    }
  }
}

setInterval(
  throttle(() => {
    console.log(1)
  }, 500),
  1
)

深复制

  • 会不会忽略 undefined
  • 会不会忽略 symbol
  • 能不能序列化函数
  • 能不能解决循环引用的对象
  • 在遇到函数、 undefined 或者 symbol 的时候,该对象能不能正常的序列化
  • 处理原型链
  • 处理DOM
  • 处理Date
  • 处理Reg
  • 处理ES6类
  • 处理null
  • 处理boolen
  • 处理array
  • 处理string
  • 处理number
  • 处理自定义对象
  function cloneObject(target, source) {
     //获取该对象的所有属性的集合
    var propNames = Object.getOwnPropertyNames(source) 
    for (let i = 0 i < names.length i++) {
       //获取属性的描述对象
       var desc = Object.getOwnPropertyDescriptor(source, propNames[i])  
       //解决多层对象与循环引用的问题,同时处理嵌套的DOM、正则、实例、Date等特殊对象
       if (typeof desc.value === "object" && desc.value !== null) {
            var obj
            //单独处理DOM;用原型链判断DOM类型
            if (desc.value instanceof HTMLElement) {
                //使用document.createElement(标签名)创建DOM
                obj = document.createElement(desc.value.nodeName) 
            } else {
                //其他对象使用constructor判断类型---大写;使用 new constructor(arguments)构建
                switch (desc.value.constructor) {
                  case Box:
                  obj = new desc.value.constructor(desc.value[Box.ARG[0]],                                       desc.value[Box.ARG[1]]) 
                  break
                  case RegExp://(正则,修饰符)
                  obj = new desc.value.constructor(desc.value.source,desc.value.flags) 
                   break
                  default :
                  obj = new desc.value.constructor() 
                 } 
               }
           //递归多层对象
           cloneObject(obj, desc.value) 
           //然后在设置属性的描述对象
           Object.defineProperty(target, propNames[i], {
                     value:obj,
                     enumerable:desc.enumerable,
                     writable:desc.writable,
                     configurable:desc.configurable
                 }) 
       } 
        else {
         //第一层不是对象或者为null直接使用Object.defineProperty自定义属性
         Object.defineProperty(target, propNames[i], desc) 
       }
    }
    return target
  } 
  
  var obj1 = cloneObject({}, obj) 
  
    obj.d.k.m = 100
  
    console.log(obj1) 
var obj = { 
    a:1,  
    b:"a",
    c:false,
    d:{
       e:undefined,
       f:null,
       g:[1, 2, 3, 4, 5],
       h:new Date(),
       i:/^[a-z]{2,4}$/gi,
       j:new Box(4, 5),
       k:{}
      }
     }
     
  class Box {
    static ARG = ["a", "b"]
    constructor(a1, b1) {
       this.a = a1
       this.b = b1
    }
    play() {
     console.log(this.a1 + this.b1) 
    }
  }
  Object.defineProperties(obj.d.k, {
    l:{ value:10 },
    m:{
    configurable:true,
    writable:true,
    value:20
        },  
    n:{
    enumerable:true, 
    value:function() {  
       console.log("aaaa") 
    }
  },
    o:{
    value:new Image()
  }  
  })

轮播图

export default class Utils{
    static ce(type, style, data) {
        var elem = document.createElement(type);
        Object.assign(elem.style, style);
        return elem;
    }
}
import Utils from "./Utils.js";

export default class Carousel {
    widthRem = 0;
    heightRem = 0;
    bnArray = [  ];
    position = 0;
    direction = "left";
    imgConX = 0;
    speed=0.5;
    auto=true;
    _dotBool=true;
    static carouselList=[  ];

constructor( _imgList = [  ], _bnList = [  ], _width = screen.width,
 						_height = screen.width / 3) {
        this.fontSize = parseInt(getComputedStyle(document.documentElement).fontSize);
        this.widthRem = _width / this.fontSize;
        this.heightRem = _height / this.fontSize;
        this.imgList = _imgList;
        this.bnList = _bnList;
        this.elem = this.createElem(  );
        Carousel.carouselList.push(this);
    }

    createElem(  ) {
        if (this.elem) return this.elem;
        var carousel = Utils.ce("div", {
            width: this.widthRem + "rem",
            height: this.heightRem + "rem",
            position: "relative",
            overflow: "hidden",
        });
        this.createImgCon(carousel);
        this.createBn(carousel);
        this.createDot(carousel);
        carousel.addEventListener("mouseenter", e => { this.mouseHandler(e) });
        carousel.addEventListener("mouseleave", e => { this.mouseHandler(e) });
        //初次执行变色函数,给第一个小圆点设置样式
        this.changePre(  );
        window.addEventListener("resize", e => { this.resizeHandler(e) });
        this.resizeHandler(  );
        return carousel;
    }
    appendTo(parent) {
        parent.appendChild(this.elem);
        //设置圆点位置
        this.dot.style.left = (this.widthRem - this.dot.offsetWidth / 100) / 2 + "rem";
    }
    source(list){
        this.imgList = list;
    }
    createImgCon(parent) {
        this.imgCon = Utils.ce("div", {
            width: this.widthRem * 2 + "rem",
            height: this.heightRem + "rem",
            position: "absolute",
            left: 0
        });
        var img = Utils.ce("img", {
            width: this.widthRem + "rem",
            height: this.heightRem + "rem",
        });
        img.src = this.imgList[0];
        this.imgCon.appendChild(img);
        parent.appendChild(this.imgCon);
    }
    createBn(parent) {
        for (let i = 0; i < this.bnList.length; i++) {
            var bn = Utils.ce("img", {
                position: "absolute",
                top: (this.heightRem - 0.6) / 2 + "rem",
                left: i === 0 ? "0.3rem" : "none",
                right: i === 1 ? "0.3rem" : "none"
            });
            bn.src = this.bnList[i];
            parent.appendChild(bn);
            this.bnArray.push(bn);
            bn.addEventListener("click", e => { this.clickHandler(e) });
        }
    }
    set dotBool(value){
        this._dotBool=value;
        this.dot.style.display=value ? "block" :"none"
    }
    get dotBool(  ){
        return this._dotBool;
    }
    createDot(parent) {
        this.dot = Utils.ce("ul", {
            position: "absolute",
            listStyle: "none",
            padding: "0px",
            margin: "0px",
            bottom: "0.4rem",
            // display:this.dotBool ? "block" :"none"
        });
        for (let i = 0; i < this.imgList.length; i++) {
            let dots = Utils.ce("li", {
                width: "0.2rem",
                height: "0.2rem",
                borderRadius: "0.15rem",
                float: "left",
                marginLeft: i === 0 ? "0rem" : "0.1rem",
                border: "2px solid #FF0000"
            });
            this.dot.appendChild(dots);
        }
        parent.appendChild(this.dot);
        this.dot.addEventListener("click", e => { this.dotClickHandler(e) });
    }
    changePre(  ) {
        if (this.pre) {//this
            this.pre.style.backgroundColor = "rgba(255,0,0,0)";
        }
        this.pre = this.dot.children[this.position];
        this.pre.style.backgroundColor = "rgba(255,0,0,0.8)";
    }

    createNextImg(  ) {//this
        var img = Utils.ce("img", {
            width: this.widthRem + "rem",
            height: this.heightRem + "rem",
        })
        img.src = this.imgList[this.position];
        if (this.direction === "left") {
            this.imgCon.appendChild(img);
            this.imgConX = 0;
        } else if (this.direction === "right") {
            this.imgCon.insertBefore(img, this.imgCon.firstChild);
            this.imgConX = -this.widthRem;
        }
        this.imgCon.style.left = this.imgConX + "rem";
        this.bool = true;
        this.changePre (  );
    }

    mouseHandler(e) {//this
        if(!this.auto) return;
        if (e.type === "mouseenter") {
            this.autoBool = false;
        } else if (e.type === "mouseleave") {
            this.autoBool = true;
            this.time = 200;
        }
    }
    clickHandler(e) {//this
        if (this.bool) return;
        if (e.currentTarget.src.indexOf("left") < 0) {
            this.direction = "left";
            this.position++;
            if (this.position > this.imgList.length - 1) this.position = 0;
        } else {
            this.direction = "right";
            this.position--;
            if (this.position < 0) this.position = this.imgList.length - 1;
        }
        this.createNextImg(  );
    }
    dotClickHandler(e) {//this
        if (this.bool) return;
        if (e.target.nodeName !== "LI") return;
        var dotList = Array.from(this.dot.children);
        var index = dotList.indexOf(e.target);
        this.direction = index > this.position ? "left" : "right";
        this.position = index;
        this.createNextImg();
    }
    resizeHandler(e) {//this
        document.documentElement.style.fontSize = innerWidth / screen.width * this.fontSize + "px";
    }
    update(  ) {
        this.imgConMove(  );
        this.autoMove(  );
    }
    imgConMove(  ) {//this
        if(!this.bool)return;
        if(this.direction==="left"){
            this.imgConX-=this.speed;
            this.imgCon.style.left=this.imgConX+"rem";
            if(this.imgConX<-this.widthRem){
                this.bool=false;
                this.imgConX=0;
                this.imgCon.firstElementChild.remove();
                this.imgCon.style.left=this.imgConX+"rem";
            }
        }else if(this.direction==="right"){
            this.imgConX+=this.speed;
            this.imgCon.style.left=this.imgConX+"rem";
            if(this.imgConX>=0){
                this.bool=false;
                this.imgConX=0;
                this.imgCon.lastElementChild.remove();
                this.imgCon.style.left=this.imgConX+"rem";
            }
        }
    }
    autoMove(  ) {//this
        if (!this.autoBool) return;
        this.time--;
        if (this.time > 0) return;
        this.time = 200;
        var evt = new MouseEvent("click");
        this.bnArray[1].dispatchEvent(evt);
    }
    dispose(  ){
        let index=Carousel.carouselList.indexOf(this);
        if(index<0) return;
        Carousel.carouselList.splice(index,1);
    }
    static UPDATE(  ){
        for(let i=0;i<Carousel.carouselList.length;i++){
            Carousel.carouselList[i].update(  );
        }
    }
}
    <style>
        /* 全局设置要单独设置 ,rem*/
        html{
            font-size: 100px;
        }
        body{
            font-size: 16px;
            margin: 0;
        }
    </style>

</head>
<body>
 <script type="module">
        import Carousel from "./js/Carousel.js";
        var bnArr=["./img/left.png","./img/right.png"];
        var arr=["./img/a.jpg","./img/b.jpg","./img/c.jpg","./img/d.jpg","./img/e.jpg"]
        let carousel=new Carousel(arr,bnArr,600,200)

    carousel.appendTo(document.body)
  </script>

发布—订阅模式(观察者模式)

// 发布订阅模式
class EventEmitter {
    constructor() {
        // 事件对象,存放订阅的名字和事件
        this.events = {};
    }
    // 订阅事件的方法
    on(eventName,callback) {
       if (!this.events[eventName]) {
           // 注意时数据,一个名字可以订阅多个事件函数
           this.events[eventName] = [callback]
       } else  {
          // 存在则push到指定数组的尾部保存
           this.events[eventName].push(callback)
       }
    }
    // 触发事件的方法
    emit(eventName) {
        // 遍历执行所有订阅的事件
       this.events[eventName] && this.events[eventName].forEach(cb => cb());
    }
    // 移除订阅事件
    removeListener(eventName, callback) {
        if (this.events[eventName]) {
            this.events[eventName] = this.events[eventName].filter(cb => cb != callback)
        }
    }
    // 只执行一次订阅的事件,然后移除
    once(eventName,callback) {
        // 绑定的时fn, 执行的时候会触发fn函数
        let fn = () => {
           callback(); // fn函数中调用原有的callback
           this.removeListener(eventName,fn); // 删除fn, 再次执行的时候之后执行一次
        }
        this.on(eventName,fn)
    }
}

Promise的实现

class Promise {
  constructor(executorCallBack) {
      //value变量用于保存resolve 或者reject 中传入的值
      //fulfillAry和rejectedAry用于保存then中的回调,因为当执行完Promise时状态可能还是等待中,这时候应该把then中的回调保存起来用于状态改变时使用
    this.status = 'pending';
    this.value = undefined;
    this.fulfillAry = [];
    this.rejectedAry = [];
    //=>执行Excutor
    //首先两个函数都得判断当前状态是否为等待中,因为规范规定只有等待态才可以改变状态
    //将当前状态更改为对应状态,并且将传入的值赋值给value;遍历回调数组并执行
    let resolveFn = result => {
      if (this.status !== 'pending') return;
      //为了保证函数执行顺序,需要将两个函数体代码使用setTimeout包裹起来
      let timer = setTimeout(() => {
        this.status = 'fulfilled';
        this.value = result;
        this.fulfillAry.forEach(item => item(this.value));
        clearTimeout(timer);
      }, 0);
    };
    let rejectFn = reason => {
      if (this.status !== 'pending') return;
      let timer = setTimeout(() => {
        this.status = 'rejected';
        this.value = reason;
        this.rejectedAry.forEach(item => item(this.value));
        clearTimeout(timer);
      }, 0);
    };
    //执行传入的参数并且将之前两个函数当做参数传进去
    //可能执行函数过程中会遇到错误,需要捕获错误并且执行reject函数
    try {
      executorCallBack(resolveFn, rejectFn);
    } catch (err) {
      //=>有异常信息按照rejected状态处理
      rejectFn(err);
    }
  }
    
  //then第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数
  //then返回的是一个新的promise, 将以回调的返回值来resolve.  
  //执行fulfilledCallBack或rejectedCallBack函数时会返回一个x
  //判断x是否为对象或者函数,如果都不是的话,将x传入resolve中;如果x是对象或者函数的话,先把x.then赋值给then,然后判断then的类型,如果不是函数类型的话,就将x传入resolve中。
  then(fulfilledCallBack, rejectedCallBack) {
   fulfilledCallBack= typeof fulfilledCallBack === 'function' ? 
        fulfilledCallBack : result => result;
   rejectedCallBack= typeof rejectedCallBack === 'function' ? 
        rejectedCallBack:reason => throw new Error(reason)
    //每次then的函数返回值都会被Promise.resolve重新包装
    return new Promise((resolve, reject) => {
      this.fulfillAry.push(() => {
        try {
          let x = fulfilledCallBack(this.value);
          x instanceof Promise ? x.then(resolve, reject) : resolve(x);
        } catch (err) {
          reject(err)
        }
      });
      this.rejectedAry.push(() => {
        try {
          let x = this.rejectedCallBack(this.value);
          x instanceof Promise ? x.then(resolve, reject) : resolve(x);
        } catch (err) {
          reject(err)
        }
      })
    });
  } 
  //返回一个promise对象
  static resolve(value) {
    //value值是一个Promise对象,则直接返回该对象
    if (value instanceof Promise) return value
    return new Promise(resolve => resolve(value))
  }
  //返回一个状态为失败的Promise对象
  static reject(value) {
    //并将给定的失败信息传递给对应的处理方法
    return new Promise((resolve, reject) => reject(value))
  }
}

  //如果在resolve后再throw错误,是不会被catch到的,因为状态改变后不可逆)  
  catch(rejectedCallBack) {
    return this.then(null, rejectedCallBack);
  }

static all(promiseAry = []) {
    let index = 0,
      result = [];
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promiseAry.length; i++) {
        promiseAry[i].then(val => {
          index++;
          result[i] = val;
          if (index === promiseAry.length) {
            resolve(result)
          }
        }, reject);
      }
    })
  }
    
  static race(promiseAry) {
    return new Promise((resolve, reject) => {
      if (promiseAry.length === 0) {
        return;
      }
      for (let i = 0; i < promiseAry.length; i++) {
        promiseAry[i].then(val => {
          resolve(val);
          return;
        }, reject);
      }
    })
  }

vue手写Tabbar

<template>
    <div>
    <footer>
      <ul>
        <router-link tag="li" to="/home" class="flex">
           <i  class="image-icons icon-home"></i>
           <span>首页</span>
        </router-link>
        <router-link tag="li" to="/category" class="flex">
           <i  class="image-icons icon-category"></i>
           <span>分类</span>
        </router-link>
        <router-link tag="li" to="/discover" class="flex">
          <i  class="image-icons icon-star"></i>
          <span>星球</span>
        </router-link>
        <router-link tag="li" to="/cart" class="flex">
          <i  class="image-icons  icon-cart"></i>
          <span>购物车</span>
        </router-link>
        <router-link tag="li" to="/user" class="flex">
          <i  class="image-icons  icon-user"></i>
          <span>我的</span>
        </router-link>
      </ul>
    </footer>
    </div>
</template>


<style lang='scss' scoped>
    ul {
      height: 100%;
      display: flex;
      align-items: center;
      .flex {
        flex: 1 1 auto
        flex-direction: column;
        text-align: center;
        display: flex;
        justify-content: center;
        align-items: center;
        span {
          margin-top: 3px;
        }
      }
    }

    .router-link-active {
      color: #ff6700;
      .image-icons {
        background-color: #ff6700;
      }
    }  
.icon-home{
    background-image: url(../assets/images/icon-home.c1947eda40.png)
}
.icon-category{
    background-image: url(../assets/images/icon-category.be93273636.png)
}
.icon-star{
    background-image: url(../assets/images/icon-star.d051789804.png)
}
.icon-cart{
    background-image: url(../assets/images/icon-cart.585c3aa4d3.png)
}
.icon-user{
    background-image: url(../assets/images/icon-user.23496a0116.png)
}

</style>

react手写tabbar

import React, { Component } from 'react'
import {
  BrowserRouter as Router,
  Route,Link,useLocation, Redirect,Switch} from 'react-router-dom'
import HomePage from './Home'
import Position from './Position'
import Login from './Login'
import User from './User'

export default class index extends Component {
  constructor() {
    super();
    this.state = {
      islogin: false
    }
    this.routers = [
      {
        path: "/home", component: HomePage, auth: false
      },
      {
        path: "/position", component: Position, auth: true
      }, {
        path: "/user", component: User, auth: true
      }
    ]
  }
  handleLogin = () => {
    this.setState({
      islogin: true
    })
  }
  render() {
    return (
      <Router>
        <Link to="/home">首页</Link> 
        <Link to="/position">职位管理</Link> 
        <Link to="/user">用户管理</Link>
        {
          this.routers.map((item) => {
              return 
              <Route path={item.path} 
              render={ ( ) => {
               return  item.auth?
               (this.state.islogin ? <item.component></item.component> : 
                  <Login login={this.handleLogin}></Login>)
                  :<item.component></item.component>
              }}>
            </Route>
          })
        }
      </Router>
    )
  }
}


//tapbar2
<Link onClick={this.changeFontColor.bind(this,3)} className={`${3 === this.state.active ? 'active-menu-li-lan active-menu-li' : 'active-menu-li'}`} to="/index/teachers">名师堂</Link>

柯里化

利用闭包原理在执行可以形成一个不销毁的作用域,然后把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个最少参数函数。

前端使用柯里化的用途主要就是简化代码结构,提高系统的维护性,一个方法,只有一个参数,强制了功能的单一性,很自然就做到了功能内聚,降低耦合。

function curry(fn, args) {
  var length = fn.length;
  var args = args || [];
  return function () {
    var newArgs = args.concat(Array.prototype.slice.call(arguments));
    if (newArgs.length < length) {
      return curry.call(this, fn, newArgs);
    } else {
      return fn.apply(this, newArgs);
    }
  }
}
function multiFn(a, b, c) {
  return a * b * c;
}
var multi = curry(multiFn);
multi(2)(3)(4);
multi(2, 3, 4);
multi(2)(3, 4);
multi(2, 3)(4);

快速排序

    //快速排序时间复杂度O(nlog(2)n)-O(log(n2)),速度最快:希尔排序 O(n)-O(n2);
    //原理:取数组中间位置的数据,跟所有数据比较,比该数小的放一左数组,大的放右数组,递归,最后拼接数据,得到想要的数组
    //重新赋值给元素替换
    Array.prototype.quickSort = function ( ) {
        var arr=this
        if (arr.length <= 1) { return arr; }
        var item = arr.splice(parseInt(arr.length / 2), 1)[0];
        // var item = arr.splice(Math.floor(arr.length / 2), 1)[0];
        var leftArr = [];
        var rightArr = [];
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] < item) {
              leftArr.push(arr[i]);
            } else {
                rightArr.push(arr[i]);
            }
        }
        return leftArr.quickSort().concat([item],rightArr.quickSort());
    };

    var arr = [1, 2, 34, 2, 1, 3, 2, 8]
    arr=arr.quickSort();
    console.log(arr)

斐波那契数列

function fib(n) {
  let array = new Array(n + 1).fill(null)
  array[0] = 0
  array[1] = 1
  for (let i = 2; i <= n; i++) {
    array[i] = array[i - 1] + array[i - 2]
  }
  return array[n]
}
fib(10)

拖拽

Object.defineProperties(HTMLElement.prototype, {
  drag: {
    enumerable: true,
    set: function (value) {
      this._drag = Boolean(value);
      if (this._drag) {
        this.style.position = "absolute";
        this.addEventListener("mousedown", this.mouseHandler)
      } else {
        this.removeEventListener("mousedown", this.mouseHandler)
      }
    },
    get: function () {
      return this._drag;
    }
  },
  mouseHandler: {
    value: function (e) {
      if (e.type === "mousedown") {
        this.offsetPoint = { x: e.offsetX, y: e.offsetY };
        document.elem = this;
        document.addEventListener("mousemove", this.mouseHandler);
        document.addEventListener("mouseup", this.mouseHandler);
      } else if (e.type === "mousemove") {
        Object.assign(document.elem.style, {
          left: e.clientX - document.elem.offsetPoint.x + "px",
          top: e.clientY - document.elem.offsetPoint.y + "px",
        })
      } else if (e.type === "mouseup") {
        document.removeEventListener("mousemove", document.elem.mouseHandler)
        document.removeEventListener("mouseup", document.elem.mouseHandler)
      }
    }
  }
});
var div = document.querySelector('div')
div.drag = true

instanceOf

function instanceOf(left, right) {
  let proto = left.__proto__;
  let prototype = right.prototype
  while (true) {
    if (proto === null) return false
    if (proto === prototype) return true
    proto = proto.__proto__;
  }
}

寄生组合式继承

 Function.prototype.extend = function (supClass) {   
            function F() { }
            // 将父类的原型赋值给这个中间替代类
            F.prototype = supClass.prototype
            // 将原子类的原型保存
            var proto = this.prototype
            // 将子类的原型设置为中间替代类的实例对象
            this.prototype = new F()
            // 将原子类的原型复制到子类原型上,合并超类原型和子类原型的属性方法
            var names = Object.getOwnPropertyNames(proto)for (var i = 0; i < names.length; i++) {
                var desc = Object.getOwnPropertyDescriptor(proto, names[i]);
                Object.defineProperty(subClass.prototype, names[i], desc)}
            // 设置子类的构造函数时自身的构造函数,以防止因为设置原型而覆盖构造函数
            this.prototype.constructor = this
            // 给子类的原型中添加一个属性,可以快捷的调用到父类的原型方法
            this.prototype.superClass = supClass.prototype
            // 如果父类的原型构造函数指向的不是父类构造函数,重新指向
            if (supClass.prototype.constructor !== supClass) {
                supClass.prototype.constructor = supClass
            }
        }

new操作符

function New(func) {
  var res = {};
  if (func.prototype !== null) {
    res.__proto__ = func.prototype;
  }
  var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
  if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
    return ret;
  }
  return res;
}
var obj = New(A, 1, 2);
// equals to
var obj = new A(1, 2);

判断是否是移动端

var browser={
  versions:function() {
    var u = navigator.userAgent
    var app = navigator.appVersion;
    return {//移动终端浏览器版本信息
      trident: u.indexOf('Trident') > -1, //IE内核
      presto: u.indexOf('Presto') > -1, //opera内核
      webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
      gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
      mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
      ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
      android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器
      iPhone: u.indexOf('iPhone') > -1 , //是否为iPhone或者QQHD浏览器
      iPad: u.indexOf('iPad') > -1, //是否iPad
      webApp: u.indexOf('Safari') == -1 //是否web应用程序,没有头部与底部
    };
  }(),
  language:(navigator.browserLanguage || navigator.language).toLowerCase()
}
if(browser.versions.mobile || browser.versions.ios || browser.versions.android ||
  browser.versions.iPhone || browser.versions.iPad) {
  window.location = "https://m.mobile.com";
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值