gzl的博客

  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

JavaScript-Style-Guide

发表于 2019-10-29 更新于 2019-11-04 分类于 JavaScript

JavaScript风格指南摘抄(自己尚不熟悉的)

https://github.com/airbnb/javascript

https://github.com/lin-123/javascript

Objects

one place

Use computed property names when creating objects with dynamic property names.

Why? They allow you to define all the properties of an object in one place.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getKey(k) {
return `a key named ${k}`;
}

// bad
const obj = {
id: 5,
name: 'San Francisco',
};
obj[getKey('enabled')] = true;

// good
const obj = {
id: 5,
name: 'San Francisco',
[getKey('enabled')]: true,
};

group

Group your shorthand properties at the beginning of your object declaration.

Why? It’s easier to tell which properties are using the shorthand.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
episodeOne: 1,
twoJediWalkIntoACantina: 2,
lukeSkywalker,
episodeThree: 3,
mayTheFourth: 4,
anakinSkywalker,
};

// good
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1,
twoJediWalkIntoACantina: 2,
episodeThree: 3,
mayTheFourth: 4,
};

quote-props

Only quote properties that are invalid identifiers. eslint: quote-props

Why? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines.

1
2
3
4
5
6
7
8
9
10
11
12
13
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};

// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};

no-prototype-builtins

Do not call Object.prototype methods directly, such as hasOwnProperty, propertyIsEnumerable, and isPrototypeOf. eslint: no-prototype-builtins

Why? These methods may be shadowed by properties on the object in question - consider { hasOwnProperty: false } - or, the object may be a null object (Object.create(null)).

1
2
3
4
5
6
7
8
9
10
11
12
// bad
console.log(object.hasOwnProperty(key));

// good
console.log(Object.prototype.hasOwnProperty.call(object, key));

// best
const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
console.log(has.call(object, key));
/* or */
import has from 'has'; // https://www.npmjs.com/package/has
console.log(has(object, key));

shallow-copy

Prefer the object spread operator over Object.assign to shallow-copy objects. Use the object rest operator to get a new object with certain properties omitted.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
delete copy.a; // so does this

// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }

// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }

const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

Arrays

copy arrays

Use array spreads ... to copy arrays.

1
2
3
4
5
6
7
8
9
10
11
// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
}

// good
const itemsCopy = [...items];

convert an iterable object

To convert an iterable object to an array, use spreads ... instead of Array.from.

1
2
3
4
5
6
7
const foo = document.querySelectorAll('.foo');

// good
const nodes = Array.from(foo);

// best
const nodes = [...foo];

convert an array-like object

Use Array.from for converting an array-like object to an array.

1
2
3
4
5
6
7
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };

// bad
const arr = Array.prototype.slice.call(arrLike);

// good
const arr = Array.from(arrLike);

mapping over iterables

Use Array.from instead of spread ... for mapping over iterables, because it avoids creating an intermediate array.

1
2
3
4
5
// bad
const baz = [...foo].map(bar);

// good
const baz = Array.from(foo, bar);

array-callback-return

Use return statements in array method callbacks. It’s ok to omit the return if the function body consists of a single statement returning an expression without side effects, following 8.2. eslint: array-callback-return

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});

// good
[1, 2, 3].map((x) => x + 1);

// bad - no returned value means `acc` becomes undefined after the first iteration
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
});

// good
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
return flatten;
});

// bad
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
} else {
return false;
}
});

// good
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
}

return false;
});

Destructuring

object destructuring

Use object destructuring when accessing and using multiple properties of an object. eslint: prefer-destructuring

Why? Destructuring saves you from creating temporary references for those properties.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;

return `${firstName} ${lastName}`;
}

// good
function getFullName(user) {
const { firstName, lastName } = user;
return `${firstName} ${lastName}`;
}

// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}

array destructuring

Use array destructuring. eslint: prefer-destructuring

1
2
3
4
5
6
7
8
const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;

multiple return values

Use object destructuring for multiple return values, not array destructuring.

Why? You can add new properties over time or change the order of things without breaking call sites.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// bad
function processInput(input) {
// then a miracle occurs
return [left, right, top, bottom];
}

// the caller needs to think about the order of return data
const [left, __, top] = processInput(input);

// good
function processInput(input) {
// then a miracle occurs
return { left, right, top, bottom };
}

// the caller selects only the data they need
const { left, top } = processInput(input);

Function

statements

