gnosis23 / hello-world-blog Goto Github PK
View Code? Open in Web Editor NEW还是 issues 里面写文章方便
Home Page: https://bohao.work
还是 issues 里面写文章方便
Home Page: https://bohao.work
也许只有一日
下 android studio;更改源
allprojects {
repositories {
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public'}
google()
jcenter()
}
}
介绍的书太多了。
// file1.js
export function rush() {
}
// file2.js
import { rush } from './file1';
rush();
由 Node.js 采用的模块化规范。
模块定义
// math.js
exports.add = function(a, b) {
...
}
模块导入
var math = require('math');
// use
math.add(1, 1);
待续
具体参考 AMD 规范
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
exports.verb = function() {
return beta.verb();
//Or:
return require("beta").verb();
}
});
改进版 AMD
虚拟节点的数据结构很普通,多了一个key属性,children 是个数组;
class VNode {
nodeName: string | function;
children: (VNode | string)[];
key: string | number | undefined;
attributes: object;
}
使用 Mocha 的过程中有没有以下疑问:
Node 环境下可以通过访问全局变量 global
,设置 expect 和 describe 即可
浏览器环境下就访问 window
mocha 有个 --require
的选项,可以传入转码器的名称。常用的如 babel-register
,可以运行时改变引用的源文件。
// 这里这个路径要注意,require 的时候最好传入测试文件所在项目的 node_module 下的路径
require(babelRegisterPath);
类型信息太重要了,可以提前发现问题、辅助IDE提示。
learn by examples...
redux 的中间件概念上不大难理解,直到它和异步操作搅合在一起....
洋葱圈吧
不要长篇大论了,直接看一个日志的例子
const logger = store => next => action => {
console.group(action.type)
console.info('dispatching', action)
// 下一个中间件处理
let result = next(action)
console.log('next state', store.getState())
console.groupEnd()
return result
}
注意中间件的返回值,最后的返回值可以通过 store.dispatch
获得。
有些时候我们可以从中间件返回一个 promise ,就可以在 redux 里做一些异步操作, 比如
// middleware
const gaga = store => next => action => {
if (action.type !== 'DELAY') return next(action);
return new Promise((resolve, reject) => {
next(Object.assign({}, action, {resolve, reject}))
})
}
// user interface
store.dispatch({ type: 'DELAY' }).then(result => {
// do sth
})
源代码很短...
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
15: old
16+: Fiber
Object.defineProperty
方法,什么时候需要用到Object.keys
的区别new
是怎么实现的__proto__
区别注:prototype 是函数的属性。
注:
注:
var a = {name: "前端开发"}; var b = a; a = null
那么b输出什么var a = {b: 1}
存放在哪里var a = {b: {c: 1}}
存放在哪里Html5 拖拽资源:
React 拖拽相关资源:
React dnd 例子:
以下都是各种类库冗长的实现细节。
在 mouseDown 的时候添加 mouseMove, mouseUp 事件到 document 和 本体上,然后 mouseUp 或者 unMount 的时候移除挂载的事件。
位移是通过 css 的 translate 来实现的。
用这个包搭个入门基本的爬虫简直so easy。
前期学一下基本概念,还有 Python 语法...
# installation
pip install scrapy
scrapy startproject spider1
scrapy genspider [option] name url
scrapy crawl spider_name
谈谈我怎么学习 Webpack的。
这个工具配置项目繁多,看了一遍文档一头雾水。
找几个模板学习一下,大概知道哪个地方是做什么的。然后试着自己折腾一套配置。
推荐两个模板
这个阶段可以自己开发一些 loader 和 plugin ;主要是大概了解下原理,可以试着找些小的项目来看。
这里推荐几个loader:
开发资源:
这个阶段可以去研究下 webpack 的原理了;试着造些轮子和高度定制 webpack。
推荐下几个教程
从招聘网站上搜罗的前端技术栈,持续收集中
从 2019 年各种发展趋势来看:除了三大框架外,推荐学习
通常使用 webpack 开发的时候会用 webpack-dev-server 配合热部署,然而有些时候已经有了自己的server,就不是很方便。官方提供了一个 webpack-dev-middleware 插件来帮助开发。
下面是使用 webpack-dev-middleware 插件(简称wdm) 的时候碰到的问题。
在一个 SPA 应用里刷新网页,如 'localhost:3000/foo/bar',后台会报错 not found error。
原因比较好找,因为后台只有 /
对应的文件,其他的路由都是前端路由。
所以我们需要一种方式将这些路径全部定向到 index.html 上。
const express = require('express')
const path = require('path')
const app = express()
// eslint-disable-next-line no-undef
if (!__DEV__) {
app.use(express.static(path.resolve(process.cwd(), 'public')))
} else {
const webpack = require('webpack')
const webpackConfig = require('../webpack.config')
const compiler = webpack(webpackConfig)
compiler.apply(new webpack.ProgressPlugin())
app.use(require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
headers: { 'Access-Control-Allow-Origin': '*' },
hot: true,
quiet: true,
noInfo: true,
stats: 'minimal',
}))
app.use(
require('webpack-hot-middleware')(compiler, {
log: false
})
)
}
// eslint-disable-next-line no-console
app.listen(3000, () => console.log('Example app listening on port 3000!'))
express的中间件采用一种洋葱圈模型,一环套一环,wdm 也不例外;
最好的方案当然是先访问 wdm 这个中间件。然后如果不匹配,再访问我们自己的中间件,将路由重定向到 /
;
但是不清楚如何将其余路由重定向到index这步
,只能先用一种比较蠢的方法:在 wdm 前面加个中间件,当发现路由不是静态资源的时候,就把请求的url改成/。
+ // stupid spa router middleware
+ app.use(function (req, res, next) {
+ const validSuffix = ['.js', '.css', '.png', '.jpg', '.jpeg'];
+ if (validSuffix.every(x => !req.url.endsWith(x))) {
+ req.url = '/'
+ }
+ next()
+ })
这种方式就是要过滤一大堆后缀,拓展性比较差。
wdm
有个选项叫 writeToDisk 可以将某些文件写到硬盘上,可以将 index.html 写到本地。
else {
app.use(require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
headers: { 'Access-Control-Allow-Origin': '*' },
hot: true,
quiet: true,
noInfo: true,
stats: 'minimal',
// index.html 存到本地,方便下一步返回
+ writeToDisk: filePath => /index.html$/.test(filePath)
}))
app.use(
require('webpack-hot-middleware')(compiler, {
log: false
})
)
}
+ // 如果没有命中wdm,默认返回首页
+ app.use((req, res) => {
+ res.sendFile(path.resolve(process.cwd(), 'public/index.html'))
+ })
// eslint-disable-next-line no-console
app.listen(3000, () => console.log('Example app listening on port 3000!'))
感觉最近很多框架都准备上 Hooks 了,别人坑也踩的差不多了,是时候看起来了
看了一圈,原先很多 render props 和高阶组件的地方可以被替换了。
感觉用 Hooks 比较合适的例子:
关键函数:
通过上一篇 《如何开发一个 Node.js CLI 程序》#28,我们已经知道怎么开发一些简单的命令行程序了。这篇文章主要介绍一些常用的库,来辅助我们开发程序。
TJ 开发的,用来创建命令行工具的程序,提供了如命令行解析以及自动生成帮助等等功能。
可以看下面的小例子找找感觉~
var program = require('commander');
program
.version('0.1.0')
.option('-p, --peppers', 'Add peppers')
.option('-P, --pineapple', 'Add pineapple')
.option('-b, --bbq-sauce', 'Add bbq sauce')
.option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
.parse(process.argv);
console.log('you ordered a pizza with:');
if (program.peppers) console.log(' - peppers');
if (program.pineapple) console.log(' - pineapple');
if (program.bbqSauce) console.log(' - bbq');
console.log(' - %s cheese', program.cheese);
很流行的 web server 框架,几行代码就能拉一个服务起来。
学习下路由,就能创造 mock 服务了。
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
用来复制粘贴代码块的工具,主要功能有:
不过需要先在本地 npm install -g
一下才能使用模板,比较麻烦。
给新手指条道路,顺便整理下知识点。
除了 React,还需要搭配 周边库 才能编写完整的应用(也就是俗称的全家桶)
感觉把这本 《You Don't Know JS: Types & Grammar》 看完就好了。
比较神奇的是基本类型里没有函数,函数也是对象,只不过这个对象有 [[call]]
或者 [[constructor]]
属性。
你去看流行的库里面基本上都有对参数对象类型的判断。这个可以参考 lodash 的代码。
Type | Result |
---|---|
Undefined | "undefined" |
Null | "object" |
Boolean | "boolean" |
Number | "number" |
String | "string" |
Symbol (new in ECMAScript 2015) | "symbol" |
Function object (implements [[Call]] in ECMA-262 terms) | "function" |
Any other object | "object" |
标准中定义的一些“抽象操作”(即“内部使用的操作”)。
其他的基本类型转换为字符串没什么特别的地方。
对普通对象来说,除了自行定义,否则 Object.prototype.toString() 返回内部属性 [[class]]
的值,如 [object Object]
。
如果对象有自己的 toString 方法,字符串化时就会调用该方法并使用其返回值。
数组的 toString 方法:调用元素的 toString 然后拼在一起,中间加逗号。
对象会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型,则再遵循以上规则将其强制转换为数字。
为了将值转换为相应的基本类型值,抽象操作 ToPrimitive 会首先检查该值是否有 valueOf 方法。如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString 的返回值并进行强制类型转换。
var a = {
valueOf: function() {
return "42";
}
};
var b = {
toString: function() {
return "42";
}
};
var c = [4, 2];
c.toString = function() {
return this.join(""); // "42"
};
Number( a ); // 42
Number( b ); // 42
Number( c ); // 42
Number( "" ); // 0
Number( [] ); // 0
Number( ["abc"] ); // NaN
...
使用 String(...)
与 Number(...)
进行转换,注意不用 new
,不会进行装箱。
var a = 42;
var b = String( a );
var c = "3.14";
var d = Number( c );
b; // "42"
d; // 3.14
除了上面的方法,还有其他转化方式
var a = 42;
var b = a.toString(); // 这个算隐式转换
var c = "3.14";
var d = +c; // 这里的+是一元操作符
parseFloat 与 Number 的区别在于前者接受后面有非字符串的情况
var a = "42";
var b = "42px";
Number(a); // 42
parseInt(a); // 42
Number(b); // NaN
parseInt(b); // 42
还有 JavaScript 中的数字有十进制、二进制、八进制和十六进制;还有科学计数法。
parseInt 不支持下面所有的数字解析,推荐 Number。
30
0b111
0o13
0xFF
1e3
-1e-2
+
号:
根据 ES5 规范 11.6.1 节,如果某个操作数是字符串或者能够通过以下步骤转换为字符串的话, + 将进行拼接操作。如果其中一个操作数是对象(包括数组),则首先对其调用 ToPrimitive 抽象操作,该抽象操作在调用 [[DefaultValue]],以数字作为上下文。
今天碰到一个需要将 JS 字符串转换为 GBK 的 Base64 形式的需求,特别记录下。
JS 内部的字符串是存储为 UTF-16 形式的 (根据实现有所区别),所以要转化为 GBK 没有直接支持,只能通过 iconv-lite
这样的包处理。
import iconv from 'iconv-lite';
// Convert from js string to an encoded buffer.
buf = iconv.encode("Sample input string", 'gbk');
处理后是一个 Uint8Array 对象,每个里面存一个字节
要将上面的字节数组转化为 base64 编码,可以求助于 btoa
方法,这个方法能把字符串形转换为 base64 形式编码
const text = String.fromCharCode.apply(null, buf);
const base64 = btoa(text);
有时候需要从 JS 源代码中提取一些信息,或者将源代码进行一定程度上的变换。这时就可以通过某种 parser 来将源代码转化为 AST(抽象语法树)形式,然后进行后续操作。
下面给出一个 babel.js 的用例,将下面代码转换为:
function max(a, b){
if(a > b){
return a;
}else{
return b;
}
}
// =>
function max(a, b) {
return a > b ? a : b;
}
将源代码转化为 AST 的过程称为 parse,使用代码如下
const babylon = require('babylon');
const sampleCode = `
function max(a, b){
if(a > b){
return a;
}else{
return b;
}
}`;
// code => ast
const ast = babylon.parse(sampleCode);
对于 ast 具体的样子,除了打印以外,还可以借助如 astexplorer 这样的工具。
将源代码变换的过程称为 transform 。
使用 babel-traverse 来遍历和修改 AST ,babel-traverse 使用了 visitor设计模式 来遍历 AST。
因为 AST 是一棵树,深度优先遍历的时候会有一个进入和回来的过程,所以 visitor 里面有 enter
和 exit
。 举个最简单的 visitor 的例子:
const MyVisitor = {
Identifier: {
enter(path) {
console.log("Entered!");
},
exit(path) {
console.log("Exited!");
}
}
};
这个例子表示碰到遍历的时候,需要采取对应的处理。
不一定每次都要写 enter、exit,可以用如下形式,表示 enter 。
const MyVisitor = {
Identifier(path) {
console.log("Entered!");
}
};
这里的 path 包含了一些结点和其他信息
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
下面我们来看看代码(请结合 astexplorer)
const traverse = require("babel-traverse").default;
const t = require("babel-types");
const myVisitor = {
IfStatement(path) {
let { consequent, alternate, test } = path.node;
if(consequent && alternate){
var consequentExp = consequent.body[0],
alternateExp = alternate.body[0];
if(t.isReturnStatement(consequentExp)
&& t.isReturnStatement(alternateExp)){
var node = t.returnStatement(
t.conditionalExpression(
test,
consequentExp.argument,
alternateExp.argument
)
);
path.replaceWith(node);
}
}
}
};
traverse(ast, myVisitor);
babel-types 提供了许多简便的函数来创建和判断结点。
最后从 AST 转换回源代码
var generate = require("babel-generator").default;
console.log(generate(ast, {
retainLines: false,
}).code);
var babel = require("babel-core");
var code = "...";
var result = babel.transform(code, {
plugins: [{
visitor: myVisitor
}]
});
console.log(result.code);
来自 React 库的工具,也是最基础的工具。
这个库强调按终端用户的视角去测试、可维护性 。
相比 Enzyme ,不会去查看内部的 props,state,而是直接查看dom。
搭个开发框架熟悉下React,想配个路由可没那么轻松。
你知道下面问题的答案吗?
<Link/>
与 <a/>
的区别 ?最最最普通的路由配置,手工编码路由地址。
// index.js
ReactDOM.render(
<BrowserRouter>
<AppRouter />
</BrowserRouter>,
document.getElementById('root')
);
// router.js
const AppRouter = () => (
<Switch>
<Route path="/" exact component={Foo} />
<Route path="/foo" component={Foo} />
<Route path="/bar" component={Bar} />
</Switch>
);
export default AppRouter;
使用 react-loadable
包装返回类
export default Loadable({
loader: () => import('./Foo'),
loading: <div>loading...</div>
});
找些简单的例子看看 Hybrid 开发效果如何
1 支付宝移动端 Hybrid 解决方案探索与实践
支付宝:历史栈,手势,离线包
2 淘宝app属于hybrid app吗?
淘宝:历史栈,手势
3 口碑
历史栈,手势,下层导航
4 饿了么
历史栈,手势
多用些基础库,少造轮子
warning
Similar to Facebook's (FB) invariant but only logs a warning if the condition is not met. This can be used to log issues in development environments in critical paths.
invariant
如名字所示,在关键地方提供个条件,来让
invariant(condition, '抛出异常如果条件错误');
debug
可以配合环境变量 DEBUG
来开启部分输出
var debug = require('debug')('http')
debug('booting %o', name);
没事找个课程学习下 The Benefits of Offline First
还挺受益的
注册 ServiceWorker,注意默认路径能管的范围是 /path/
。
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/path/sw.js').then(...).catch(...)
}
当修改 ServiceWorker 后,F5 后新更新的不会生效,必须等老的结束后才行。
可通过 DevTools 观察 Waiting 状态
一种刷新方法是转到其他页面,再转回来;另一种是按 Shift + 刷新 ;
最暴力的劫持请求,参考学习下 Response 对象。
self.addEventListener('fetch', function(event) {
event.respondWith(new Response('<div class="a-winner-is-me">666</div>', {
headers: {
'Content-Type': 'text/html;charset=utf-8'
}
}));
});
FetchEvent.respondWith 同时也接受 fetch 对象
主要 API
缓存时机 install
self.addEventListener('install', function (event) {
event.waitUntil(
caches.open('wittr-static-v1').then(function (cache) {
return cache.addAll([
'/',
'js/main.js',
'css/main.css',
]);
})
);
});
使用时机 fetch
self.addEventListener('fetch', function(event) {
// respond with an entry from the cache if there is one.
// If there isn't, fetch from the network.
event.respondWith(
caches.open('wittr-static-v1').then(function (cache) {
return cache.match(event.request.url);
}).then(response => {
return response ? response : fetch(event.request);
})
);
});
清理旧缓存
在获取不到新的SW时,旧的SW能够使用缓存;而当新的SW能运行时,干掉旧的缓存。
这个时机为 activate
文章资源收集
Enzyme is a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components' output.
Enzyme's API is meant to be intuitive and flexible by mimicking jQuery's API for DOM manipulation and traversal.
Enzyme 本身安装
npm i --save-dev enzyme enzyme-adapter-react-16
另外还需要一个 Test runner, 这里以 Jest 为例:
首先安装依赖:
npm i --save-dev jest babel-jest babel-preset-env babel-preset-react
# 需要配置 .babelrc
然后需要配置 adapter。在 package.json 里加入
{
"jest": {
"setupTestFrameworkScriptFile": "<rootDir>src/setupTests.js"
}
}
然后在 setupTests.js 文件里输入
// setup file
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
假设要测试一个非常傻瓜的组件,组件的功能是每次按下一个按钮,计数器加一。
// One.js
import React from 'react';
export default class One extends React.Component {
state = {
count: 0
};
add = () => {
this.setState({count: this.state.count + 1});
};
render() {
return (
<button onClick={this.add}>Click me</button>
);
}
}
那么相应的测试代码如下
import React from 'react';
import One from './One';
import {shallow} from 'enzyme';
describe('test One', () => {
it('should add 1', () => {
// shallow: 只会创建第一层的组件
const wrapper = shallow(<One />);
// like jQuery selector
wrapper.find('button').at(0).simulate('click');
expect(wrapper.state('count')).toEqual(1);
});
});
通过 .prop()
不仅能读取属性,还能调用函数
import React from 'react';
export default class Two extends React.Component {
state = {
text: ''
};
change = e => {
this.setState({ text: e.target.value || '' })
};
render() {
return (
<input onChange={this.change} />
);
}
}
那么我们怎么测试呢 ?
import React from 'react';
import Two from './Two';
import {shallow} from 'enzyme';
describe('test Two', () => {
it('should update text', () => {
// shallow: 只会创建第一层的组件
const wrapper = shallow(<Two />);
// like jQuery selector
wrapper.find('input').at(0).prop('onChange')({target: {value: 'hello'}});
expect(wrapper.state('text')).toEqual('hello');
});
});
本文介绍如何使用 Chrome 的开发者工具(F12)来定位内存泄漏的问题。读本文前请确保有基础的 HTML、JavaScript 和垃圾回收的知识。
如下图所示,开发者工具里的 【Memory】选项卡里,有三个选项:
1)用来分析某个瞬间的内存
2)用来分析某段时间轴上的内存分配
一般用第二个来分析内存泄漏。使用方法非常简单,点击开始,然后果断时间按结束
比如说误用了this,而存储到了 window 上
function hello() {
this.obj = [];
for (var i = 0; i < 1000000; i++) {
this.obj[i] = i;
}
}
hello();
函数内引用了一个对象,而且这个函数一直在被使用
(function() {
var counter = new Array(4008123);
var i = 0
setInterval(() => {
i += 1
counter[i] = i;
}, 1000);
})()
方法同行,然后在多个地方都能观察到结果,甚至能定位到具体那行代码.
JS代码里引用了 DOM 对象,然后删除 document 里的 DOM 后没有及时清除 JS 里的引用造成的内存泄漏。
var doms = []
for (var i = 0; i < 10000; i++) {
var p = document.createElement('p')
p.textContent = 'hello ' + i;
doms.push(p)
document.body.appendChild(p)
document.body.removeChild(p)
}
还有就是 DOM 对象上的监听函数使用不当。比如有一万个 <button />
,然后每个上面都有不同的click事件,这留给大家作为课堂作业
。
我要把能看到的所有相关知识都汇总起来。
[[prototype]]
探索下热模块更新(以下简称 HMR )原理,主要偏重开发者所能控制的部分,原理部分尽量减小。
想想看以前每次改变代码以后,需要手动刷新页面查看效果,非常低效。后来出现的 live reload 改变了一点,但是每次刷新原来页面的状态就全部丢失了。
HMR 是一种在运行时动态改变模块代码的技术,可以理解为模块范围内的 live reload,这样的好处时可以保留页面的状态。
那怎么写热更新部分的代码呢?
参照下一节
每次都要这么写也太折腾人了,而且不能指望所有用户都对 HMR 有了解。
dva.js
就用了一个 babel 插件 来自动替我们处理这些事情。(很有局限性)
本文不是来批判 this 的,只是用来帮助理解 this 的。多数内容都是从 《你不知道的JavaScript》 中学到的。结论请直接跳到最后一章。
JavaScript 中的 this 是动态作用域(dynamic scope)的,而不是词法作用域(lexical scope)的。它们两个分别是什么意思呢?举个例子:
var a = 1; // 位置一
function foo() {
console.log(a);
}
function bar() {
var a = 2; // 位置二
foo();
}
上面代码中的 foo 函数里有个变量 a ,当我们在查找 a 变量值的时候,会从这个函数定义的地方附近
寻找,这里就是位置一, 这就是我们常说的词法作用域。而像 bar 函数里,调用 foo 函数,很明显结果还是输出 1。但如果此时是动态作用域,那么此时就会输出 2,也就是说从函数调用的地方附近
寻找,这里就是位置二。
所以正因为 this 是动态作用域的,所以它绑定的对象是在调用或运行时决定的。
那么在调用的时候由什么决定 this 的值呢 ? 参考下 ES5 的定义:
11.1.1 The this Keyword # Ⓣ Ⓡ Ⓖ
The this keyword evaluates to the value of the ThisBinding of the current execution context.
10.4.1.1 Initial Global Execution Context # Ⓣ Ⓐ
The following steps are performed to initialize a global execution context for ECMAScript code C:
Set the VariableEnvironment to the Global Environment.
Set the LexicalEnvironment to the Global Environment.
Set the ThisBinding to the global object.
10.4.3 Entering Function Code # Ⓣ
The following steps are performed when control enters the execution context for function code contained in function object F, a caller provided thisArg, and a caller provided argumentsList:
If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the global object.
Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).
Else set the ThisBinding to thisArg.
...
11.2.3 Function Calls # Ⓣ
The production CallExpression : MemberExpression Arguments is evaluated as follows:
Let ref be the result of evaluating MemberExpression.
Let func be GetValue(ref).
Let argList be the result of evaluating Arguments, producing an internal list of argument values (see 11.2.4).
If Type(func) is not Object, throw a TypeError exception.
If IsCallable(func) is false, throw a TypeError exception.
If Type(ref) is Reference, then
If IsPropertyReference(ref) is true, then
Let thisValue be GetBase(ref).
Else, the base of ref is an Environment Record
Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).
Else, Type(ref) is not Reference.
Let thisValue be undefined.
13.2.2 [[Construct]] # Ⓣ
When the [[Construct]] internal method for a Function object F is called with a possibly empty list of arguments, the following steps are taken:
Let obj be a newly created native ECMAScript object.
Set all the internal methods of obj as specified in 8.12.
Set the [[Class]] internal property of obj to "Object".
Set the [[Extensible]] internal property of obj to true.
Let proto be the value of calling the [[Get]] internal property of F with argument "prototype".
If Type(proto) is Object, set the [[Prototype]] internal property of obj to proto.
If Type(proto) is not Object, set the [[Prototype]] internal property of obj to the standard built-in Object prototype object as described in 15.2.4.
Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.
If Type(result) is Object then return result.
Return obj.
我知道你们也不会看的,直接看如下几种分类:
具体如何绑定,参考结论。
如果要判定一个运行中函数的 this 绑定,就需要找到这个函数的直接调用位置。找到之后就可以顺序应用下面四条规则来判断 this 的绑定对象。
快速搭建个JS开发环境,包括 ESLint、webpack等等
使用 airbnb 的包
npx install-peerdeps --dev eslint-config-airbnb-base
然后添加.eslintrc
到目录下:
{
"extends": "airbnb-base",
"rules": {
"prefer-arrow-callback": 0,
"func-names": 0
}
}
接上篇 #54 ,入门好以后写一些普通页面没什么问题了。但是要写出可复用性高、易于维护的组件需要多多练习,除了看代码、写代码外没有什么别的捷径。
我会按难易度从小到大列出一些源代码:
自己去玩玩看看
介绍下几种方式的区别
一般 Element
都有这个属性,他是一个 NamedNodeMap
类型,结构类似于
var dom = document.getElementById('111');
console.log(dom.attributes);
// NamedNodeMap {0: id, 1: ok, id: id, ok: ok, length: 2}
// 通过以下方式遍历它
for (var i = 0; i < dom.attributes.length; i++) {
console.log(dom.attributes[i].name + dom.attributes[i].value);
}
通过 setAttribute 属性来设置的值,会出现在 attributes 中
var shit = document.getElementById('shit');
shit.setAttribute('value', '10');
shit.setAttribute('WIDTH', 100);
shit.setAttribute('NAME-WANG', false);
console.log(shit.attributes);
// NamedNodeMap {0: id, 1: value, 2: width, 3: name-wang, id: id, value: value, width: width, name-wang: name-wang, length: 4}
权威指南上说这两个方法用于设置非标准的属性。(当然标准的也能访问,那么相较而言全用它不就好了么...)例如:
var image = document.images[0];
var width = parseInt(image.getAttribute('WIDTH'));
image.setAttribute('class', 'thumbnail');
和第三种方式来访问属性的区别:
HTMLElement 和其子类型定义了一些属性,他们对应于元素的标准 HTML 属性。
直接修改值会出现在标签里,比如:
var shit = document.getElementById('shit');
shit.max = 20;
shit.readOnly = true;
shit._str = {hello: 2000} // 值可以是对象
这种方式和其他的区别就是:
由标准所定义的,在执行以前创建的对象实例。
使用内置的构造函数(实现了[[constructor]]
方法)创建的对象。常见的构造函数如:
比较好玩的是 Function ,咱们可以用 new Function 来创建函数对象 。
在面试的时候有没有碰到过如下这类问题:
其实这几个问题跟浏览器的渲染有关
回流/重排 (Reflow): 在网络浏览器中执行的一个流程,用于重新计算文档中各元素的位置和几何形状,以便重新呈现该文档的部分内容或全部内容
很多种用户操作和可能的 DHTML 更改都可触发重排。例如:调整浏览器窗口的大小、使用涉及计算出的样式的 JavaScript 方法、在 DOM 中添加或移除元素,以及更改某个元素的类,等等。另外值得注意的是,一些操作可能会导致重排用时比您想象的要长
F12 > Performance > record > stop
首先我们用 marginLeft 来让元素居中
<!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>
<style>
#parent { width:400px;height:400px;border:1px solid greenyellow; }
#gaga { width: 200px; height: 200px; background: coral; }
</style>
</head>
<body>
<div id="parent">
<div id="gaga">Gaga</div>
</div>
<script>
setTimeout(() => {
var gaga = document.getElementById('gaga');
gaga.style.marginLeft = '100px';
}, 1500);
</script>
</body>
</html>
可以发现其中几个紫色和绿色的过程: Recalculate Style, Layout, Update Layer Tree, Paint, Composite Layer。其中 Layout 就是回流的计算。
然后我们把 marginLeft 替换成 transform
<style>
#gaga { transform: translateX(0); ... }
</style>
<script>
gaga.style.transform = 'translateX(100px)';
</script>
这个库好难懂,各种各样的名词。
我发现别人几篇文章写的特别好...
Saga
:源自 saga pattern , 在这里可以用 generator function 替换。effect
:An Effect is simply an object that contains some information to be interpreted by the middleware. You can view Effects like instructions to the middleware to perform some operation首先要懂什么是 generator function
。写作 function *
,调用会返回一个 iterator
;
iterator
有一个 next 方法,调用会返回 {value, done}
这样的东西。
function* generator(i) {
yield i;
yield i + 10;
}
var gen = generator(10) // 调用返回个 iterator
gen.next().value // 调用 iterator.next,返回第一个yield右边的10
gen.next().value // 再次 iterator.next,返回第二个yield右边的 i + 10
yield 后面跟的东西怎么处理,完全取决于环境。
saga 的 effect 可以分为两类:watcher 与 worker。
注意每个 effect
都要在前面加个 yield
:因为 effect 只是一个纯粹的 JavaScript 对象,它需要被中间件解释才有意义。
take
:监听某个动作
function *saga() {
// take 监听某个动作
// 后面接的方法可以是 generator func 或者 返回 promise 的函数
yield take('FETCH_USER', fetchUser);
}
takeEvery
:重复监听某个动作,就相当于
// 因为返回 fork 所以 yield takeEvery 不会阻塞
const takeEvery = (patternOrChannel, saga, ...args) => fork(function*() {
while (true) {
const action = yield take(patternOrChannel)
yield fork(saga, ...args.concat(action))
}
})
call
:调用某个方法
function *saga() {
// 方法可以是 generator func 或者 返回 promise 的函数
// 会阻塞
const response = yield call(fetchUser);
}
fork
:和 call 的区别是前者为非阻塞的。
// 不会阻塞,返回值为task,用于取消任务
const task = yield fork(fetchApi);
yield take('CANCEL_FETCH');
yield cancel(task);
put
:发出一个 action
yield put({ type: 'hello' })
select
:获取 state
const state = yield select(state => state.p1)
学习 d3.js 其实也是对数据可视化的入门,这个库里一些设计理念可以学习下。
不要手动硬编码图的大小,而是通过比例尺来换算。
除了线性比例尺,还有指数、对数、量子比例尺等。
可以理解为将数值转换为图形位置的函数。常用的布局有弧度布局、力图布局、树形布局,可以做一个桑吉图布局练习以下。
假设我们要渲染一个柱状图,输入是一组数组, [{name: 1, value: 100}, {name: 2, value: 200}, ...],
然后我们通过布局函数得到一组渲染用的数据。
export default function () {
let values = [];
function histo() {
const result = [];
let sumX = 0;
values.forEach((value) => {
result.push({
x: sumX,
y: value,
});
sumX += 50;
});
return result;
}
histo.values = function (_) {
if (arguments.length) {
values = _;
return histo;
}
return values;
};
return histo;
}
使用方法
import histo from './layout.js';
var dataset = [{name: 1, value: 100}, {name: 2, value: 200}, ...];
var layout = histo().values(dataset);
var out = layout.histo();
不要把布局计算和渲染图的逻辑搅合在一起。
d3从第四版开始后就把代码模块化了。目前市面上的书籍和网上的教程有很多都是v3的代码,迁移过来需要一定的修改。具体改动参考文档 changelog
下面介绍下常用的几个模块
提供了常用的如 d3.min
, d3.max
, d3.sum
, d3.range
等数组操作
提供了一些用于画图的工厂方法/
select , selectAll 等操作
各种比例尺
提供了各种layout:line, area, arc, pie, stack...
在理解了一些基本理念后可以直接看例子学习。
我指的 cli 程序定义为:能通过 npm install -g
安装,然后就像 shell 命令一样执行特定任务的程序。
1 创建入口文件,第一行 shebang 不能省略
#!/usr/bin/env node
const [,, ...args] = process.argv;
console.log(`hello world gaga ${args}`);
2 在 package.json
里面添加
"bin": "./cli.js"
3 在本地调试的时候运行 npm link
,会在某个文件里创建一个软连接。 (结束后记得 npm unlink)
最近尝试把桑基图迁移到 G2 ,主要有以下这些好处:
大概了解了下绘图需要涉及哪些接口:
DataSet.View()
和 Transform
,用来计算 node 和 edge 的位置信息; G2自带了一个 diagram.sankey
的 Transform 算法,如果不合适可以自己手写chart.view()
接口,一张图表可以由多个 view 拼接起来; 比如桑基图就是由 NodeView 和 EdgeView 组合而成; 所以如果要在 Node 上再显示一些文字的话,只要继续添加 View 即可。源代码来自 Chrome Samples
。
class Polygon {
constructor(height, width) {
this.name = 'Polygon';
this.height = height;
this.width = width;
}
sayName() {
ChromeSamples.log('Hi, I am a ', this.name + '.');
}
sayHistory() {
ChromeSamples.log('"Polygon" is derived from the Greek polus (many) ' +
'and gonia (angle).');
}
static triple(n) {
n = n || 1;
return n * 3;
}
}
上面的代码会被转译成:
var Polygon = (function() {
function Polygon(height, width) {
_classCallCheck(this, Polygon);
this.name = "Polygon";
this.height = height;
this.width = width;
}
_createClass(
Polygon,
[{
key: "sayName",
value: function sayName() {
ChromeSamples.log("Hi, I am a ", this.name + ".");
}
}, {
key: "sayHistory",
value: function sayHistory() {
ChromeSamples.log(
'"Polygon" is derived from the Greek polus (many) ' +
"and gonia (angle)."
);
}
}],
[{
key: "triple",
value: function triple(n) {
n = n || 1;
return n * 3;
}
}]
);
return Polygon;
})();
感觉代码写的太“碎”,到处都是散落的 function ...
这周决定重读下《重构》,做一些笔记。
这玩意就存储量大点咯?
类似于 Table 。
var dbPromise = idb.open('test-db3', 1, function(upgradeDb) {
if (!upgradeDb.objectStoreNames.contains('people')) {
// define primary key
upgradeDb.createObjectStore('people', {keyPath: 'email'});
}
if (!upgradeDb.objectStoreNames.contains('notes')) {
// define primary key, auto increment
upgradeDb.createObjectStore('notes', {autoIncrement: true});
}
if (!upgradeDb.objectStoreNames.contains('logs')) {
// define primary key id, auto increment
upgradeDb.createObjectStore('logs', {keyPath: 'id', autoIncrement: true});
}
});
索引......
// objectStore.createIndex('indexName', 'property', options)
var dbPromise = idb.open('test-db4', 1, function(upgradeDb) {
if (!upgradeDb.objectStoreNames.contains('people')) {
var peopleOS = upgradeDb.createObjectStore('people', {keyPath: 'email'});
peopleOS.createIndex('gender', 'gender', {unique: false});
peopleOS.createIndex('ssn', 'ssn', {unique: true});
}
if (!upgradeDb.objectStoreNames.contains('notes')) {
var notesOS = upgradeDb.createObjectStore('notes', {autoIncrement: true});
notesOS.createIndex('title', 'title', {unique: false});
}
if (!upgradeDb.objectStoreNames.contains('logs')) {
var logsOS = upgradeDb.createObjectStore('logs', {keyPath: 'id',
autoIncrement: true});
}
});
谈不上源码解读,仅仅说说学到的东西
roadhog 依赖于 webpack-dev-server
,然后 webpack-dev-server
底层使用了 express
。
解析配置文件,逐条添加路由规则。
// .roadhogrc.mock.js
export default {
// Support type as Object and Array
'GET /api/users': { users: [1,2] },
// Method like GET or POST can be omitted
'/api/users/1': { id: 1 },
// Support for custom functions, the API is the same as express@4
'POST /api/users/create': (req, res) => { res.end('OK'); },
};
如上面这个文件,路由就会被转化为如下代码(略作简化):
const config = getConfig();
const mockRules = [];
Object.keys(config).forEach(key => {
const keyParsed = parseKey(key);
mockRules.push({
path: keyParsed.path,
method: keyParsed.method,
target: config[key],
});
});
mockRules.forEach(mock => {
app[mock.method](
mock.path,
createMockHandler(mock.method, mock.path, mock.target)
)
});
其他的也大致同理。
这里有个细节, require
同一个文件会有缓存。可以操作 require.cache
来解决
function getConfig() {
if (existsSync(configFile)) {
// disable cache
Object.keys(require.cache).forEach(file => {
if (file === configFile) {
debug(`delete cache ${file}`);
delete require.cache[file];
}
});
return require(configFile);
} else {
return {};
}
}
文件监控,一旦发生变化就重新加载路由,这里涉及到动态修改 express 的路由。
可以用 app._router.stack
来操作路由对象
const watcher = chokidar.watch(configFile, {
ignored: /node_modules/,
persistent: true
});
watcher.on('change', path => {
console.log(chalk.green('CHANGED'), path);
watcher.close();
app._router.stack.splice(lastIndex + 1, mockRules.length + 2);
applyMock(app);
});
让 Node 代码支持 ES6 语法。
const files = [
'app.js',
'mock.js',
'route.config.js'
];
require('@babel/register')({
only: [new RegExp(files.join('|'))],
ignore: [/node_modules/]
});
require('./app.js');
另外 .babelrc
需要配好
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.