Github每日一题——7.15学习记录

Github每日一题——7.15学习记录_第1张图片

前言


题目来自Daily-Interview-Question 木易杨
欢迎star,加入讨论

本文记录自己对题目的解决方式以及综合大神们的看法。

题目


1、匹配elective后的数字输出(写出你认为的最优解法)

https://www.xx.cn/api?keyword=&level1=&local_batch_id=&elective=&local_province_id=33
https://www.xx.cn/api?keyword=&level1=&local_batch_id=&elective=800&local_province_id=33
https://www.xx.cn/api?keyword=&level1=&local_batch_id=&elective=800,700&local_province_id=33

正则表达式解决方法:

const getUrlValue = function(url){
  let res = url.match(/(?<=elective=)(\d+(,\d+)*)/)
  return res ? res[0].split(',') : []
}

(?<=elective=) 是指匹配以elective=开头的字符串;
(\d+(, \d+))指匹配数字开头,可能不定数量逗号分隔后是数字的字符串。

String.prototype.match()

语法:

str.match(regexp)

参数:
  • 一个正则表达式对象
  • 如果传入一个非正则表达式对象,则会隐式地使用 new RegExp(obj) 将其转换为一个 RegExp(obj)
  • 果你没有给出任何参数并直接使用match() 方法 ,你将会得到一 个包含空字符串的Array[]
返回值:
  • 如果使用g标志,则将返回与完整正则表达式匹配的所有结果(Array),但不会返回捕获组,或者未匹配 null。
  • 如果未使用g标志,则仅返回第一个完整匹配及其相关的捕获组(Array)。 在这种情况下,返回的项目将具有如下所述的其他属性,或者未匹配 null。

URLSearchParams的解决方案:

 new URLSearchParams('https://www.xx.cn/api?keyword=&level1=&local_batch_id=&elective=800,700&local_province_id=33').get('elective')
urls.forEach(url => {
console.log(new URLSearchParams(url).get('elective').split(','));
});

URLSearchParams

构造函数:
  • URLSearchParams()
    返回一个URLSearchParams对象
方法:
  • URLSearchParams.append()
    插入一个指定的键/值对作为新的搜索参数。

  • URLSearchParams.delete()
    从搜索参数列表里删除指定的搜索参数及其对应的值。

  • URLSearchParams.entries()
    返回一个iterator可以遍历所有键/值对的对象

  • URLSearchParams.get()
    获取指定搜索参数的第一个值。

  • URLSearchParams.getAll()
    获取指定搜索参数的所有值,返回是一个数组。

  • URLSearchParams.has()
    返回Boolean判断是否存在此搜索参数。

  • URLSearchParams.keys()
    返回iterator 此对象包含了键/值对的所有键名

  • URLSearchParams.set()
    设置一个搜索参数的新值,假如原来有多个值将删除其他所有的值。

  • URLSearchParams.sort()
    按键名排序

  • URLSearchParams.toString()
    返回搜索参数组成的字符串,可直接使用在URL上。

  • URLSearchParams.values()
    返回iterator此对象包含了键/值对的所有值。

不过URLSearchParams对IE的兼容性不友好
Github每日一题——7.15学习记录_第2张图片

URL解决方法:

new URL
("[https://www.xx.cn/api?keyword=&level1=&local_batch_id=&elective=700,800&local_province_id=33](https://www.xx.cn/api?keyword=&level1=&local_batch_id=&elective=700,800&local_province_id=33)")
.searchParams.get("elective")
.split(",")
.filter(e=>e)
.map(e=>e)

2、模拟实现一个 localStorage

localStorage

  • 介绍:
    只读的localStorage 属性允许你访问一个Document源的对象 Storage;其存储的数据能在跨浏览器会话保留。localStorage 类似 sessionStorage,但其区别在于:存储在 localStorage 的数据可以长期保留;而当页面会话结束——也就是说,当页面被关闭时,存储在 sessionStorage 的数据会被清除

    另外,localStorage中的键值对总是以字符串的形式存储。 (需要注意, 和js对象相比, 键值对总是以字符串的形式存储意味着数值类型会自动转化为字符串类型).