ECMA-262 defines a block as a list of statements. A function declaration is not a statement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}

// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}

arguments

Never use arguments, opt to use rest syntax ... instead. eslint: prefer-rest-params

Why? ... is explicit about which arguments you want pulled. Plus, rest arguments are a real Array, and not merely Array-like like arguments.

1
2
3
4
5
6
7
8
9
10
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}

// good
function concatenateAll(...args) {
return args.join('');
}

default parameter syntax

Use default parameter syntax rather than mutating function arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// really bad
function handleThings(opts) {
// No! We shouldn’t mutate function arguments.
// Double bad: if opts is falsy it'll be set to an object which may
// be what you want but it can introduce subtle bugs.
opts = opts || {};
// ...
}

// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}

// good
function handleThings(opts = {}) {
// ...
}

default parameters last

Always put default parameters last.

1
2
3
4
5
6
7
8
9
// bad
function handleThings(opts = {}, name) {
// ...
}

// good
function handleThings(name, opts = {}) {
// ...
}

Never mutate parameters

Never mutate parameters. eslint: no-param-reassign

Why? Manipulating objects passed in as parameters can cause unwanted variable side effects in the original caller.

1
2
3
4
5
6
7
8
9
// bad
function f1(obj) {
obj.key = 1;
}

// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}

Never reassign parameters

Never reassign parameters. eslint: no-param-reassign

Why? Reassigning parameters can lead to unexpected behavior, especially when accessing the arguments object. It can also cause optimization issues, especially in V8.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// bad
function f1(a) {
a = 1;
// ...
}

function f2(a) {
if (!a) { a = 1; }
// ...
}

// good
function f3(a) {
const b = a || 1;
// ...
}

function f4(a = 1) {
// ...
}

Arrow Function

wrap it in parentheses

In case the expression spans over multiple lines, wrap it in parentheses for better readability.

Why? It shows clearly where the function starts and ends.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// bad
['get', 'post', 'put'].map((httpMethod) => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
);

// good
['get', 'post', 'put'].map((httpMethod) => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));

Classes & Constructors

Always use class

Always use class. Avoid manipulating prototype directly.

Why? class syntax is more concise and easier to reason about.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// bad
function Queue(contents = []) {
this.queue = [...contents];
}
Queue.prototype.pop = function () {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
};

// good
class Queue {
constructor(contents = []) {
this.queue = [...contents];
}
pop() {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
}
}

Use extends for inheritance

Use extends for inheritance.

Why? It is a built-in way to inherit prototype functionality without breaking instanceof.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
return this.queue[0];
};

// good
class PeekableQueue extends Queue {
peek() {
return this.queue[0];
}
}

method chaining

Methods can return this to help with method chaining.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// bad
Jedi.prototype.jump = function () {
this.jumping = true;
return true;
};

Jedi.prototype.setHeight = function (height) {
this.height = height;
};

const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined

// good
class Jedi {
jump() {
this.jumping = true;
return this;
}

setHeight(height) {
this.height = height;
return this;
}
}

const luke = new Jedi();

luke.jump()
.setHeight(20);

no-useless-constructor

Classes have a default constructor if one is not specified. An empty constructor function or one that just delegates to a parent class is unnecessary. eslint: no-useless-constructor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// bad
class Jedi {
constructor() {}

getName() {
return this.name;
}
}

// bad
class Rey extends Jedi {
constructor(...args) {
super(...args);
}
}

// good
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}

Modules

use modules (import/export)

Always use modules (import/export) over a non-standard module system. You can always transpile to your preferred module system.

Why? Modules are the future, let’s start using the future now.

1
2
3
4
5
6
7
8
9
10
11
// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;

// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;

// best
import { es6 } from './AirbnbStyleGuide';
export default es6;

not use wildcard imports

Do not use wildcard imports.

Why? This makes sure you have a single default export.

1
2
3
4
5
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';

// good
import AirbnbStyleGuide from './AirbnbStyleGuide';

do not directly

And do not export directly from an import.

Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent.

1
2
3
4
5
6
7
8
// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide';

// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;

import/no-mutable-exports

Do not export mutable bindings. eslint: import/no-mutable-exports

Why? Mutation should be avoided in general, but in particular when exporting mutable bindings. While this technique may be needed for some special cases, in general, only constant references should be exported.

1
2
3
4
5
6
7
// bad
let foo = 3;
export { foo };

// good
const foo = 3;
export { foo };

import/prefer-default-export

