Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

第 103 题:模拟实现一个 localStorage #166

Open
yygmind opened this issue Jul 11, 2019 · 36 comments
Open

第 103 题:模拟实现一个 localStorage #166

yygmind opened this issue Jul 11, 2019 · 36 comments

Comments

@yygmind
Copy link
Contributor

yygmind commented Jul 11, 2019

No description provided.

@yygmind yygmind added the 阿里 label Jul 11, 2019
@EnergySUD
Copy link

EnergySUD commented Jul 11, 2019

		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

@negativeentropy9
Copy link

` 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 是否为字符串

@EnergySUD
Copy link

` 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 是否为字符串

因为自带的localStorage也是把对象自动转成字符串的。我测试过自带的localStorage也是这样子。

@nano-papa
Copy link

'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)
    }
  }
})

@lvwxx
Copy link

lvwxx commented Jul 11, 2019

class mockLocalStorage {
  constructor() {
    this.store =  {}
  }
  getItem(key) {
    return this.store[key] || null
  }
  setItem(key,value) {
    this.store[key] = value
  }
  removeItem(key) {
    delete this.store[key]
  }
  clear() {
    this.store = {}
  }
}
window.localStorage2 = new mockLocalStorage()

@little-kun
Copy link

这答案吓到我了。存储过之后,刷新页面,还能取出来吗???

@yoluxi
Copy link

yoluxi commented Jul 11, 2019

'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)
    }
  }
})

代理中这个判断又什么作用呀

@nano-papa
Copy link

nano-papa commented Jul 11, 2019

'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)
    }
  }
})

代理中这个判断又什么作用呀
localStorage不就这样么,你可以拿个浏览器看一下,这个比较细节了

@negativeentropy9
Copy link

` 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 是否为字符串

因为自带的localStorage也是把对象自动转成字符串的。我测试过自带的localStorage也是这样子。

localStorage.setItem('test',{a:'a'})

image

@EnergySUD
Copy link

` 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 是否为字符串

因为自带的localStorage也是把对象自动转成字符串的。我测试过自带的localStorage也是这样子。

localStorage.setItem('test',{a:'a'})

image

我的也是这样的,你可以试一下啊。

@michaelcai
Copy link

'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)
    }
  }
})

代理中这个判断又什么作用呀

localStorage 可以通过 localStorage.xxx 方式取值。

@jhoneybee
Copy link

jhoneybee commented Jul 11, 2019

image

如果我翻译的没错,红色下面应该说的是localStorage保存的数据理应都是字符串

@jhoneybee
Copy link

https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage

这个是localStorage的定义似乎还挺复杂的

@wingmeng
Copy link

难道不是应该用 cookie 模拟 localStorage 吗(兼容 IE8 及以下的 polyfill)?

!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);

@habc0807
Copy link

难道不是应该用 cookie 模拟 localStorage 吗(兼容 IE8 及以下的 polyfill)?

!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);

@wingmeng 使用cookie解决了刷新浏览器存储信息不被清除的问题,但是这样存储的信息就参与了服务器的通信,增加了请求负担,这个问题需要考虑吗

@weineel
Copy link

weineel commented Jul 11, 2019

@wingmeng 考虑了刷新浏览器存储信息不被清除。
是不是还得考虑 window.onstorage 的监听。

@yucopowo
Copy link

  1. same-origin rules 特定于页面的协议,还有隐身模式的区别
    当浏览器进入隐身模式(private browsing mode)的时候,会创建一个新的、临时的、空的数据库,用以存储本地数据(local storage data)。当浏览器关闭时,里面的所有数据都将被丢弃。

  2. 模拟持久存储
    a. like Internet Explorer < 8. It also makes use of cookies.
    b. IndexedDB
    c. WebSQL

  3. Storage Interface

interface Storage {
  readonly attribute unsigned long length;
  [IndexGetter] DOMString key(in unsigned long index);
  [NameGetter] DOMString getItem(in DOMString key);
  [NameSetter] void setItem(in DOMString key, in DOMString data);
  [NameDeleter] void removeItem(in DOMString key);
  void clear();
};
  1. localStorage 中的键值对总是以字符串的形式存储 key.toString() value.toString()
localStorage.setItem('a', {a:1})
undefined

localStorage.getItem('a')
"[object Object]"

a={a:1}
localStorage.setItem(a, {a:1})
{[object Object]: "[object Object]"}

localStorage.setItem('a', document.body)
localStorage.getItem('a')
"[object HTMLBodyElement]"