语法:

myStorage = localStorage

示例:

增加一个数据项目

localStorage.setItem('myCat', 'Tom');

读取 localStorage

let cat = localStorage.getItem('myCat')

移除 localStorage 项

localStorage.removeItem('myCat')

移除所有的 localStorage 项

localStorage.clear()

现在我们知道了localStorage的接口有增加,读取,删除和删除所有

思路:

先实现这些功能,然后挂载到全局变量里面去

来自EnergySUD的解决方案

const localStorageMock = (function() {
    let store = {}
    return {
        getItem: function(key) { return store[key] || null },
        setItem: function(key, value) { store[key] = value.toString() },
        removeItem: function(key) { delete store[key] },
        clear: function() { store = {} },
    }
})()

Object.defineProperty(window, 'localStorage2', {
    value: localStorageMock
})
        
localStorage2.setItem('test', 'test')
console.log(localStorage2.getItem("test"))  //test

localStorage2.removeItem('test')
console.log(localStorage2.getItem("test"))  //null

localStorage2.setItem('test', 'test')
localStorage2.clear()
console.log(localStorage2.getItem("test"))  //null

但是这个方法有问题,问题在于value的值如果是个对象,就跟浏览器的表示方式不一样。

方案二

楼下13834242832有个解决方案,用String字符串对象解决了上面的问题
同时用了Map对象来保存键值对

'use strict'
const valuesMap = new Map()

class LocalStorage {
  getItem (key) {
    const stringKey = String(key)
    if (valuesMap.has(key)) {
      return String(valuesMap.get(stringKey))
    }
    return null
  }

  setItem (key, val) {
    valuesMap.set(String(key), String(val))
  }

  removeItem (key) {
    valuesMap.delete(key)
  }

  clear () {
    valuesMap.clear()
  }

  key (i) {
    if (arguments.length === 0) {
      throw new TypeError("Failed to execute 'key' on 'Storage': 1 argument required, but only 0 present.") // this is a TypeError implemented on Chrome, Firefox throws Not enough arguments to Storage.key.
    }
    var arr = Array.from(valuesMap.keys())
    return arr[i]
  }

  get length () {
    return valuesMap.size
  }
}
const instance = new LocalStorage()

global.localStorage = new Proxy(instance, {
  set: function (obj, prop, value) {
    if (LocalStorage.prototype.hasOwnProperty(prop)) {
      instance[prop] = value
    } else {
      instance.setItem(prop, value)
    }
    return true
  },
  get: function (target, name) {
    if (LocalStorage.prototype.hasOwnProperty(name)) {
      return instance[name]
    }
    if (valuesMap.has(name)) {
      return instance.getItem(name)
    }
  }
})

Map

Objects 和 maps 的比较
相同点:
  • 都允许你按键存取一个值、删除键、检测一个键是否绑定了值。
不同点:
  • 可以通过 size 属性直接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。

  • 一个Object的键只能是字符串,但一个 Map 的键可以是任意值,包括函数、对象、基本类型。

  • Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。

  • Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后再进行迭代。

  • Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。虽然 ES5 开始可以用 map = Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。

  • Map 在涉及频繁增删键值对的场景下会有些性能优势。

来源:Map - JavaScript | MDN

Proxy

语法:
let p = new Proxy(target, handler)
参数:
  • target
    用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

  • handler
    一个对象,其属性是当执行一个操作时定义代理的行为的函数。

实例:
  • 基础示例

在以下简单的例子中,当对象中不存在属性名时,缺省返回数为37,例子中使用了get
有一种给指定对象挂载了get方法的意思。

let handler = {
    get: function(target, name){
        return name in target ? target[name] : 37;
    }
};

let p = new Proxy({}, handler);

p.a = 1;
p.b = undefined;

console.log(p.a, p.b);    // 1, undefined

console.log('c' in p, p.c);    // false, 37
  • 无操作转发代理

在以下例子中,我们使用了一个原生 JavaScript 对象,代理会将所有应用到它的操作转发到这个对象上。

let target = {};
let p = new Proxy(target, {});