In modules with a single export, prefer default export over named export. eslint: import/prefer-default-export

Why? To encourage more files that only ever export one thing, which is better for readability and maintainability.

1
2
3
4
5
// bad
export function foo() {}

// good
export default function foo() {}

Iterators and Generators

Prefer higher-order functions

Don’t use iterators. Prefer JavaScript’s higher-order functions instead of loops like for-in or for-of. eslint: no-iterator no-restricted-syntax

Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side effects.

Use map() / every() / filter() / find() / findIndex() / reduce() / some() / … to iterate over arrays, and Object.keys() / Object.values() / Object.entries() to produce arrays so you can iterate over objects.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const numbers = [1, 2, 3, 4, 5];

// bad
let sum = 0;
for (let num of numbers) {
sum += num;
}
sum === 15;

// good
let sum = 0;
numbers.forEach((num) => {
sum += num;
});
sum === 15;

// best (use the functional force)
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;

// bad
const increasedByOne = [];
for (let i = 0; i < numbers.length; i++) {
increasedByOne.push(numbers[i] + 1);
}

// good
const increasedByOne = [];
numbers.forEach((num) => {
increasedByOne.push(num + 1);
});

// best (keeping it functional)
const increasedByOne = numbers.map((num) => num + 1);

generator-star-spacing

If you must use generators, or if you disregard our advice, make sure their function signature is spaced properly. eslint: generator-star-spacing

Why? function and * are part of the same conceptual keyword - * is not a modifier for function, function*is a unique construct, different from function.

1
2
3
4
5
6
7
8
9
// good
function* foo() {
// ...
}

// good
const foo = function* () {
// ...
};

Properties

dot-notation

Use dot notation when accessing properties. eslint: dot-notation

1
2
3
4
5
6
7
8
9
10
const luke = {
jedi: true,
age: 28,
};

// bad
const isJedi = luke['jedi'];

// good
const isJedi = luke.jedi;

bracket notation

Use bracket notation [] when accessing properties with a variable.

1
2
3
4
5
6
7
8
9
10
const luke = {
jedi: true,
age: 28,
};

function getProp(prop) {
return luke[prop];
}

const isJedi = getProp('jedi');

exponentiation operator

Use exponentiation operator ** when calculating exponentiations. eslint: no-restricted-properties.

1
2
3
4
5
// bad
const binary = Math.pow(2, 10);

// good
const binary = 2 ** 10;

Variables

no-multi-assign

Don’t chain variable assignments. eslint: no-multi-assign

Why? Chaining variable assignments creates implicit global variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// bad
(function example() {
// JavaScript interprets this as
// let a = ( b = ( c = 1 ) );
// The let keyword only applies to variable a; variables b and c become
// global variables.
let a = b = c = 1;
}());

console.log(a); // throws ReferenceError
console.log(b); // 1
console.log(c); // 1

// good
(function example() {
let a = 1;
let b = a;
let c = a;
}());

console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError

// the same applies for `const`

Comparison Operators & Equality

shortcuts for

Use shortcuts for booleans, but explicit comparisons for strings and numbers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// bad
if (isValid === true) {
// ...
}

// good
if (isValid) {
// ...
}

// bad
if (name) {
// ...
}

// good
if (name !== '') {
// ...
}

// bad
if (collection.length) {
// ...
}

// good
if (collection.length > 0) {
// ...
}

Commas

Additional trailing comma

Additional trailing comma: Yup. eslint: comma-dangle

Why? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don’t have to worry about the trailing comma problem in legacy browsers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// bad - git diff without trailing comma
const hero = {
firstName: 'Florence',
- lastName: 'Nightingale'
+ lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing']
};

// good - git diff with trailing comma
const hero = {
firstName: 'Florence',
lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing'],
};

// bad
function createHero(
firstName,
lastName,
inventorOf
) {
// does nothing
}

// good
function createHero(
firstName,
lastName,
inventorOf,
) {
// does nothing
}

// good (note that a comma must not appear after a "rest" element)
function createHero(
firstName,
lastName,
inventorOf,
...heroArgs
) {
// does nothing
}

// bad
createHero(
firstName,
lastName,
inventorOf
);

// good
createHero(
firstName,
lastName,
inventorOf,
);

// good (note that a comma must not appear after a "rest" element)
createHero(
firstName,
lastName,
inventorOf,
...heroArgs
)

Type Casting & Coercion

Strings

Strings: eslint: no-new-wrappers

