liam61 / blog Goto Github PK
View Code? Open in Web Editor NEWLawler's Blog 不定期分享一些前端技术
Lawler's Blog 不定期分享一些前端技术
欢迎讨论~
关键词:promise.then 与 setTimeout 并行
抓住 promise 的状态只能由 pending -> fulfilled,或者 pending -> rejected,并且只能进行一次改变
function promiseWithTimeout(url, timeout = 3000) {
return new Promise((resolve, reject) => {
fetch(url).then(data => data.json()).then(data => resolve(data)); // fetch 先得到结果就 resolve
setTimeout(() => reject(Error('time is out!')), timeout); // 时间到了还没 fetch 到就 reject
});
}
promiseWithTimeout('http://localhost:8080/data.json')
.then(data => console.log(data))
.catch(err => console.error(err));
// server.js 测试
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({ origin: '*' }));
app.use('/data.json', (req, res) => {
setTimeout(() => res.end(JSON.stringify({ a: 1 })), Math.floor(Math.random() * 6 * 1000));
});
app.listen(8080, () => console.log('the app is running at http://localhost:8080'));
找准每段时间的变换点即可
function format(date, testMode) {
const MIN = 60 * 1000;
const HOUR = 60 * MIN;
const DAY = HOUR * 24;
const WEEK = DAY * 7;
const time = testMode ? date : Date.now() - date;
if (time < MIN) return '刚刚';
if (time < HOUR) return Math.floor(time / MIN) + '分前';
if (time < DAY) return Math.floor(time / HOUR) + '小时前';
if (time < WEEK) return Math.floor(time / DAY) + '天前';
return new Date(testMode ? Date.now() - date : date).toLocaleString();
}
// 测试
console.log(format(60 * 1000 - 1, true)); // 59 秒 999
console.log(format(60 * 1000, true)); // 1 分
console.log(format(60 * 1000 * 59 + 59 * 1000 + 999, true)); // 59 分 59 秒 999
console.log(format(60 * 1000 * 60, true)); // 1 小时
console.log(format(60 * 1000 * 60 * 23 + 60 * 1000 * 59 + 59 * 1000 + 999, true)); // 23 小时 59 分 59 秒 999
console.log(format(60 * 1000 * 60 * 24, true)); // 1 天
console.log(format(60 * 1000 * 60 * 24 * 6, true)); // 6 天
console.log(format(60 * 1000 * 60 * 24 * 7, true)); // 7 天
// 正常使用
console.log(format(1554111847534)); // 发布时的时间戳
JS 中基本类型
5 种原始类型:Boolean, Number, String, Null, Undefined(暂不考虑 ES6 的 Symbol)还有 Object
// 这里把数组也考虑了进来
function deepClone(someObj) {
const { toString } = Object.prototype;
const isObject = o => toString.call(o).slice(8, -1) === 'Object';
function _deepClone(source) {
if (Array.isArray(source)) { // 处理数组
return source.reduce((res, item) => {
res.push(_deepClone(item));
return res;
}, []);
}
if (isObject(source)) { // 处理对象
return Object.keys(source).reduce((res, key) => {
res[key] = _deepClone(source[key]);
return res;
}, {});
}
return source; // 处理其他类型
}
return _deepClone(someObj);
}
// 测试
console.log(deepClone([1, 2, [3], [4, 5, [6, 7]]]));
console.log(deepClone({ a: 1, b: { c: 3, d: 7, e: { y: 8, z: 4 } } }));
console.log(deepClone(12));
console.log(deepClone(true));
console.log(deepClone(null));
console.log(deepClone(undefined));
console.log(deepClone([1, { a: 9, b: 8 }, [3], [4, 5, [6, 7]]]));
console.log(deepClone({ a: 1, b: { c: [4, 5, [6]], d: 7, e: { y: 8, z: 4 } } }));
关键词:发布-订阅模式
其实核心就是维护一个对象,对象的 key 存的是事件 type,对应的 value 为触发相应 type 的回调函数,即 listeners,然后 trigger 时遍历通知,即 forEach 进行回调执行。
class EventTarget {
constructor() {
this.listeners = {}; // 储存事件的对象
}
on(type, callback) {
if (!this.listeners[type]) this.listeners[type] = []; // 如果是第一次监听该事件,则初始化数组
this.listeners[type].push(callback);
}
once(type, callback) {
if (!this.listeners[type]) this.listeners[type] = [];
callback._once = true; // once 只触发一次,触发后 off 即可
this.listeners[type].push(callback);
}
off(type, callback) {
const listeners = this.listeners[type];
if (Array.isArray(listeners)) {
// filter 返回新的数组,会每次对 this.listeners[type] 分配新的空间
// this.listeners[type] = listeners.filter(l => l !== callback);
const index = listeners.indexOf(callback); // 根据 type 取消对应的回调
this.listeners[type].splice(index, 1); // 用 splice 要好些,直接操作原数组
if (this.listeners[type].length === 0) delete this.listeners[type]; // 如果回调为空,删除对该事件的监听
}
}
trigger(event) {
const { type } = event; // type 为必传属性
if (!type) throw new Error('没有要触发的事件!');
const listeners = this.listeners[type]; // 判断是否之前对该事件进行监听了
if (!listeners) throw new Error(`没有对象监听 ${type} 事件!`);
if (!event.target) event.target = this;
listeners.forEach(l => {
l(event);
if (l._once) this.off(type, l); // 如果通过 once 监听,执行一次后取消
});
}
}
// 测试
function handleMessage(event) { console.log(`message received: ${ event.message }`); }
function handleMessage2(event) { console.log(`message2 received: ${ event.message }`); }
const target = new EventTarget();
target.on('message', handleMessage);
target.on('message', handleMessage2);
target.trigger({ type: 'message', message: 'hello custom event' }); // 打印 message,message2
target.off('message', handleMessage);
target.trigger({ type: 'message', message: 'off the event' }); // 只打印 message2
target.once('words', handleMessage);
target.trigger({ type: 'words', message: 'hello2 once event' }); // 打印 words
target.trigger({ type: 'words', message: 'hello2 once event' }); // 报错:没有对象监听 words 事件!
关键词:Object.defineProperty
class Observer {
constructor(data) { this.data = data; }
observe(prop, cbObj) { // 调用了 observe 才会对属性 prop 进行观察
if (!prop || typeof prop !== 'string') return;
this.defineReactive(this.data, prop, this.data[prop], cbObj);
// 由于 new Observer() 出的对象没有 data.a 属性,必须通过 data.data.a 调用,熟悉 vue 的应该知道这一点
// 为了方便我们直接调用 data.a,把 this.data 的属性代理到 this 上
this.proxyData(prop, this.data);
}
defineReactive(obj, key, value, { getCallback, setCallback }) { // 抽离的对属性进行监控的方法
Object.defineProperty(obj, key, {
get() {
getCallback(value);
return value;
},
set(newValue) {
if (newValue !== value) {
setCallback(newValue, value);
value = newValue;
}
}
});
}
proxyData(key, data) {
Object.defineProperty(this, key, {
get() { return data[key]; }, // 对调用 data.a 进行劫持,返回内部的 data[key]
set(newValue) { data[key] = newValue; } // 对 data.a = 2; 进行劫持,实际上的在操作内部的 data[key] = 2;
});
}
}
// 测试
const data = new Observer({ a: 1, b: 2 });
const callbackObj = {
getCallback(value) { console.log(`get: value: ${value}`); },
setCallback(newValue, oldValue) { console.log(`set: newValue: ${newValue}, oldValue: ${oldValue}`); }
}
data.observe("a", callbackObj);
data.a; // console: 1
data.a = 2; // console: 2 1
data.b; // undefined 因为没有进行 data.observe('b', ...)
要求:使用正则和非正则两种方式实现
评论区大神们,欢迎附上正则版~
// 法1:toLocaleString
// 浏览器提供该 api 对数字进行格式化,不过对于小数的处理使用的是四舍五入
let num = 1234567;
console.log(num.toLocaleString('en-US')); // 1,234,567
num = 12345.6785;
console.log(num.toLocaleString('en-US')); // 12,345.679
// 法2:数组操作
function format(num) {
if (typeof num !== 'number') return num;
const [ first, last ] = (num + '').split('.');
const flen = first.length;
const tmp = first.split('').reverse().reduce((res, n, i) => {
if ((i + 1) % 3 === 0 && i !== flen - 1) res.push(n, ',');
else res.push(n);
return res;
}, []).reverse().join('');
return last ? tmp + '.' + last : tmp;
}
// 法3:递归
function format(num) {
if (typeof num !== 'number') return num;
const [first, last] = (num + '').split('.');
function run(s) {
if (s.length <= 3) return s;
return run(s.slice(0, -3)) + ',' + s.slice(-3);
}
return last ? run(first) + '.' + last : run(first);
}
console.log(format(123456)); // 123,456
console.log(format(1234567)); // 1,234,567
console.log(format(12345.6785)); // 12,345.6785
console.log(format(123456.78)); // 123,456.78
利用数组缓存,再按照标志依次输出
function getData(urls) {
// 感谢 @jinxin0112 的指正
return new Promise((resolve, reject) => {
const res = [], len = urls.length;
urls.forEach((url, i) => {
fetch('http://localhost:8080' + url).then(data => data.json())
.then(data => {
res[i] = { data, printed: false }; // 将数据放入缓存数组
let flag = true;
for (let j = 0; j < len && flag; j += 1) {
if (res[j]) { // 如果标志为 j 的有返回值,则继续
if (!res[j].printed) {
console.log(res[j].data);
res[j].printed = true;
j === len - 1 && resolve(res.map(o => o.data))
}
} else { // 无返回值,则跳出
flag = false;
}
}
}, reject);
});
})
}
const listPromise = getData(['/data.json', '/data2.json', '/data3.json', '/data4.json']);
listPromise.then(res => console.log(res));
// server.js 测试:模拟各个请求延迟返回
const express = require('express');
const cors = require('cors');
const app = express();
const getTime = () => Math.floor(Math.random() * 4 * 1000);
app.use(cors({ origin: '*' }));
app.use('/data.json', (req, res) => {
setTimeout(() => res.end(JSON.stringify({ a: 1 })), getTime());
});
app.use('/data2.json', (req, res) => {
setTimeout(() => res.end(JSON.stringify({ b: 2 })), getTime());
});
app.use('/data3.json', (req, res) => {
setTimeout(() => res.end(JSON.stringify({ c: 3 })), getTime());
});
app.use('/data4.json', (req, res) => {
setTimeout(() => res.end(JSON.stringify({ d: 4 })), getTime());
});
app.listen(8080, () => console.log('the app is running at http://localhost:8080'));
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.