shuch / blog Goto Github PK
View Code? Open in Web Editor NEW忘记所有,从新开始
忘记所有,从新开始
class Cat {
say() {
console.log("meow ~")
}
}
利用es5 function 实现:
function Cat() {}
Object.defineProperty(Cat.prototype, 'say', {
value: function() { console.log("meow ~"); },
enumerable: false,
configurable: true,
writable: true
})
实际上当我们给一个类添加一个属性的时候,会调用到 Object.defineProperty 这个方法,它会接受三个参数:target 、name 和 descriptor ,上面两种写法等价
function isAnimal(target) {
target.isAnimal = true
return target
}
// 装饰器
@isAnimal
class Cat {
// ...
}
console.log(Cat.isAnimal) // true
// 上面装饰器代码基本等同于
Cat = isAnimal(function Cat() { ... })
decorator
优势
参考:
Decorator 浅析与实践
经历过H5接入echart 的需求后,同时小程序端使用taro开发,本以为小程序接入图表不是很难的事,结果硬生生躺了一次坑,上线之后决定总结一次,记录开发期间遇到的各种问题以及解决办法,方便自己后续迭代以及后来者避开风险,只有在面临bug和上线的一番对抗和内心挣扎之后,才会理解经验是多么宝贵。
ec-canvas
文件过大(超过500k),微信开发者工具无法编译上传的问题按照官网的指示,下载 ec-canvas
包引入工程目录,github版本的 echart.js 文件大小为 700k,虽然本地可以成功引入并绘制,但最后一步上传却让人大失所望,超过微信单个文件大小限制500k。
此时官网按照指示,在线定制 echart.js 内容
定制好后,成功将文件大小控制在363k,导入工程目录替换原文件。然而经过taro编译压缩过后,原来的图表变成一片空白,内心崩溃。
经过几番折腾过后,终于找到了解决办法:
在官网定制时不能勾选代码压缩,下载源文件后去专业的压缩网站压缩代码,再导入工程目录
此时上传问题迎刃而解。出现这个问题的原因在于,taro在编译 echart.js 时会将注释的部分内容进行编译导致代码报错,报错截图为证:
2020.4.11更新
echart官网在线定制源文件,目前已经移除license注释信息
在经过一番挣扎过后,终于可以画出满意的折线图了,微信开发者工具中一切风平浪静,上传到体验版后,傻眼了,弹窗和底部悬浮button都被图标盖住了,这下改动就大了。于是傻乎乎的把底部button和弹窗改成了 cover-view,再到真机上,渲染一个列表就卡的不行,更不谈样式的各种不兼容。看来cover-view 虽然强大可也不能滥用啊!
经过一番思索,决定在渲染完成后,启用 echart 的保存图片功能,将绘制完成的 canvas 保存成图片。
const ecComponent = this.$scope.selectComponent('#deal-chart-dom-area');
if (!ecComponent) {
return;
}
ecComponent.canvasToTempFilePath({
success: res => {
this.setState({ img: res.tempFilePath });
},
fail: res => console.error('canvasToTempFilePath', res)
});
可问题是,图片虽然可以解决层级问题,但失去了图表的可交互性,无法两全其美,如果能在初始状态echart渲染图表,渲染完成后将canvas转换成图片保存下来,在有弹框时将 canvas 切换成图片,就可以解决这个问题。实际效果看上去还可以接受,剩下的问题就是控制图片和图表的切换,工作量应该会大幅减少。
生产环境中,在部分安卓手机上保存图片会失败,查找原因,将下部分代码
ctx.draw(true, () => {
wx.canvasToTempFilePath(opt, this);
});
加100ms延时,改成
ctx.draw(true, setTimeout(() => {
wx.canvasToTempFilePath(opt, this);
}, 100));
重新编译,图片就能正常保存啦!
总结来看,微信小程序做图表不是一个很好的方式,从长期看,建议使用h5承载图表业务,代码的健壮性和扩展性能更加可控。
以上是开发过程的不完全记录,也是个人的解决办法,可能有不妥之处,如果有更好的微信图表方案,欢迎留言探讨!
try {
throw new Error('throw error');
} catch (e) {
console.log(e);
}
try {
setTimeout(() => {
throw new Error('throw error');
}, 1000);
} catch (e) {
console.log(e);
}
异步代码异常用try/catch
捕获不了,因为event loop
,异步函数在事件队列中,执行是在调用栈顶部,脱离了定义的上下文,如果抛出异常,无法追溯到异步任务的创建者。
callback
处理异常回调函数第一个参数是error
对象,如果没有错误就是null
fs.readFile('noexist', (err, data) => {
if (err) throw err;
console.log(data);
})
event
捕获异常const EventEmitter = require('event')
const myEvent = new EventEmitter();
myEvent.on('error', function (e) {
console.log('error event fired', e);
})
myEvent.emit('error', new Event('error from event'))
promise
捕获异常new Promise((resolve) => {
throw new Error('123');
}).catch(e => {
console.log(e);
})
process
捕获异常process.on('uncaughtException', (e) => {
console.log('catch exception',e );
})
setTimeout(() => {
throw new Error('124')
}, 1000);
domain
捕获异常const domain = require('domain');
const d = domain.create();
d.on('error', (e) => {
console.log('domain error', e);
})
d.run(() => {
setTimeout(() => {
throw new Error('124');
}, 1000);
});
d.run(() => {
throw new Error('123');
})
try/catch
callback
、promise
、event
event
包含原生EventEmitter
、全局process
、domain
方式动态计算font-size,计算公式:
375 / 16 = deviceWidth / fontSize
document.documentElement.style.fontSize = (document.body.clientWidth / 375) * 16 + 'px';
按750设计稿,基础字体大小 16px计算
375/100=3.75=1vw
jit是Just-in-Time 及时编译的缩写,特点是程序执行时进行编译。
aot是 Ahead-of-Time 提前编译的缩写,特点是程序执行前进行编译。
try/catch
,在catch
块中上报错误信息promise.catch
捕获错误window.onerror
window.onerror = function(message, source, lineno, colno, error) {
// 错误信息
console.log('message', message);
// 发生错误脚本URL
console.log('source', source);
// 错误行号
console.log('lineno', lineno);
// 错误列号
console.log('colno', colno);
// 错误堆栈信息
console.log('error', error);
// 阻止错误打印
return true;
}
throw new Error(123);
promise
代码抛出的错误,无法在window.error
和try/catch
中捕获,可以添加promise全局异常捕获:
window.addEventListener('unhandledrejection', function(e) {
// 错误信息
console.log('reason', e.reason);
e.preventDefault();
});
function report(message, source, lineno, colno, error) {
var url = 'https://report.net/error';
url += '?msg='+message+'&source='+source+'&row='+lineno+'&col='+colno+'&err='+error;
var img = new Image();
img.url = url;
}
线上代码一般都有压缩,map
文件存贮着源文件和压缩文件的映射关系,可以定位到源文件的出错位置。
source-map
bundle.js.map
文件发送到监控系统保存source-map
查出源文件的报错位置1.安装 n 模块
npm install -g n
2.安装稳定版本
n stable
3.安装最新版本
n latest
4.安装具体版本
n v12.4.0
IntersectionObserver
const observer = new IntersectionObserver((entries) => {
const image = entries[0];
if (image.isIntersecting) {
setLoading(false);
observer.unobserve(ref.current);
}
});
observer.observe(ref.current);
getBoundingClientRect
function elementInViewport(el) {
var rect = el.getBoundingClientRect()
return (
rect.top >= 0
&& rect.left >= 0
&& rect.top <= (window.innerHeight || document.documentElement.clientHeight)
)
}
JavaScript
延迟加载react
中的suspense
ES6
动态 import()
const ProfilePage = React.lazy(() => import('./ProfilePage')); // 懒加载
// 在 ProfilePage 组件处于加载阶段时显示一个 spinner
<Suspense fallback={<Spinner />}>
<ProfilePage />
</Suspense>
font-display
@font-face {
font-family: 'MyWebFont'; /* Define the custom font name */
src: url('myfont.woff2') format('woff2'),
url('myfont.woff') format('woff'); /* Define where the font can be downloaded */
font-display: fallback; /* Define how the browser behaves during download */
}
在开发模式下,使用代码热替换可以提高开发效率,使用 HMR 过程的总结如下:
src/index.js
import './style.css';
import img from './cat.jpg';
const cat = new Image();
cat.src = '../' + img;
document.body.appendChild(cat);
底部添加 HMR
客户端
if (module.hot) {
module.hot.accept();
}
src/index.html
引入打包好的文件
<script src="bundle.js"></script>
因为webpack-dev-server
将文件打包到内存,引入时需要访问项目根路径/
webpack.config.js
devServer: {
contentBase: './src',// 访问http://localhost:3000/到src/index.html
port: 3000,
}
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
npx webpack-dev-server
,访问http://localhost:3000/
index.js
文件,控制台即可看到效果[HMR] Waiting for update signal from WDS...
[WDS] Hot Module Replacement enabled.
[WDS] Live Reloading enabled.
webpack-hot-middleware
模式配合express 插件模式,开启HMR
webpack.config.js
entry: {
app: ['./src/index.js', 'webpack-hot-middleware/client'],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
webpack-hot-middleware
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const app = express();
const config = require('../webpack.dev.config');
const compiler = webpack(config);
app.use(webpackDevMiddleware(compiler));
app.use(webpackHotMiddleware(compiler));
index.js
添加 hot module replacement
浏览器端入口,跟webpack-dev-server
模式相同。if (module.hot) {
module.hot.accept();
}
配置完成后,启动nodejs
服务,改动客户端index.js
代码,即可发现浏览器更新了
[HMR] connected
[HMR] bundle rebuilding
[HMR] bundle rebuilt in 109ms
[HMR] Checking for updates on the server...
[HMR] Updated modules:
[HMR] - ./src/print.js
[HMR] App is up to date.
node
服务器中引入中间件,实现HMR
。模块化的出现是为了代码复用,隔离作用域
IIFE
Immediately Invoked Function Expression
立即执行函数表达式
(function() {
var x = 1;
var y = 2;
consoe.log(x + y);
})()
在自执行函数中编写代码,隔离外部环境
AMD
Asynchronous Module Definition
异步模块定义
// 定义 math.js
define(function() {
var add = function(x, y) {
return x + y;
}
return {
add,
};
})
// 引入
require(['math'], funtion(math) {
console.log(math.add(1, 2));
})
推崇依赖前置,实现有require.js和curl.js
CMD
Common Module Definition
通用模块定义
define(function(require, exports) {
// 获取模块 math 的接口
var a = require('./math');
// 调用模块 math 的方法
a.add(1, 2);
});
推崇依赖后置,在使用的地方requre
,sea.js实现
commonjs
nodejs
服务端模块规范
var math = require('math');
math.add(1, 2);
模块运行时同步加载,引入对象的拷贝,有缓存。
UMD
Universal Module Definition
通用模块定义,兼容以上amd,cmd,commonjs
三种规范
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
//Node, CommonJS之类的
module.exports = factory(require('jquery'));
} else {
//浏览器全局变量(root 即 window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
//方法
function myFunc(){};
//暴露公共方法
return myFunc;
}));
webpack require ensure
webpack 2.x
中的代码分割方法,通过jsonp
的形式异步加载代码。目前被es module
动态import()
取代。
ES Module
ES2015
引入的模块化规范。
import math from './math.js';
math.add(1, 2);
浏览器支持script
标签运行es module
<script type="module"></script>
属于编译时静态引入,输出的是模块的引用地址,且引用是只读的。
浏览器是多线程的
GUI
渲染线程JavaScript
引擎线程为了防止渲染出现不可预期的结果(js
可能修改dom
或css
),GUI
渲染线程和JavaScript
引擎线程互斥。
HTML
构建DOM
树 Dom Tree
CSS
构建 CSS 规则树 CSS Rule Tree
CSS
规则树附加到 DOM
树上,删除不可见元素,形成渲染树 Render Tree
Reflow
或Layout
):计算每个元素盒子位置和大小Repaint
):根据元素的位置和大小,计算元素的绝对像素Display
):将绝对像素发给GPU
,将多个合成层合并,形成一个层。元素新增和删除以及位置或大小改变会触发节点的回流(Reflow),之所以叫回流是因为浏览器默认的布局方式是流式布局。参考回流和重绘
js
操作dom
style
样式改变通过class
而不是手动修改GPU
加速渲染will-change
和translateZ
documentFragment
离线操作 dom
web worker
中完成async
属性会异步下载script
,下载后立即执行,下载完成前不影响html
解析。defer
属性会异步下载script
,下载后延迟执行,等到html
解析完成,执行完成后触发DOMContentLoaded
事件async
是无序的,而 defer
有序先看一个图:
同步过程:
所谓JavaScript单线程语言,指的是只有一个执行栈,采用FILO先进后出的顺序执行。
异步过程:
event table
中注册回调函数,继续执行执行栈中的其他任务(WebAPIs)。event queue
中,也叫做callback queue
stack
中当前宏任务执行完毕,主线程会查找event queue
中的事件,并将其回调依次加入到执行栈,异步任务执行。从第一个同步任务开始执行到第一个异步任务执行完毕的过程,称为一次事件循环 event loop
。
任务task
包含任务队列task queue
和微任务队列microtask queue
,参考。
在一个事件循环中,异步任务返回的结果会被放入到一个任务队列中。根据异步事件的类型,这个事件会被推到对应的任务队列或者微任务队列中。在当前执行栈为空的时候,主线程会查看微任务队列是否有事件存在。
如果存在,则会依次执行微任务队列中的事件对应的回调,直到微任务队列为空。然后去Task Queue
中取出一个事件,把对应的回调推入执行站。
如果不存在,再去Task Queue
中取出一个事件,并把它对应的回调推入到当前执行栈。
task queue
:setInterval, setTimeout, setImmediate
microtask queue
:promise.then(), process.nextTick(), new MutaionOberserver()
结论:
只有执行栈(stack)执行完毕,才去处理微任务队列中的事件,然后再去task queue
中取出一个事件。同一次事件循环,microtask queue
在task queue
之前执行。
node 中的 event loop
由 libuv
实现(源码),不同的任务会分配给不同的线程。总共分为6个阶段,不同阶段对应执行不同的task queue
,每个阶段task queue
执行完后再去执行微任务队列中的任务。
setTimeout
或 setInterval
回调函数fs
或 socket
回调函数setImmedate
回调函数process.nextTick
是独立于任务队列之外的任务,优先于微任务执行.参考setTimeout(function () {
console.log(1);
});
new Promise(function(resolve,reject){
console.log(2)
resolve(3)
}).then(function(val){
console.log(val);
})
打印结果:2 3 1
process.nextTick(() => {
console.log('nextTick')
})
Promise.resolve()
.then(() => {
console.log('then')
})
setImmediate(() => {
console.log('setImmediate')
})
console.log('end')
打印结果
end
nextTick
then
setImmediate
With the new changes in Node v11, nextTick callbacks and microtasks will run between each individual setTimeout and setImmediate callbacks, even if the timers queue or the immediates queue is not empty
background-image
和linear-gradient
background: url(bg.png) right bottom /136px no-repeat, linear-gradient(125deg, #F54732 0%, #FF7338 100%);
过程:
基于 cookie session 认证的特点是服务端维护用户信息,如果一个应用存在多个服务,根据跨域原则,实现单点登录就需要在多个服务器之间共享用户信息。另外一种是基于 token 的身份验证方式。
过程:
这种做法好处在于服务端不需要存储当前用户的信息,减轻服务端同步数据的压力,易于实现单点登录。缺点是在有效期之前用户退出,服务端无法判断是否有效,客户端需要销毁失效的 token,并在下一次请求时重新登录。
JWT
基于 token 的验证方式比较流行的是 JSON Web Token(JWT),下图是它的登录原理:
另外两种方式是 SAML 和 2.0 OAuth 2.0 ,后面继续更新。
当执行git push
时,git
控制台有报错信息:
> git fetch
error: cannot create pipe for ssh: Too many open files
fatal: unable to fork
原因是git
文件占用内存超出限制,手动执行垃圾回收,清理git
缓存
git gc
runtime广义理解包括三个范畴
Rust
比 C
更容易将错误发现在编译时而非运行时。C
语言stdio.h
JavaScript
的nodejs
或浏览器一般指js运行的宿主环境
js
引擎JavaScriptCore
负责js代码的解析和执行Hermes
对js
代码进行预编译(字节码),减少加载代码体积,缩短了用户交互时间。js
和 native
通信JavaScriptCore
直接执行js代码。1.下载 flutter repo
2.编辑 .zshrc
mac catalina 系统,编辑 $HOME/.zshrc 文件,加入
export PATH="$PATH:/Users/shuchen/Code/flutter/bin"
运行 source .zshrc
生效
3.检查 PATH
echo $PATH
which flutter
浏览器同源安全策略,包括协议,域名和端口,只要一个不同,即为跨域。
如果没有同源策略,别人的网站可以获取自己已登录网站的cookie
,数据泄漏。
要知道,浏览器可以正确做出请求,服务器也能正确做出响应,但浏览器会拒绝接受跨域服务器的响应
jsonp
iframe
img src
Cross-origin Resource sharing,需要客户端和服务端支持,分为简单请求(GET,POST,HEAD)和非简单请求(PUT,DELETE)
简单请求
客户端请求头带 origin
字段,标识请求源
Origin: http://shuch.com
服务器根据origin
字段判断是否同意请求。同意请求的响应头
Access-Control-Allow-Origin: http://shuch.im
如果允许任意来源访问,可用 *
标识
非简单请求
浏览器会先发送一个预检请求(Preflight Request),询问服务器是否支持该请求
OPTION: api.com/delete/1
Origin: http://shuch.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Content-Type
若服务器支持,则返回响应体
Access-Control-Allow-Origin: http://shuch.com
Access-Control-Allow-Methods: GET,POST,DELETE
Access-Control-Allow-Headers: Content-Type
浏览器检测响应头中包含origin
,则正常发出请求
携带身份认证信息
XMLHttpRequest
请求默认不发送cookie
, 可以设置 withCredentials: true
来允许浏览器发送cookie
信息,同时服务器响应头中应包函
Access-Controll-Allow-Credentials: true
若服务器没有返回该字段,则浏览器不会把响应体给发送端。
·
该响应头和Access-Controll-Allow-Origin:*
互斥,若服务器设置通配符,客户端请求失败。
使用ajax
向服务端发出请求,带上请求路径以及参数,代理服务端接收到请求后,直接对路径和参数转发请求,由于服务端不受跨域请求限制,等接收到响应后再返回到前端,实现跨域。
相比于CORS
跨域,JSONP
仅支持GET
请求,但浏览器兼容性强。
利用script
标签的跨域特点,定义一个回调函数callback
,在服务器返回json
数据时,包装一层callback(json)
,在客户端执行。
为避免callback
冲突,客户端可用时间戳设置函数名。
function jsonp() {
return new Promise((resove, reject) => {
var script = document.createElement('script');
var calbackName = 'callback' + Date.now();
script.src = req.url + '?callback=' + callbackName;
window[calbackName] = function(json) {
resovel(json);
}
script.onerror = function() {
reject(new Error('error' + script.src));
}
});
}
jsonp({
url: 'https://api.com/get',
}).then(res => {
console.log(res);
});
iframe
跨域将两个文档document.domain
设置为相同实现跨域
postMessage
通信
// 发送消息
function sendMessage() {
const iframe = document.getElementById('iframe');
iframe.contentWindow.postMessage({a: 1}, 'http://localhost:3001');
}
// 接受消息
window.addEventListener('message', (e) => {
if (e.origin === "http://localhost:3000") {
console.log('receive message', e.data);
e.source.postMessage({
a: 'hi 3000',
arr: [1,2,3],
date: new Date(),
}, e.origin);
}
})
window.name
跨域
原理是同一个tab
下设置的window.name
可以共享
window.location.hash
跨域
// 改变`hash`
function changeHash() {
const iframe = document.getElementById('iframe');
iframe.src = 'http://localhost:3001' + '#' + JSON.stringify({ a: 1});
}
// 监听`hash change`
window.addEventListener('hashchange' , (e) => {
const hash = window.location.hash;
const data = decodeURIComponent(hash.split('#')[1]);
console.log('hash from parent: ', JSON.parse(data));
window.parent.location.href = "http://localhost:3000#" + data;
})
利用图片的src
属性可以跨域的特点,发送请求,一般用于数据埋点,性能监控
优势
1001 | 1010 = 1011 // 合并1
1001 & 1010 = 1000 // 只保留共有1
- 原文地址:Javascript – How Prototypal Inheritance really works
- 原文作者:vjeux
众所周知,JavaScript有原型继承性。但通常情况下,原型继承是通过new
操作符实现的,大部分的解释都让人困惑,这篇文章旨在澄清什么是原型继承以及如何使用。
JavaScript原型通常是这样定义的:
When accessing the properties of an object, JavaScript will traverse the prototype chain upwards until it finds a property with the requested name. Javascript Garden
JavaScript通过__proto__
属性来代表原型链中下一个对象。跟着这片文章,让我们看看__proto__
和prototype
的区别。
说明:
__proto__
是JavaScript非标准属性,最好不要在你的代码中用到。当然,这篇文章是用来解释JavaScript原型继承的原理。
下面的一段代码展示了JavaScript引擎是如何找一个对象的属性:
function getProperty(obj, prop) {
if (obj.hasOwnProperty(prop))
return obj[prop]
else if (obj.__proto__ !== null)
return getProperty(obj.__proto__, prop)
else
return undefined
}
根据原型继承的定义,我们创建一个对象 Point
它包含两个属性x,y
坐标和一个print
方法。
为了创建一个新的point
对象,我们只需要创建一个对象,并将新的对象的__proto__
属性指向Point
var Point = {
x: 0,
y: 0,
print: function () { console.log(this.x, this.y); }
};
var p = {x: 10, y: 20, __proto__: Point};
p.print(); // 10 20
奇怪的是,大家都知道原型继承的定义,但并不会这么写。反而更喜欢下面的写法:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype = {
print: function () { console.log(this.x, this.y); }
};
var p = new Point(10, 20);
p.print(); // 10 20
这跟上面写的代码完全不同,这里的Point
是一个函数,他使用了一个prototype
的属性,还有new
操作符,这是个什么鬼?
Brendan Eich 在创造JavaScript
时,想让它看起来像传统的面向对象语言,类似于Java、C++。在传统的面向对象语言中,使用了 new
操作符来创建一个类的实例。所以,他也写了一个JavaScript
的new
操作符:
new
操作符的对象应该是一个函数。prototype
属性中。new
操作符接收一个函数F
和参数arguments
: new F(arguments...)
,总共做了三件事:
__proto__
属性设置为F.prototype
F
,并把this
绑定到实例上。 function New (f) {
/*1*/ var n = { '__proto__': f.prototype };
return function () {
/*2*/ f.apply(n, arguments);
/*3*/ return n;
};
}
写一个测试用例来检验一下:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype = {
print: function () { console.log(this.x, this.y); }
};
var p1 = new Point(10, 20);
p1.print(); // 10 20
console.log(p1 instanceof Point); // true
var p2 = New (Point)(10, 20);
p2.print(); // 10 20
console.log(p2 instanceof Point); // true
JavaScript规范只给我们提供了 new
操作符,然而,Douglas Crockford 找到一种方法,利用new
操作符实现了真正的原型继承。
他创建了 Object.create 方法:
Object.create = function (parent) {
function F() {}
F.prototype = parent;
return new F();
};
代码看起来很奇怪,但做的事情确很简单,它创建了一个新的对象并把它的prototype
属性设置为任意一个你要创建的对象。如果允许使用__proto__
的话,还可以这么写:
Object.create = function (parent) {
return { '__proto__': parent };
};
使用真正的原型继承,第一个Point
例子可以改写成:
var Point = {
x: 0,
y: 0,
print: function () { console.log(this.x, this.y); }
};
var p = Object.create(Point);
p.x = 10;
p.y = 20;
p.print(); // 10 20
我们已经了解了什么是原型继承,以及实现原型继承的几种方式。
但是,使用__proto__
和 Object.create()
有一些缺点:
__proto__
是非标准化的属性,甚至会被废弃。原生的Object.create()
方法实现方式和 Douglas Crockford 实现方式并不完全相同。Object.create()
方法跟new
不同,还没有被优化过。速度会慢上10倍。引申阅读:
如果你能看懂下面这张图(来自 ECMAScript 标准),你将会有意想不到的收获!
Object/Prototype Relationshipsvar updateManager = wx.getUpdateManager();
updateManager.onCheckForUpdate(function (res) {
// 请求完新版本信息的回调
console.log(res.hasUpdate)
})
updateManager.onUpdateReady(function () {
wx.showModal({
title: '更新提示',
content: '新版本已经准备好,是否重启应用?',
success: function (res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate()
}
}
})
})
upload.html
<input type="file" name="myfile" id="file">
<script type="text/javascript">
document.getElementById('file').onchange = function (event) {
const xhr = new XMLHttpRequest();
var formData = new FormData();
const file = event.target.files[0];
formData.append("myfile", file);
xhr.open("POST", "/profle", true);
xhr.send(formData);
}
</script>
server.js
const express = require('express')
const multer = require('multer')
const path = require('path')
const app = express();
const upload = multer({ dest: 'upload/' })
app.post('/profle', upload.single('myfile'), function (req, res, next) {
// console.log(req.body);
res.send({ret_code: '0'});
})
app.get('/upload', function(req, res, next) {
const html = path.join(__dirname, 'upload.html');
res.sendFile(html);
})
app.listen(3000);
node server.js
正常的话,upload目录目下会多出一个文件。
MulterError: Unexpected field
at wrappedFileFilter (D:\code\study\html\node_modules\multer\index.js:40:19)
at Busboy.<anonymous> (D:\code\study\html\node_modules\multer\lib\make-middleware.js:114:7)
at emitMany (events.js:147:13)
at Busboy.emit (events.js:224:7)
at Busboy.emit (D:\code\study\html\node_modules\busboy\lib\main.js:38:33)
at PartStream.<anonymous> (D:\code\study\html\node_modules\busboy\lib\types\multipart.js:213:13)
at emitOne (events.js:116:13)
at PartStream.emit (events.js:211:7)
at HeaderParser.<anonymous> (D:\code\study\html\node_modules\dicer\lib\Dicer.js:51:16)
at emitOne (events.js:116:13)
如果遇到上传失败,检查 upload.single('myfile')
文件名是否与 formData.append("myfile", file)
文件名对应
“计算机科学只存在两个难题:缓存失效和命名。” ——Phil KarIton
按存储位置来分,缓存分为以下几种:
Service Worker
Memory Cache
Disk Cache
优先级由上到下,如果没有命中,就走HTTP Network
Service Worker
Service Worker
提供一种灵活的缓存方案,允许开发者中手动存储缓存文件,以及配置缓存匹配策略。
install
安装实例,添加缓存activate
激活实例,删除旧缓存fetch
配置缓存匹配规则Memory Cache
浏览器缓存策略。几乎所有的网络请求,都会被加入内存缓存。主要分为preloader
和preload
css
的@import
<video poster />
<link rel="preload" />
效果是相同一个请求img src
或link href
不会请求两次。在页面被关闭时,缓存失效。
Disk Cache
绝大部分缓存都来自磁盘缓存,并遵循HTTP Header
规则,包括强缓存和协商缓存
强缓存
Expires
(http 1.0)Cache-Control
(http 1.1)协商缓存
Last-Modified
和If-Modified-Since
Etag
和 If-None-Match
Cache-Control
常规字段public
公共资源缓存,服务器和客户端都缓存private
私有资源,只有客户端可缓存max-age
客户端最长缓存时间,相对值no-cache
客户端不使用强制缓存,而走协商缓存no-store
客户端不缓存,每次请求服务器Cache-Control
优先于Expires
Etag
优先于Last-Modified
Cache-Control: no-cache
和Cache-Control: max-age: 0
等效,目的是不取强缓存,请求服务端验证资源新鲜度(有效性)。s-maxage
和max-age
作用一样,前者只在代理服务器(类似nginx
、cdn
)生效,优先级高于max-age
和expire
。当请求返回时,有三种情况
memory cache
cache-control
设置disk cache
service worker
的fetch
规则决定是否缓存文件function PubSub() {
this.store = {};
this.on = function(event, fn) {
var events = this.store[event];
if (!events) {
this.store[event] = [];
}
events.push(fn);
return fn;
}
this.emit = function(event) {
var events = this.store[event];
if (!events) {
return;
}
for (var i=0;i<events.length;i++) {
var event = events[i];
event();
}
}
}
function PubSub() {
this.store = {};
this.off = function (event, fn) {
var events = this.store[event];
if (!events) {
return;
}
var index = events.indexOf(fn);
console.log('index',index);
if (index !== -1) {
events.splice(index , 1);
}
}
}
function PubSub() {
this.store = {};
this.once = function (event, fn) {
var events = this.store[event];
if (!events) {
return;
}
var index = events.indexOf(fn);
console.log('index',index);
if (index !== -1) {
events.splice(index , 1);
}
}
}
在 JavaScript 中声明一个对象或数组有两种主要的方式。一种是用字面量语法创建,另一种是使用冗长的构造器方式。
var obj = {};
var arr = [];
或者
var obj = new Object;
var arr = new Array;
多年来,由于以下三个原因,使用字面量语法已经成为习惯方式,正因为如此,我们也应该使用这种方式。
Array = Object = function () {
alert('Foo');
};
var arr = new Array; // alerts "Foo"
var obj = new Object; // alerts "Foo"
你可能会想,我怎么可能覆盖 Object 和 Array ,我不担心。不过要记住你永远无法阻止第三方库会这么做,非常可能一不小心,你的程序就跪了。
var obj = {
prop: 1,
add: function (val) {
return this.prop + val;
}
};
// What do you think ar and ar2 equal?
var ar = new Array(1);
var ar2 = new Array(1,2,3,4);
// Probably this?
alert(ar.join(",") === "1");
alert(ar2.join(",") === "1,2,3,4");
事实上,当 new Array()
接收一个参数,它会创建长度为这个参数的数组,每个数组元素的值是 undefined。当数组构造器接收多个参数时,它会创建一个数组,每个数组元素就是传入的值。
这里有个疑问,如果遇到Object 或 Array 对象被覆盖,有方法可以还原吗?希望有答案的同学通知我。
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.