前言
題目來自Daily-Interview-Question 木易楊
歡迎star首懈,加入討論
本文記錄自己對題目的解決方式以及綜合大神們的看法。
題目
1缀台、匹配elective后的數(shù)字輸出(寫出你認(rèn)為的最優(yōu)解法)
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
正則表達(dá)式解決方法:
const getUrlValue = function(url){
let res = url.match(/(?<=elective=)(\d+(,\d+)*)/)
return res ? res[0].split(',') : []
}
(?<=elective=) 是指匹配以elective=開頭的字符串;
(\d+(, \d+))指匹配數(shù)字開頭,可能不定數(shù)量逗號分隔后是數(shù)字的字符串枫慷。
String.prototype.match()
語法:
str.match(regexp)
參數(shù):
- 一個(gè)正則表達(dá)式對象
- 如果傳入一個(gè)非正則表達(dá)式對象,則會(huì)隱式地使用 new RegExp(obj) 將其轉(zhuǎn)換為一個(gè) RegExp(obj)
- 果你沒有給出任何參數(shù)并直接使用match() 方法 浪规,你將會(huì)得到一 個(gè)包含空字符串的Array[]
返回值:
- 如果使用g標(biāo)志或听,則將返回與完整正則表達(dá)式匹配的所有結(jié)果(Array),但不會(huì)返回捕獲組罗丰,或者未匹配 null神帅。
- 如果未使用g標(biāo)志,則僅返回第一個(gè)完整匹配及其相關(guān)的捕獲組(Array)萌抵。 在這種情況下找御,返回的項(xiàng)目將具有如下所述的其他屬性元镀,或者未匹配 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
構(gòu)造函數(shù):
- URLSearchParams()
返回一個(gè)URLSearchParams對象
方法:
URLSearchParams.append()
插入一個(gè)指定的鍵/值對作為新的搜索參數(shù)霎桅。URLSearchParams.delete()
從搜索參數(shù)列表里刪除指定的搜索參數(shù)及其對應(yīng)的值栖疑。URLSearchParams.entries()
返回一個(gè)iterator
可以遍歷所有鍵/值對的對象URLSearchParams.get()
獲取指定搜索參數(shù)的第一個(gè)值。URLSearchParams.getAll()
獲取指定搜索參數(shù)的所有值滔驶,返回是一個(gè)數(shù)組遇革。URLSearchParams.has()
返回Boolean判斷是否存在此搜索參數(shù)。URLSearchParams.keys()
返回iterator
此對象包含了鍵/值對的所有鍵名URLSearchParams.set()
設(shè)置一個(gè)搜索參數(shù)的新值揭糕,假如原來有多個(gè)值將刪除其他所有的值萝快。URLSearchParams.sort()
按鍵名排序URLSearchParams.toString()
返回搜索參數(shù)組成的字符串,可直接使用在URL上著角。URLSearchParams.values()
返回iterator
此對象包含了鍵/值對的所有值揪漩。
不過URLSearchParams對IE的兼容性不友好
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、模擬實(shí)現(xiàn)一個(gè) localStorage
localStorage
- 介紹:
只讀的localStorage
屬性允許你訪問一個(gè)Document
源的對象Storage
吏口;其存儲(chǔ)的數(shù)據(jù)能在跨瀏覽器會(huì)話保留奄容。localStorage
類似sessionStorage
,但其區(qū)別在于:存儲(chǔ)在localStorage
的數(shù)據(jù)可以長期保留产徊;而當(dāng)頁面會(huì)話結(jié)束——也就是說昂勒,當(dāng)頁面被關(guān)閉時(shí),存儲(chǔ)在sessionStorage
的數(shù)據(jù)會(huì)被清除
?
另外舟铜,localStorage
中的鍵值對總是以字符串的形式存儲(chǔ)戈盈。 (需要注意, 和js對象相比, 鍵值對總是以字符串的形式存儲(chǔ)意味著數(shù)值類型會(huì)自動(dòng)轉(zhuǎn)化為字符串類型).
語法:
myStorage = localStorage
示例:
增加一個(gè)數(shù)據(jù)項(xiàng)目
localStorage.setItem('myCat', 'Tom');
讀取 localStorage
let cat = localStorage.getItem('myCat')
移除 localStorage 項(xiàng)
localStorage.removeItem('myCat')
移除所有的 localStorage 項(xiàng)
localStorage.clear()
現(xiàn)在我們知道了localStorage的接口有增加,讀取谆刨,刪除和刪除所有
思路:
先實(shí)現(xiàn)這些功能奕谭,然后掛載到全局變量里面去
來自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
但是這個(gè)方法有問題,問題在于value的值如果是個(gè)對象痴荐,就跟瀏覽器的表示方式不一樣血柳。
方案二
樓下13834242832有個(gè)解決方案,用String字符串對象解決了上面的問題
同時(shí)用了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 的比較
相同點(diǎn):
- 都允許你按鍵存取一個(gè)值生兆、刪除鍵难捌、檢測一個(gè)鍵是否綁定了值。
不同點(diǎn):
可以通過 size 屬性直接獲取一個(gè) Map 的鍵值對個(gè)數(shù)鸦难,而 Object 的鍵值對個(gè)數(shù)只能手動(dòng)計(jì)算根吁。
一個(gè)
Object
的鍵只能是字符串
,但一個(gè)Map
的鍵可以是任意值合蔽,包括函數(shù)击敌、對象、基本類型拴事。Map 中的鍵值是有序的沃斤,而添加到對象中的鍵則不是圣蝎。因此,當(dāng)對它進(jìn)行遍歷時(shí)衡瓶,Map 對象是按插入的順序返回鍵值徘公。
Map 可直接進(jìn)行迭代,而 Object 的迭代需要先獲取它的鍵數(shù)組哮针,然后再進(jìn)行迭代关面。
Object 都有自己的原型,原型鏈上的鍵名有可能和你自己在對象上的設(shè)置的鍵名產(chǎn)生沖突十厢。雖然 ES5 開始可以用 map = Object.create(null) 來創(chuàng)建一個(gè)沒有原型的對象等太,但是這種用法不太常見。
Map 在涉及頻繁增刪鍵值對的場景下會(huì)有些性能優(yōu)勢蛮放。
Proxy
語法:
let p = new Proxy(target, handler)
參數(shù):
target
用Proxy包裝的目標(biāo)對象(可以是任何類型的對象澈驼,包括原生數(shù)組,函數(shù)筛武,甚至另一個(gè)代理)。handler
一個(gè)對象挎塌,其屬性是當(dāng)執(zhí)行一個(gè)操作時(shí)定義代理的行為的函數(shù)徘六。
實(shí)例:
-
基礎(chǔ)示例
在以下簡單的例子中,當(dāng)對象中不存在屬性名時(shí)榴都,缺省返回?cái)?shù)為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
-
無操作轉(zhuǎn)發(fā)代理
在以下例子中竿音,我們使用了一個(gè)原生 JavaScript 對象,代理會(huì)將所有應(yīng)用到它的操作轉(zhuǎn)發(fā)到這個(gè)對象上拴驮。
let target = {};
let p = new Proxy(target, {});
p.a = 37; // 操作轉(zhuǎn)發(fā)到目標(biāo)
console.log(target.a); // 37. 操作已經(jīng)被正確地轉(zhuǎn)發(fā)
-
驗(yàn)證
通過代理春瞬,你可以輕松地驗(yàn)證向一個(gè)對象的傳值。這個(gè)例子使用了 set
有點(diǎn)將函數(shù)掛載到對象上的意思
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
-
擴(kuò)展構(gòu)造函數(shù)
方法代理可以輕松地通過一個(gè)新構(gòu)造函數(shù)來擴(kuò)展一個(gè)已有的構(gòu)造函數(shù)套啤。這個(gè)例子使用了construct
和apply
宽气。
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 節(jié)點(diǎn)
有時(shí)你希望切換兩個(gè)不同的元素的屬性或類名
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'
方案三
wingmeng解決了刷新瀏覽器存儲(chǔ)信息不被清除的問題
!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實(shí)現(xiàn)
未完待續(xù)
每日更新