p.a = 37;   // 操作转发到目标

console.log(target.a);    // 37. 操作已经被正确地转发
  • 验证

通过代理,你可以轻松地验证向一个对象的传值。这个例子使用了 set
有点将函数挂载到对象上的意思

let validator = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }

    // The default behavior to store the value
    obj[prop] = value;
  }
};

let person = new Proxy({}, validator);

person.age = 100;

console.log(person.age); 
// 100

person.age = 'young'; 
// 抛出异常: Uncaught TypeError: The age is not an integer

person.age = 300; 
// 抛出异常: Uncaught RangeError: The age seems invalid
  • 扩展构造函数

方法代理可以轻松地通过一个新构造函数来扩展一个已有的构造函数。这个例子使用了constructapply

function extend(sup,base) {
  var descriptor = Object.getOwnPropertyDescriptor(
    base.prototype,"constructor"
  );
  base.prototype = Object.create(sup.prototype);
  var handler = {
    construct: function(target, args) {
      var obj = Object.create(base.prototype);
      this.apply(target,obj,args);
      return obj;
    },
    apply: function(target, that, args) {
      sup.apply(that,args);
      base.apply(that,args);
    }
  };
  var proxy = new Proxy(base,handler);
  descriptor.value = proxy;
  Object.defineProperty(base.prototype, "constructor", descriptor);
  return proxy;
}

var Person = function(name){
  this.name = name
};

var Boy = extend(Person, function(name, age) {
  this.age = age;
});

Boy.prototype.sex = "M";

var Peter = new Boy("Peter", 13);
console.log(Peter.sex);  // "M"
console.log(Peter.name); // "Peter"
console.log(Peter.age);  // 13
  • 操作 DOM 节点

有时你希望切换两个不同的元素的属性或类名

let view = new Proxy({
  selected: null
},
{
  set: function(obj, prop, newval) {
    let oldval = obj[prop];

    if (prop === 'selected') {
      if (oldval) {
        oldval.setAttribute('aria-selected', 'false');
      }
      if (newval) {
        newval.setAttribute('aria-selected', 'true');
      }
    }

    // The default behavior to store the value
    obj[prop] = newval;
  }
});

let i1 = view.selected = document.getElementById('item-1');
console.log(i1.getAttribute('aria-selected')); // 'true'

let i2 = view.selected = document.getElementById('item-2');
console.log(i1.getAttribute('aria-selected')); // 'false'
console.log(i2.getAttribute('aria-selected')); // 'true'

来源自Proxy - JavaScript | MDN

方案三

wingmeng解决了刷新浏览器存储信息不被清除的问题

!window.localStorage && !function(win) {
  var thousandYears = 1e3 * 365 * 24 * 36e5;

  function getCookies() {
    return document.cookie.match(/([^;=]+)=([^;]+)/g) || [];
  }

  function getExpires(flag) {
    flag = flag || 1;

    return 'expires=' +
      (new Date((+new Date()) + thousandYears * flag)).toUTCString();
  }

  function get(key) {
    var cookies = getCookies();

    for (var i = 0; i < cookies.length; i++) {
      var param = cookies[i].match(/^\s*([^=]+)=(.+)/);

      if (param[1] === String(key)) {
        return decodeURIComponent(param[2]);
      }
    }

    return null;
  }

  function set(key, value, isExpired) {
    document.cookie = [
      key + '=' + encodeURIComponent(value),
      getExpires(isExpired ? -1 : 1),
      'path=/'
    ].join('; ');
  }

  function remove(key) {
    set(key, '', true);
  }

  function clear() {
    var cookies = getCookies();

    for (var i = 0; i < cookies.length; i++) {
      var key = cookies[i].match(/^\s*([^=]+)/)[1];
      remove(key);
    }
  }

  // 注册到 window 对象上
  win.localStorage = {
    getItem: get,
    setItem: set,
    removeItem: remove,
    clear: clear
  };
}(window);

也可以看一下 MDN LocalStorage实现

未完待续
每日更新

你可能感兴趣的:(Github每日一题——7.15学习记录)