1
2
3
4
5
6
7
8
9
10
11
12
13
// => this.reviewScore = 9;

// bad
const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"

// bad
const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()

// bad
const totalScore = this.reviewScore.toString(); // 不保证返回string

// good
const totalScore = String(this.reviewScore);

Numbers

Numbers: Use Number for type casting and parseInt always with a radix for parsing strings. eslint: radix no-new-wrappers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const inputValue = '4';

// bad
const val = new Number(inputValue);

// bad
const val = +inputValue;

// bad
const val = inputValue >> 0;

// bad
const val = parseInt(inputValue);

// good
const val = Number(inputValue);

// good
const val = parseInt(inputValue, 10);

Booleans

Booleans: eslint: no-new-wrappers

1
2
3
4
5
6
7
8
9
10
const age = 0;

// bad
const hasAge = new Boolean(age);

// good
const hasAge = Boolean(age);

// best
const hasAge = !!age;

Naming Conventions

Use camelCase

Use camelCase when you export-default a function. Your filename should be identical to your function’s name.

1
2
3
4
5
function makeStyleGuide() {
// ...
}

export default makeStyleGuide;

Use PascalCase

Use PascalCase when you export a constructor / class / singleton / function library / bare object.

1
2
3
4
5
6
const AirbnbStyleGuide = {
es6: {
},
};

export default AirbnbStyleGuide;

CSS-宽高等比例自适应矩形

发表于 2019-10-27 分类于 CSS

前言

有一天看某个开源项目的时候,有一个疑惑就是怎么实现一个响应式的正方形,即将宽度设为百分比,然后高度与宽度始终保持相同,上网查到了相应的方法。

解决

原理:padding 和 margin 都是相对于父元素的宽度来计算的

因此就有了下面的代码:

1
<div class="scale"></div>
1
2
3
4
5
.scale {
width: 100%;
height: 0;
padding-bottom: 100%;
}

关键点就是height: 0;和padding-bottom: 50%;

有子元素的情况

如果元素中包含子元素的话,那么仅仅是上面的CSS部分是不够的,因为 height 是 0,子元素设置的高度会撑开父元素的高度,就像下面的代码这样,scale 会变成一个宽高比1比2的盒子。

1
2
3
4
5
<div class="scale">
<div class="item">
这里是所有子元素的容器
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
.scale {
width: 100%;
padding-bottom: 100%;
height: 0;
}

.item {
width: 100%;
height: 100%;
background-color: aquamarine;
}

因此css需要改成下面这样,加上position。

1
2
3
4
5
6
7
8
9
10
11
12
13
.scale {
width: 100%;
padding-bottom: 100%;
height: 0;
position: relative;
}

.item {
width: 100%;
height: 100%;
background-color: aquamarine;
position: absolute;
}

应用

在html-tags项目中有相应的应用

CSS-stylus

发表于 2019-10-25 分类于 CSS

安装

1
npm install stylus -g

编译命令

1
stylus index.styl -o index.css   //index是创建的文件名  -o 的意思是-out 输出css文件

你会发现stylus自动帮我们在本目录下编译并生成好了index.css文件

每修改一次stylus文件就要再输入一次stylus命令进行重新编译,这样对于我们开发者来说极大的不便。
所以stylus提供了解决的方法,就是在原有基础上加上-w

1
stylus -w index.styl -o index.css   //在原有的基础上加了-w  w的意思是watch,也就是监听

这样就可以实时监听修改,并实时编译。

这是写好的 stylus 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
*
margin 0
padding 0
.tab
display flex
width 100%
height 40px
line-height 40px
.tab-item
flex 1
text-align center
& > a
display block
font-size 14px
color rgb(77, 85, 93)
text-decoration none

这是编译生成后的 css 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
* {
margin: 0;
padding: 0;
}
.tab {
display: flex;
width: 100%;
height: 40px;
line-height: 40px;
}
.tab .tab-item {
flex: 1;
text-align: center;
}
.tab .tab-item > a {
display: block;
font-size: 14px;
color: #4d555d;
text-decoration: none;
}

注释

https://www.zhangxinxu.com/jq/stylus/comments.php

在 stylus 中的单行注释不会写入编译后的 css 中,多行注释在compress选项未启用的时候才会被输出

1…8910…32

gzl

96 日志
14 分类
37 标签
© 2020 gzl
由 Hexo 强力驱动 v3.7.1
|
主题 – NexT.Pisces v7.2.0