@xiemeng
Copy link

xiemeng commented Jul 12, 2019

// 模拟实现一个 localStorage
const localStorage = (function(){
let store = {};
return {
getItem(key){
return store[key] || null;
},
setItem(key,value){
store[key] = value.toString();
},
removeItem(key){
delete store[key]
},
clear(){
store = {};
}
}
})()

@james9527
Copy link

` 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 是否为字符串

因为自带的localStorage也是把对象自动转成字符串的。我测试过自带的localStorage也是这样子。

楼主写的基本上跟浏览器自带的功能一样了,localStorage.setItem存一个对象时一般要加上JSON.stringify序列化下,localStorage.getItem取一个对象时再用JSON.parse反序列化下就可以了。

@xiemeng
Copy link

xiemeng commented Jul 12, 2019 via email

@zwmmm
Copy link

zwmmm commented Jul 12, 2019

localStorage 难道最主要的功能不是持久化存储么?还可以在控制台手动删除。上面的答案没有一个能打的啊

@EnergySUD
Copy link

localStorage 难道最主要的功能不是持久化存储么?还可以在控制台手动删除。上面的答案没有一个能打的啊

你要知道,js的权限是被限制的,如果不用浏览器自带的,是不可能持久化的。

@zwmmm
Copy link

zwmmm commented Jul 12, 2019

localStorage 难道最主要的功能不是持久化存储么?还可以在控制台手动删除。上面的答案没有一个能打的啊

你要知道,js的权限是被限制的,如果不用浏览器自带的,是不可能持久化的。

所以上面模拟的都是啥,就实现一个api也叫模拟么?就不说持久化存储好了,最基本的容量限制也没处理吧?

@EnergySUD
Copy link

localStorage 难道最主要的功能不是持久化存储么?还可以在控制台手动删除。上面的答案没有一个能打的啊

你要知道,js的权限是被限制的,如果不用浏览器自带的,是不可能持久化的。

所以上面模拟的都是啥,就实现一个api也叫模拟么?就不说持久化存储好了,最基本的容量限制也没处理吧?

这,要不你设计一个,我们只是给自己的想法而已,有缺陷是很正常的啊。

@J-DuYa
Copy link

J-DuYa commented Jul 12, 2019

使用Map数据结构构造localstorage

export class mockLocalstorage {
  constructor() {
    this.store = new Map(); // 记录存储数据
  }

  getItem(key) {
    const stringKey = String(key);
    if (this.store.has(stringKey)) {
      return String(this.store.get(stringKey));
    } else {
      return null;
    }
  }

  setItem(key, val) {
    try {
      this.store.set(String(key), val);
    } catch (e) {
      throw new Error(e);
    }
  }

  keys() {
    return Object.keys(this.store);
  }

  removeItem(key) {
    this.store.delete(String(key));
  }

  clear() {
    this.store.clear();
  }

  get length() {
    return this.store.size;
  }
}

测试

this.mockLocalstorage = new mockLocalstorage();
this.mockLocalstorage.setItem("name", "duya");
console.log(this.mockLocalstorage.getItem("name")); // 测试mockLocalstorage duya

@xiaokeqi
Copy link

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)
    }
  }
})

为什么要用proxy啊,它的作用是什么呢?求解

@xiaokeqi
Copy link

'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)
    }
  }
})

代理中这个判断又什么作用呀

localStorage 可以通过 localStorage.xxx 方式取值。

不用proxy,也可以localStorage.xxx方式取值啊~~~

@me1a
Copy link

me1a commented Jul 15, 2019

localStorage 难道最主要的功能不是持久化存储么?还可以在控制台手动删除。上面的答案没有一个能打的啊

你要知道,js的权限是被限制的,如果不用浏览器自带的,是不可能持久化的。

所以上面模拟的都是啥,就实现一个api也叫模拟么?就不说持久化存储好了,最基本的容量限制也没处理吧?

这,要不你设计一个,我们只是给自己的想法而已,有缺陷是很正常的啊。

这个题目设计的没有意义。 我一看题目就觉得实现的是持久化存储

@gongph
Copy link

gongph commented Jul 18, 2019

忽略了 localStorage 的特性,即刷新,关闭页面仍然存在。正解应该是使用能够持久化的方法或者接口,比如 cookie 来模拟实现。 #171

@steve000
Copy link

如题(localStorage)的核心功能没有体现,持久化存储不是js模拟能实现的,浏览器自带功能还是借助浏览器吧

@wulichenyang
Copy link

'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)
    }
  }
})

localStorage.key(i)访问key的时候,越界返回null,arr[i]越界返回undefined,需要修正这一点。

@DarthVaderrr
Copy link

如果是要求实现localStorage的持久化存储功能....这是不可能的; 如果不是,那么这个题....好像没有什么意义啊,最多考察一下语法

@julyL
Copy link

julyL commented Oct 18, 2019

标注是阿里的题,明显考察的是用cookie实现polyfill,为啥一堆人认为在考api语法...

@bbrucechen
Copy link

		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

可是这样无法实现持久存储啊

@soneway
Copy link

soneway commented Dec 29, 2019

全都没有考虑持久化存储呀, 我的想法是通过cookie或者indexedDB

@yaodongyi
Copy link

使用indexedDB 模拟实现一个基础的 localStorage

index.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>Document</title>
  </head>
  <body>
    <button>set</button>
  </body>
</html>

<script src="./localStorageMock.js"></script>

localStorageMock.js

const name = Symbol('localStorageMock');
const localStorageMock = new (class {
  constructor() {
    Object.assign(this, { [name]: {}, db: {}, objectStore: {}, result: '' });
    Object.defineProperties(this, {
      DBOpenRequest: {
        value: window.indexedDB.open('localStorageMock', 1),
        writable: false,
        configurable: false,
      },
    });
    this.DBOpenRequest.onupgradeneeded = event => {
      this.db = event.target.result;
      if (!this.db.objectStoreNames.contains('store')) {
        this.objectStore = this.db.createObjectStore('store', { keyPath: 'name' });
        this.objectStore.createIndex('name', 'name', { unique: false });
      }
    };
    this.DBOpenRequest.onerror = event => console.error(event);
    this.DBOpenRequest.onsuccess = event => {
      this.db = event.target.result;
    };
  }
  /** 获取所有Storage数据列表 */
  readAll() {
    return new Promise(resolve => {
      let objectStore = this.db.transaction('store').objectStore('store');
      let arr = [];
      objectStore.openCursor().onsuccess = event => {
        let cursor = event.target.result;
        if (cursor) {
          arr.push(cursor.value);
          cursor.continue();
          return resolve(arr);
        } else {
          console.log('没有更多数据了!');
        }
      };
    });
  }
  getItem(key) {
    return new Promise((resolve, reject) => {
      let request = this.db
        .transaction(['store'], 'readonly')
        .objectStore('store')
        .index('name')
        .get(key);
      request.onerror = () => reject('事务失败');
      request.onsuccess = function(event) {
        if (request.result) {
          return resolve(request.result);
        } else {
          return reject('未获得数据记录');
        }
      };
    });
  }
  /** 注意隐患,刷新会情况this[name]数据,建议直接setItem */
  add(key, value) {
    console.log(this[name].hasOwnProperty(key));
    if (this[name].hasOwnProperty(key)) {
      return this.setItem(key, value);
    }
    Object.assign(this[name], { [key]: value });
    let request = this.db
      .transaction(['store'], 'readwrite')
      .objectStore('store')
      .add({ name: key, value: value });
    request.onsuccess = event => console.log('数据写入成功', event.target.result);
    request.onerror = err => console.error('数据写入失败', err.target.error);
  }
  removeItem(key) {
    let request = this.db
      .transaction(['store'], 'readwrite')
      .objectStore('store')
      .delete(key);
    request.onsuccess = () => console.log('数据删除成功');
    request.onerror = err => console.error('数据删除失败', err);
  }
  clear() {
    let request = this.db
      .transaction(['store'], 'readwrite')
      .objectStore('store')
      .clear();
    request.onsuccess = () => console.log('数据库清除成功');
    request.onerror = err => console.error('数据库清除失败', err);
  }
  setItem(key, value) {
    let request = this.db
      .transaction(['store'], 'readwrite')
      .objectStore('store')
      .put({ name: key, value: value });
    request.onsuccess = () => console.log('数据写入成功');
    request.onerror = err => console.error('数据写入失败', err);
  }
})();

document.getElementsByTagName('button')[0].addEventListener('click', async function() {
  console.log(localStorageMock);
  await localStorageMock.setItem('小明', '22岁');
  console.log(await localStorageMock.getItem('小明'));
  console.log(await localStorageMock.readAll());
  await localStorageMock.removeItem('小明');
  await localStorageMock.clear();
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests