美文网首页
JS杂项备忘录

JS杂项备忘录

作者: burningalive | 来源:发表于2019-02-05 17:40 被阅读0次

    快速排序

    const utils = {
      swap(array, a, b) {
        [array[a], array[b]] = [array[b], array[a]]
      },
      randomNum() {
        return Math.floor(Math.random() * 100)
      },
      randomArray() {
        return Array.from(Array(this.randomNum()), _ => this.randomNum())
      }
    }
    
    function partition(array, start, end) {
      let j = start
      let index = Math.floor(Math.random()*(end -start + 1) + start)
      utils.swap(array, index, end)
      let pivot = array[end]
      for (let i = start; i <= end; i++) {
        if (array[i] <= pivot) {
          utils.swap(array, i, j++)
        }
      }
      return j - 1
    }
    
    function quickSort(array, start = 0, end = array.length -1) {
      if (end - start < 1) return array
      let pivotIndex = partition(array, start, end)
      quickSort(array, start, pivotIndex - 1)
      quickSort(array, pivotIndex + 1, end)
      return array
    }
    let array = utils.randomArray()
    console.log(quickSort(array))
    

    函数组合

    function pipe(...funcs){
        return function(x){
            return funcs.reduce(function(accu, func){
                return func(accu);
            }, x);
        }
    }
    let str = 'jspool'
    
    function stringToUpper(str) {
        return str.toUpperCase()
    }
    
    function stringReverse(str) {
        return str.split('').reverse().join('')
    }
    
    function getThreeCharacters(str){
        return str.substring(0,3)
    }
    
    function stringToArray(str) {
        return str.split('')
    }
    
    let toUpperAndGetThreeAndArray = compose(stringToArray, getThreeCharacters,stringToUpper)
    let result = toUpperAndGetThreeAndArray(str) // => ["J","S","P"]
    
    

    call, bind, apply

     Function.prototype.myCall = function (context, ...arg) {
            const fn = Symbol('临时属性')
            context[fn] = this
            context[fn](...arg)
            delete context[fn]
        }
        Function.prototype.myBind = function (context, ...firstarg) {
            const that = this
            const bindFn = function (...secoundarg) {
                return that.myCall(context, ...firstarg, ...secoundarg)
            }
            bindFn.prototype = Object.create(that.prototype)
            return bindFn
        }
    

    事件

    1. DOM事件流:捕获阶段 → 目标阶段 → 冒泡阶段
    2. 事件捕获的具体流程:window document html body → 元素
      注意,第一个接收到事件的是window。
      获取HTML根元素,document.documentElement

    dom操作

    node.parentNode
    node.removeChild()

    function findParent(node) {
      let parentNode = node.parentNode;
      let root = this.el.find(item => item === parentNode);
      if (root) {
        root.removeChild();
      } else {
        findParent(parentNode);
      }
      window.addEventListener('DOMContentLoaded', function () {}, false);
    }
    

    Event对象的常见应用

    event.preventDefault()阻止默认事件。
    event.stopPropagation()阻止冒泡(防止父级元素响应)。
    event.stopImmediatePropagation()给同一个元素注册两个同类型事件a,b;在a事件注册函数中添加这段调用,可阻止b的注册事件触发。
    event.currentTarget触发时获取当前绑定的事件元素(常用于事件委托获取父级元素)。
    event.target 获取当前触发的目标对象(子级元素)。

    自定义事件

    Event无法传参,其余与CustomEvent一致.
    CustomEvent传递参数,需在new CustomEvent的第二个参数给定一个拥有detail属性的对象。

    ele.addEventListener('custome', function(e){
      console.log(e.deatil.data);  // Event时为null, CustomEvent时为‘somedata’
    });
    var eve = new Event('custome');
    var eve = new CustomEvent('custome', {
      detail: { 
        data: 'somedata'
      }
    });
    ele.dispatchEvent(eve);
    

    类型转换

    Number函数

    转换时优先调用valueOf,后toString,这两步操作如果返回基本数据类型则将这个值重新调用Number函数,如果这两步都未返回基础数据类型则报错。
    字符串:如果可以解析为数值,转换为相应值,不同于parseInt,包含非数字字符会返回NaN,为小数则返回小数,空字符串返回0.
    undefined:返回NaN。
    null:返回0.

    [] + []答案为空字符串。 加号运算符两侧未出现字符串,优先转换为数据,左侧valueOf返回其本身,故继续调用toString,数组toString方法相当于调用join方法,返回空字符串,右侧同理。
    {} + []答案为0。左侧大括号被认为为空代码块,运算表达式实为+[],对空数组调用Number方法,空数组toString后为空字符串,Number('')返回0。
    [] + {}答案为'[object Object]'。空数组被转为空字符串,加号左侧为string故右侧值转为string类型。

    原型链

    创建对象的几种方法

    1. 直接书写字面量或new Object({ })
    2. function的new调用
    3. Object.create(obj)

    模拟new运算符

    var myNew = function (func, ...arg){
        var obj = Object.create(func.prototype);
        var res = func.apply(obj, arg);
        if (typeof res === 'object') return res;
        else return obj;
    }
    

    function继承

    function Parent() {
      this.name = 'parent';
      this.arr = [1, 2, 3];
    }
    function Child() {
      Parent.call(this);
      this.type = 'child';
    }
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
    

    运行机制类

    1. 如何理解js的单线程

    JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。

    JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

    所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

    为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

    什么是任务队列
    什么是event loop: 「面试必考」从多线程到Event Loop全面梳理
    将语句放入异步任务队列的时机

    具体参考饿了么前端的这两篇文章
    Event Loop 这个循环你晓得么?(附GIF详解)

    设计模式

    1. 观察者模式 订阅发布
    class Publisher {
      constructor() {
        this.eventEmitter = new EventEmitter();
      }
      publish(type, ...args) {
       this.eventEmitter.emit(type, ...args);
      }
    }
    class Subscriber {
      constructor() {
        this.eventEmitter = new EventEmitter();
      }
      subscribe(publisher, type) {
       publisher.eventEmitter.on(type, subscriber, cb);
      }
    }
    class EventEmitter {
        constructor() {
            // 定义存放事件订阅的存储变量
            this.listeners = {};
        }
    
        on(type, subscriber, cb) {
            let cbs = this.listeners[type];
            if (!cbs) {  // 判断是否已经有订阅
                cbs = [];
            }
            // 订阅一则事件
            cbs.push(cb);
            this.listeners[type] = cbs;
            return this;
        }
    
        emit(type, ...args) {
            const cbs = this.listeners[type];
            if (Array.isArray(cbs)) {
                for (let i = 0; i < cbs.length; i++) {
                    const cb = cbs[i];
                    if (typeof cb === 'function') {
                        // 触发一则事件
                        cb(...args);
                    }
                }
            }
            return this;
        }
    
        off(type, cb) {
            if (cb) {  // 如果有回调,则取消订阅该回调
                let cbs = this.listeners[type];
                cbs = cbs.filter(func => func !== cb);
                this.listeners[type] = cbs;
            } else {  // 否则取消订阅整个事件
                this.listeners[type] = null;
                delete this.listeners[type];
            }
            return this;
        }
    }
    
    export default new EventEmitter();
    

    以下这种则是给每个对象上方添加listener的实现方式, 使用WeakMap

    var listeners = new WeakMap();
    // 监听事件
    function on(object, event, fn){
        var thisListeners = listeners.get(object);
        if(!thisListeners) thisListeners = {};
        if(!thisListeners[event]) thisListeners[event] = [];
        thisListeners[event].push(fn);
        listeners.set(object, thisListeners);
    }
    // 触发事件
    function emit(object, event){
        var thisListeners = listeners.get(object);
        if(!thisListeners) thisListeners = {};
        if(!thisListeners[event]) thisListeners[event] = [];
        thisListeners[event].forEach(function(fn){
            fn.call(object, event);
        });
    }
    // 使用
    var obj = {};
    on(obj, 'hello', function(){
        console.log('hello');
    });
    emit(obj, 'hello');
    

    一些题

    1. 完成函数changeDateFormate, 将字符串中的'MM/DD/YYYY'为替换为'YYYY/MM/DD'.
      输入: 'John was born in 02/13/1920 and died in 01/20/1970.'
      输出: 'John was born in 1920/02/13 and died in 1970/01/20.'
    function changeDateFormate(str) {
      const dateReg = /\d{2}\/\d{2}\/\d{4}/g;
      var times = str.match(dateReg);
      times = times.map(item => {
        var times = item.split('/');
        times.unshift(times.pop());
        return times.join('/');
      });
      var splitDateStrs = str.split(dateReg);
      var res = [];
      splitDateStrs.forEach((item, idx) => {
         res.push(item); 
         times[idx] && res.push(times[idx]);
      });
      return res.join('');
    }
    
    1. css3设计一个立起的圆形, 并围绕自身中轴线做360度持续旋转.
    2. css分割单行和多行截断字符串, 最后以...结尾
    .单行 {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      width: xxpx;
    }
    .多行 {
      overflow: hidden;
      display: -webkit-box;
      -webkit-line-clamp: 3;
      text-overflow: ellipsis;
      -webkit-box-orient: vertical; // 好像不需要这个
    }
    
    1. 什么是CSRF, 怎么造成的, 有什么防御方法?
    2. Cookie和Session有什么区别
    3. 如何html中开启关闭DNS预读取?
      <meta http-equiv="x-dns-prefetch-control" content="on">
    4. <script>标签defer或async属性的取用及二者的区别?
    1. defer: HTML解析完后才执行, 如果有多个, 按照加载的顺序一次执行
    2. async: 加载完之后立即执行, 如果有多个, 执行顺序和加载顺序无关
    1. 设计移到js的range算法, 如下:
      range(1, 10, 3), 返回[1, 4, 7, 10]. range('A', 'F', 2), 返回['A', 'C', 'E'].
      使用'A'.charCodeAt(0)String.fromCharCode()
    function range(start , end , interval) {
        const res = [];
        if (typeof start == 'string' && typeof end =='string') {
            start = start.charCodeAt(0);
            end = end.charCodeAt(0);
            for (let i = start; i<=end; i+=interval) {
                   res.push(String.fromCharCode(i));
          } 
        } else if (typeof start == 'number' && typeof end == 'number'){
          for (let i = start; i<=end; i+=interval) { 
              res.push(i);
            } 
        }
        return res;
    }
    
    1. [{
      time: number,
      content: string
      },
      {
      time: number,
      content: string
      },
      ...]. 需要快速定位到某个时间点的弹幕, 请编码实现(不使用sort)

    Object深拷贝.

    目前虽然已经考虑到很多条件, 但还不太完善.
    在codepen上打开

    // 简化版本
    function clone(target, map = new WeakMap()) {
        if (!isObject(target)) {
          return target;
        } else {
            const isArray = Array.isArray(target);
            let cloneTarget = new target.constroctor();
    
            if (!map.has(target)) {
              map.set(target, cloneTarget);
            } else {
              return map.get(target);
            }
    
            const keys = isArray ? undefined : Reflect.ownKeys(target);
            forEach(keys || target, (value, key) => {
                if (keys) {
                    key = value;
                }
                cloneTarget[key] = clone(target[key], map);
            });
            return cloneTarget;
        }
    }
    function isObject(target) {
        const type = typeof target;
        return target !== null && (type === 'object' || type === 'function');
    }
    function getType(target) {
        return Object.prototype.toString.call(target);
    }
    
    function forEach(array, iteratee) {
        let index = -1;
        while (++index < array.length) {
            iteratee(array[index], index);
        }
    }
    const target = {
        field1: 1,
        field2: undefined,
        field3: {
            child: 'child'
        },
        field4: [2, 4, 8],
        f: { [Symbol()]: { f: { f: { f: { f: { f: { f: { f: { f: { f: { f: {} } } } } } } } } } } },
    };
    
    
    // 以下是更全的版本
    
    const mapTag = 'Map'; const setTag = 'Set'; const arrayTag = 'Array'; 
    const objectTag = 'Object'; const argsTag = 'Arguments'; const boolTag = 'Boolean'; 
    const numberTag = 'Number'; const stringTag = 'String'; 
    const symbolTag = 'Symbol';  const regexpTag = 'RegExp'; const funcTag = 'Function';
    
    const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];
    
    function isObject(target) {
        const type = typeof target;
        return target !== null && (type === 'object' || type === 'function');
    }
    
    function getType(target) {
        return Object.prototype.toString.call(target).slice(8, -1);
    }
    
    function cloneReg(targe) {
        const reFlags = /\w*$/;
        const result = new RegExp(targe.source, reFlags.exec(targe)[0]);
        result.lastIndex = targe.lastIndex;
        return result;
    }
    
    function cloneFunction(func) {
        const funcString = func.toString();
        if (!func.prototype) {
            return eval(funcString);
        } else {
            const bodyReg = /(?<={).*(?=})/s;
            const paramReg = /(?<=\().*(?=\)\s*{)/s;
            const param = paramReg.exec(funcString);
            const body = bodyReg.exec(funcString);
            if (body) {
                if (param) {
                    return new Function(param[0], body[0]);
                    const paramArr = param[0].split(',');
                    // return new Function(...paramArr, body[0]);
                } else {
                    return new Function(body[0]);
                }
            } else {
                return null;
            }
        }
    }
    
    function cloneOtherType(targe, type) {
        switch (type) {
            case numberTag:
            case stringTag:
            case boolTag:
            case symbolTag:
                return Object(targe.valueOf());
            case regexpTag:
                return cloneReg(targe);
            case funcTag:
                return cloneFunction(targe);
            default:
                return new targe.constructor(targe);
        }
    }
    
    function clone(target, map = new WeakMap()) {
        if (!isObject(target)) return target;    // 原始类型
    
        // 防止循环引用
        if (map.has(target))  return map.get(target);
    
        // 初始化
        const type = getType(target);
        if (!deepTag.includes(type)) {
            return cloneOtherType(target, type);
        } 
    
        let cloneTarget = new target.constructor();
        map.set(target, cloneTarget);
    
        if (type === setTag) {    // 克隆set
            target.forEach(value => {
                cloneTarget.add(clone(value, map));
            });
        }
    
        if (type === mapTag) {    // 克隆map
            target.forEach((value, key) => {
                cloneTarget.set(key, clone(value, map));
            });
        }
    
        if (type === arrayTag) { // 数组
            target.forEach(v => {
              cloneTarget.push(clone(v, map));
            });
          }
    
        if (type === objectTag) { // Object
          for (let key of Reflect.ownKeys(target)) {
            cloneTarget[key] = clone(target[key], map);
          }
        }
        return cloneTarget;
    }
    
    // 用例demo
    const sym = Symbol('testB');
    let a = {
      a: function (a, b) {
        console.log(a,b);
      },
      b: 'b',
      c: [1, 2, {a: 1}],
      
    };
    a[sym] = "symbol";
    a.d = a;
    
    let b = clone(a);
    console.log(a.d === a, a);
    console.log(b.d === b, b);
    

    函数柯里化

    柯里化是个十分实用的工具函数, 可以用于包裹一些业务逻辑, 以下是个人修改版.

    function curry(func, len = func.length) {
      return function curried(...args) {
        if (args.length >= len) {
          return func.apply(this, args);
        } else {
          return function(...newArgs) {
            return curried.apply(this, args.concat(newArgs));
          }
        }
      };
    }
    

    Promise

    function MyPromise(executor){
      let self = this
      self.value = undefined
      self.reason = undefined
      self.status = 'pending'
      self.onResolvedCallbacks = []
      self.onRejectedCallbacks = []
      function resolve(value){
        if(self.status === 'pending'){ //保证状态一旦变更,不能再次修改
          self.value = value
          self.status = 'resolved' // 成功状态
          self.onResolvedCallbacks.forEach(fn => {
            fn()
          })
        }
      }
      function reject(reason){
        if(self.status === 'pending'){
          self.reason = reason
          self.status = 'rejected' //失败状态
          self.onRejectedCallbacks.forEach(fn => {
            fn()
          })
        }
      }
      executor(resolve, reject) // 因为会立即执行这个执行器函数
    }
    
    MyPromise.prototype.then = function(onFulfilled, onRejected){
      let self = this
      return new MyPromise(function(resolve, reject){
        if(self.status === 'resolved'){
          onFulfilled(self.value)
        }
        if(self.status === 'rejected'){
          onRejected(self.reason)
        }
        if(self.status === 'pending'){
          self.onResolvedCallbacks.push(function(){
            onFulfilled(self.value)
          })
          self.onRejectedCallbacks.push(function(){
            onRejected(self.reason)
          })
        }
      }
    }
    
    

    判断url是图片的正则: const imgReg = /\.(png|jpe?g|gif|svg|bmp|raw|webp)(\?.*)?$/;

    相关文章

      网友评论

          本文标题:JS杂项备忘录

          本文链接:https://www.haomeiwen.com/subject/rtspsqtx.html