ESLint 配置

配置方式

  1. 注释配置-使用 JavaScript 注释直接嵌入配置信息到文件。
  2. 配置文件-可以使用 JavaScriptJSONYAML文件来指定,.eslintrc.*

指定解释器选项

默认情况为 ECMAScript 5 语法。可以选择其他 ECMAScript 版本以及 JSX 语法。

但是支持 JSX 并不能支持 React,如果想使用 React 应该使用 eslint-plugin-react

分析器选项 .eslintrc.* 文件中使用parserOptions属性。可用的选项有:

  • ecmaVersion ECMAScript语法的版本,默认为3,5
  • sourceType 设置”script”(默认值)或者”module”如果你的代码是在ECMAScript中的模块。
  • ecmaFeatures 指示哪些额外的语言特性的对象
    • globalReturn 允许在全局范围 return
    • impliedStrict 使用全局严格模式
    • jsx 使用JSX
    • experimentalObjectRestSpread 启用对实验性支持对象静止/扩展性
1
2
3
4
5
6
7
8
9
{
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
}

指定解释器

默认情况下,ESLint 使用 Espree作为解释器

也可以使用以下解释器:

  • Esprima
  • Babel-ESLin
1
2
3
4
5
6
{
"parser": "esprima",
"rules": {
"semi": "error"
}
}

指定环境

可用的有:

  • browser - 浏览器全局变量。
  • node - Node.js加载全局变量和Node.js的作用域。
  • commonjs - CommonJS的全局变量和CommonJS的作用域(用这个使用Browserify /只的WebPack浏览器的代码)。
  • shared-node-browser - 全局常见的两种节点和浏览器。
  • es6-使除了模块的所有的 ECMAScript 6 的特性(这自动地将设置ecmaVersion解析器选项〜6)。
  • worker - 网络工作者全局变量。
  • amd-定义 require()define() 全局变量为每AMD规范。
  • mocha - 将所有的 mocha 全局变量。
  • jasmine - 将所有的jasmine的1.3和2.0版本测试全局变量。
  • jest - jest 全局变量。
  • phantomjs - PhantomJS 全局变量。
  • protractor - protractor 全局变量。
  • qunit - QUnit 全局变量。
  • jquery - jQuery 的全局变量。
  • prototypejs - Prototype.js 全局变量。
  • shelljs - ShellJS全局变量。
  • meteor - meteor全局变量。
  • mongo - MongoDB 的全局变量。
  • applescript -AppleScript 的全局变量。
  • nashorn - Java 8 Nashorn 全局变量。
  • serviceworker - serviceworker 的全局变量。
  • atomtest - atomtest 全局变量。
  • embertest - embertest 全局变量。
  • webextensions - webextensions 全局变量
  • greasemonkey - GreaseMonkey 全局变量。

这些环境不是相互排斥的,所以你可以定义一个超过一次。

环境可以文件的内部被指定,在配置文件或使用–env 命令行标志,例如:

1
/* eslint-env node, mocha */

要在配置文件中指定的环境中,使用env键,并指定要由每设置要启用的环境true

1
2
3
4
5
6
{
"env": {
"browser": true,
"node": true
}
}

或在一个package.json文件

1
2
3
4
5
6
7
8
9
10
{
"name": "mypackage",
"version": "0.0.1",
"eslintConfig": {
"env": {
"browser": true,
"node": true
}
}
}

全局说明

使用没有声明的变量会被警告,但如果使用的是文件里的全局变量使 ESLint 不会发出警告。

JavaScript 注释方式:
1
/* global var1, var2 */

如果你想选择指定这些全局变量不应该被写入(只读),那么你可以设置每一个false标志:

1
/* global var1:false, var2:false */
配置文件方式:
1
2
3
4
5
6
{
"globals": {
"var1": true,
"var2": false
}
}

配置插件

ESLint 支持使用第三方插件。使用插件之前,你必须使用 npm 来安装它。
要配置一个配置文件内的插件,使用的 plugins 键,其中包含插件名称的列表。该 eslint-plugin- 前缀可以从插件名称被省略。

例如:

1
2
3
4
5
6
{
"plugins": [
"plugin1",
"eslint-plugin-plugin2"
]
}

配置规则

注释方式:
1
/* eslint eqeqeq: "off", curly: "error" */

在该示例中,eqeqeq 关闭,curly 为错误。也可以使用数字相当于该规则的严重性:

1
/* eslint eqeqeq: 0, curly: 2 */

如果规则有更多的选项,你可以使用数组文本语法,如指定它们:

1
/* eslint quotes: ["error", "double"], curly: 2 */

配置文件方式:
1
2
3
4
5
6
7
{
"rules": {
"eqeqeq": "off",
"curly": "error",
"quotes": ["error", "double"]
}
}

注意:当从插件指定的规则,一定要忽略 eslint-plugin-ESLint 只使用前缀的名称在内部找到规则。

文件中暂时禁用 ESLint

例如:

1
2
3
/* eslint-disable */
alert('foo');
/* eslint-enable */

还可以禁用或启用特定规则的警告:

1
2
3
4
/* eslint-disable no-alert, no-console */
alert('foo');
console.log('bar');
/* eslint-enable no-alert, no-console */

要禁用特定行上的规则:

1
2
3
4
alert('foo'); // eslint-disable-line

// eslint-disable-next-line
alert('foo');

使用配置文件

1
eslint -c myconfig.json myfiletotest.js

规则

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
"no-alert": 0,//禁止使用alert confirm prompt
"no-array-constructor": 2,//禁止使用数组构造器
"no-bitwise": 0,//禁止使用按位运算符
"no-caller": 1,//禁止使用arguments.caller或arguments.callee
"no-catch-shadow": 2,//禁止catch子句参数与外部作用域变量同名
"no-class-assign": 2,//禁止给类赋值
"no-cond-assign": 2,//禁止在条件表达式中使用赋值语句
"no-console": 2,//禁止使用console
"no-const-assign": 2,//禁止修改const声明的变量
"no-constant-condition": 2,//禁止在条件中使用常量表达式 if(true) if(1)
"no-continue": 0,//禁止使用continue
"no-control-regex": 2,//禁止在正则表达式中使用控制字符
"no-debugger": 2,//禁止使用debugger
"no-delete-var": 2,//不能对var声明的变量使用delete操作符
"no-div-regex": 1,//不能使用看起来像除法的正则表达式/=foo/
"no-dupe-keys": 2,//在创建对象字面量时不允许键重复 {a:1,a:1}
"no-dupe-args": 2,//函数参数不能重复
"no-duplicate-case": 2,//switch中的case标签不能重复
"no-else-return": 2,//如果if语句里面有return,后面不能跟else语句
"no-empty": 2,//块语句中的内容不能为空
"no-empty-character-class": 2,//正则表达式中的[]内容不能为空
"no-empty-label": 2,//禁止使用空label
"no-eq-null": 2,//禁止对null使用==或!=运算符
"no-eval": 1,//禁止使用eval
"no-ex-assign": 2,//禁止给catch语句中的异常参数赋值
"no-extend-native": 2,//禁止扩展native对象
"no-extra-bind": 2,//禁止不必要的函数绑定
"no-extra-boolean-cast": 2,//禁止不必要的bool转换
"no-extra-parens": 2,//禁止非必要的括号
"no-extra-semi": 2,//禁止多余的冒号
"no-fallthrough": 1,//禁止switch穿透
"no-floating-decimal": 2,//禁止省略浮点数中的0 .5 3.
"no-func-assign": 2,//禁止重复的函数声明
"no-implicit-coercion": 1,//禁止隐式转换
"no-implied-eval": 2,//禁止使用隐式eval
"no-inline-comments": 0,//禁止行内备注
"no-inner-declarations": [2, "functions"],//禁止在块语句中使用声明(变量或函数)
"no-invalid-regexp": 2,//禁止无效的正则表达式
"no-invalid-this": 2,//禁止无效的this,只能用在构造器,类,对象字面量
"no-irregular-whitespace": 2,//不能有不规则的空格
"no-iterator": 2,//禁止使用__iterator__ 属性
"no-label-var": 2,//label名不能与var声明的变量名相同
"no-labels": 2,//禁止标签声明
"no-lone-blocks": 2,//禁止不必要的嵌套块
"no-lonely-if": 2,//禁止else语句内只有if语句
"no-loop-func": 1,//禁止在循环中使用函数(如果没有引用外部变量不形成闭包就可以)
"no-mixed-requires": [0, false],//声明时不能混用声明类型
"no-mixed-spaces-and-tabs": [2, false],//禁止混用tab和空格
"linebreak-style": [0, "windows"],//换行风格
"no-multi-spaces": 1,//不能用多余的空格
"no-multi-str": 2,//字符串不能用\换行
"no-multiple-empty-lines": [1, {"max": 2}],//空行最多不能超过2行
"no-native-reassign": 2,//不能重写native对象
"no-negated-in-lhs": 2,//in 操作符的左边不能有!
"no-nested-ternary": 0,//禁止使用嵌套的三目运算
"no-new": 1,//禁止在使用new构造一个实例后不赋值
"no-new-func": 1,//禁止使用new Function
"no-new-object": 2,//禁止使用new Object()
"no-new-require": 2,//禁止使用new require
"no-new-wrappers": 2,//禁止使用new创建包装实例,new String new Boolean new Number
"no-obj-calls": 2,//不能调用内置的全局对象,比如Math() JSON()
"no-octal": 2,//禁止使用八进制数字
"no-octal-escape": 2,//禁止使用八进制转义序列
"no-param-reassign": 2,//禁止给参数重新赋值
"no-path-concat": 0,//node中不能使用__dirname或__filename做路径拼接
"no-plusplus": 0,//禁止使用++,--
"no-process-env": 0,//禁止使用process.env
"no-process-exit": 0,//禁止使用process.exit()
"no-proto": 2,//禁止使用__proto__属性
"no-redeclare": 2,//禁止重复声明变量
"no-regex-spaces": 2,//禁止在正则表达式字面量中使用多个空格 /foo bar/
"no-restricted-modules": 0,//如果禁用了指定模块,使用就会报错
"no-return-assign": 1,//return 语句中不能有赋值表达式
"no-script-url": 0,//禁止使用javascript:void(0)
"no-self-compare": 2,//不能比较自身
"no-sequences": 0,//禁止使用逗号运算符
"no-shadow": 2,//外部作用域中的变量不能与它所包含的作用域中的变量或参数同名
"no-shadow-restricted-names": 2,//严格模式中规定的限制标识符不能作为声明时的变量名使用
"no-spaced-func": 2,//函数调用时 函数名与()之间不能有空格
"no-sparse-arrays": 2,//禁止稀疏数组, [1,,2]
"no-sync": 0,//nodejs 禁止同步方法
"no-ternary": 0,//禁止使用三目运算符
"no-trailing-spaces": 1,//一行结束后面不要有空格
"no-this-before-super": 0,//在调用super()之前不能使用this或super
"no-throw-literal": 2,//禁止抛出字面量错误 throw "error";
"no-undef": 1,//不能有未定义的变量
"no-undef-init": 2,//变量初始化时不能直接给它赋值为undefined
"no-undefined": 2,//不能使用undefined
"no-unexpected-multiline": 2,//避免多行表达式
"no-underscore-dangle": 1,//标识符不能以_开头或结尾
"no-unneeded-ternary": 2,//禁止不必要的嵌套 var isYes = answer === 1 ? true : false;
"no-unreachable": 2,//不能有无法执行的代码
"no-unused-expressions": 2,//禁止无用的表达式
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],//不能有声明后未被使用的变量或参数
"no-use-before-define": 2,//未定义前不能使用
"no-useless-call": 2,//禁止不必要的call和apply
"no-void": 2,//禁用void操作符
"no-var": 0,//禁用var,用let和const代替
"no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],//不能有警告备注
"no-with": 2,//禁用with

"array-bracket-spacing": [2, "never"],//是否允许非空数组里面有多余的空格
"arrow-parens": 0,//箭头函数用小括号括起来
"arrow-spacing": 0,//=>的前/后括号
"accessor-pairs": 0,//在对象中使用getter/setter
"block-scoped-var": 0,//块语句中使用var
"brace-style": [1, "1tbs"],//大括号风格
"callback-return": 1,//避免多次调用回调什么的
"camelcase": 2,//强制驼峰法命名
"comma-dangle": [2, "never"],//对象字面量项尾不能有逗号
"comma-spacing": 0,//逗号前后的空格
"comma-style": [2, "last"],//逗号风格,换行时在行首还是行尾
"complexity": [0, 11],//循环复杂度
"computed-property-spacing": [0, "never"],//是否允许计算后的键名什么的
"consistent-return": 0,//return 后面是否允许省略
"consistent-this": [2, "that"],//this别名
"constructor-super": 0,//非派生类不能调用super,派生类必须调用super
"curly": [2, "all"],//必须使用 if(){} 中的{}
"default-case": 2,//switch语句最后必须有default
"dot-location": 0,//对象访问符的位置,换行的时候在行首还是行尾
"dot-notation": [0, { "allowKeywords": true }],//避免不必要的方括号
"eol-last": 0,//文件以单一的换行符结束
"eqeqeq": 2,//必须使用全等
"func-names": 0,//函数表达式必须有名字
"func-style": [0, "declaration"],//函数风格,规定只能使用函数声明/函数表达式
"generator-star-spacing": 0,//生成器函数*的前后空格
"guard-for-in": 0,//for in循环要用if语句过滤
"handle-callback-err": 0,//nodejs 处理错误
"id-length": 0,//变量名长度
"indent": [2, 4],//缩进风格
"init-declarations": 0,//声明时必须赋初值
"key-spacing": [0, { "beforeColon": false, "afterColon": true }],//对象字面量中冒号的前后空格
"lines-around-comment": 0,//行前/行后备注
"max-depth": [0, 4],//嵌套块深度
"max-len": [0, 80, 4],//字符串最大长度
"max-nested-callbacks": [0, 2],//回调嵌套深度
"max-params": [0, 3],//函数最多只能有3个参数
"max-statements": [0, 10],//函数内最多有几个声明
"new-cap": 2,//函数名首行大写必须使用new方式调用,首行小写必须用不带new方式调用
"new-parens": 2,//new时必须加小括号
"newline-after-var": 2,//变量声明后是否需要空一行
"object-curly-spacing": [0, "never"],//大括号内是否允许不必要的空格
"object-shorthand": 0,//强制对象字面量缩写语法
"one-var": 1,//连续声明
"operator-assignment": [0, "always"],//赋值运算符 += -=什么的
"operator-linebreak": [2, "after"],//换行时运算符在行尾还是行首
"padded-blocks": 0,//块语句内行首行尾是否要空行
"prefer-const": 0,//首选const
"prefer-spread": 0,//首选展开运算
"prefer-reflect": 0,//首选Reflect的方法
"quotes": [1, "single"],//引号类型 `` "" ''
"quote-props":[2, "always"],//对象字面量中的属性名是否强制双引号
"radix": 2,//parseInt必须指定第二个参数
"id-match": 0,//命名检测
"require-yield": 0,//生成器函数必须有yield
"semi": [2, "always"],//语句强制分号结尾
"semi-spacing": [0, {"before": false, "after": true}],//分号前后空格
"sort-vars": 0,//变量声明时排序
"space-after-keywords": [0, "always"],//关键字后面是否要空一格
"space-before-blocks": [0, "always"],//不以新行开始的块{前面要不要有空格
"space-before-function-paren": [0, "always"],//函数定义时括号前面要不要有空格
"space-in-parens": [0, "never"],//小括号里面要不要有空格
"space-infix-ops": 0,//中缀操作符周围要不要有空格
"space-return-throw-case": 2,//return throw case后面要不要加空格
"space-unary-ops": [0, { "words": true, "nonwords": false }],//一元运算符的前/后要不要加空格
"spaced-comment": 0,//注释风格要不要有空格什么的
"strict": 2,//使用严格模式
"use-isnan": 2,//禁止比较时使用NaN,只能用isNaN()
"valid-jsdoc": 0,//jsdoc规则
"valid-typeof": 2,//必须使用合法的typeof的值
"vars-on-top": 2,//var必须放在作用域顶部
"wrap-iife": [2, "inside"],//立即执行函数表达式的小括号风格
"wrap-regex": 0,//正则表达式字面量用小括号包起来
"yoda": [2, "never"]//禁止尤达条件

详细规则参考官方文档

评论和共享

本地开发服务器

安装 webpack-dev-server

1
npm install --save-dev webpack-dev-server

修改配置文件 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
...
devServer: {
contentBase: "./", //告诉服务器在哪里,从提供内容
port: 9000, //端口
inline: true, //
compress: false, //是否启用gzip压缩
historyApiFallback: false, //将404定向到固定位置
}
...
};

修改 package.json

1
2
3
4
5
6
7
{
...
"scripts": {
"dev": "webpack-dev-server",
},
...
}

webpack HotModuleReplacement

1
npm install --save react-hot-loader@next

Create a .babelrc

1
2
3
4
5
6
7
8
9
{
"presets": [
["es2015", {"modules": false}],
"react"
],
"plugins": [
"react-hot-loader/babel"
]
}

webpack.config.js

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
const { resolve } = require('path');
const webpack = require('webpack');

module.exports = {
context: resolve(__dirname, 'src'),

entry: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/only-dev-server',
'./index.js'
// the entry point of our app
],
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'dist'),
publicPath: '/'
},

devtool: 'inline-source-map',

devServer: {
hot: true,
// enable HMR on the server
},

module: {
rules: [
{
test: /\.jsx?$/,
use: [ 'babel-loader', ],
exclude: /node_modules/
//注意,这里不能使用 options: {} 应该在项目根目录创建 .babelrc
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader?modules', ],
},
],
},

plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
// prints more readable module names in the browser console on HMR updates
],
};

src/index.js

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
import React from 'react';
import ReactDOM from 'react-dom';

import { AppContainer } from 'react-hot-loader';
// AppContainer is a necessary wrapper component for HMR

import App from './components/App';

const render = (Component) => {
ReactDOM.render(
<AppContainer>
<Component/>
</AppContainer>,
document.getElementById('root')
);
};

render(App);

// Hot Module Replacement API
if (module.hot) {
module.hot.accept('./components/App', () => {
render(App)
});
}

webpack 仪表盘

安装 webpack-dashboard

1
npm install webpack-dashboard --save-dev

修改配置文件 webpack.config.js

1
2
3
4
5
6
7
8
9
10
// Import the plugin:
var DashboardPlugin = require('webpack-dashboard/plugin');

// If you aren't using express, add it to your webpack configs plugins section:
plugins: [
new DashboardPlugin({ port: 3001 })
]

// If you are using an express based dev server, add it with compiler.apply
compiler.apply(new DashboardPlugin());

HTML代码热加载

webpack-dev-server 只能监控入口文件(JS/LESS/CSS/IMG)的变化,因此 HTML 文件的变化必须依赖插件来进行监控。
安装 html-webpack-plugin

1
npm install html-webpack-plugin --save-dev

修改配置文件 webpack.config.js, 把 index.html 加入监控

1
2
3
4
5
6
7
8
9
10
11
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
...
plugins: [
new HtmlWebpackPlugin({ // html代码热加载
template: './index.html'
}),
],
...
};

自动打开浏览器

安装 open-browser-webpack-plugin

1
npm install open-browser-webpack-plugin --save-dev

修改配置文件 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
var OpenBrowserPlugin = require('open-browser-webpack-plugin');

module.exports = {
...
plugins: [
new OpenBrowserPlugin({ //自动打开浏览器
url: 'http://localhost:9000'
})
],
...
};

配置 json 加载器

安装 json-loader

1
npm install json-loader --save-dev

修改配置文件 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
...
module: {
rules: [{
test: /\.json$/,
use: [
{ loader: "json-loader" },
]
}]
}
};

创建 config.json 文件,内容如下

1
2
3
4
{
"name": "demo",
"type": "HTML5"
}

使用

1
2
3
var config = require('../config.json')

console.log(config.name);

配置 LESS 编译

安装 less style-loader css-loader less-loader

1
npm install less style-loader css-loader less-loader --save-dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = {
...
module: {
rules: [
{
test: /\.less$/,
use: [
{ loader: "less-loader" },
{ loader: "style-loader" },
{
loader: "css-loader",
options: {
modules: true,
}
}
]
},
}
};

配置 Babel 编译

安装 babel-core babel-loader babel-preset-es2015

1
npm install babel-core babel-loader babel-preset-es2015 --save-dev

修改配置文件 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
...
module: {
rules: [{
test: /\.js$/, //babel解析器
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015']
}
}]
}
};

配置 React

安装 babel-core babel-loader babel-preset-es2015 babel-preset-react

1
npm install babel-core babel-loader babel-preset-es2015 babel-preset-react --save-dev

修改配置文件 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.exports = {
...
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: [
path.resolve(__dirname, "/node_modules/")
],
query: {
presets: ['es2015','react']
}
}
]
}
};

配置 jQuery 解析器

安装 jquery

1
npm install jquery --save-dev

修改配置文件 webpack.config.js

1
2
3
4
5
6
7
8
9
10
module.exports = {
...
plugins: [
new webpack.ProvidePlugin({ //jquery解析器
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
]
};

配置 js 代码压缩

修改配置文件 webpack.config.js, 在 plugin 中添加 webpack.optimize.UglifyJsPlugin 模块

1
2
3
4
5
6
7
8
9
10
11
12
13
var webpack = require('webpack');
var uglifyJsPlugin = webpack.optimize.UglifyJsPlugin;

module.exports = {
...
plugins: [
new uglifyJsPlugin({ //js代码压缩
compress: {
warnings: false
}
})
]
};

配置 eslint 语法解析

安装 esline

1
npm install eslint eslint-loader eslint-friendly-formatter eslint-plugin-html babel-eslint eslint-config-standard eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-standard --save-dev

在项目根目录下添加 eslint 配置文件 .eslintrc.js

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
// http://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
},
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
extends: 'standard',
// required to lint *.vue files
plugins: [
'html'
],
// add your custom rules here
'rules': {
// allow paren-less arrow functions
'arrow-parens': 0,
"indent": [2, 4],//缩进风格
'no-undef': 0,
// allow async-await
'generator-star-spacing': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
}
}

修改配置文件 webpack.config.js
安装 url-loader

1
npm install url-loader --save-dev

修改配置文件 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
...
module: {
loaders: [{
test: /\.(png|jpg)$/,
use: [
{
loader: "url-loader"
}
]
}]
}
};

配置图片

1
2
3
4
5
6
7
module.exports = {
...
module: {
loaders: [
]
}
};

配置 normalize.css

安装 normalize.css

1
npm install normalize.css --save

使用

1
import 'normalize.css';

配置 iconfont

  • http://www.iconfont.cn/ 选图标,添加到购物车,下载代码。
  • 有三种方式,推荐使用 unicode 方式,将字体文件和 iconfont.css 放到项目中
  • iconfont.css 修改字体路径例如 url('../font/iconfont.woff?t=1494653894295') 形式
  • 修改 webpack.config.js 配置 url-loader

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    module.exports = {
    ...
    module: {
    loaders: [{
    test: /\.(woff|svg|eot|ttf)\??.*$/,
    use: [
    {
    loader: "url-loader"
    }
    ]
    }]
    }
    };
  • 使用 iconfont

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import React, { Component } from 'react';
    import Font from '../../style/iconfont.css';

    class Banner extends Component{
    render(){
    return (
    <div className={Style.historyButtonBack+" "+Font.iconfont+" "+Font["icon-houtui"]}></div>
    )
    }
    }

评论和共享

CSS-Modules

CSS Modules 用法

局部作用域

CSS 的规则是全局的,产生局部作用域的唯一方法,就是使用一个独一无二的 class 的名字,不会与其他选择器重名。这就是 CSS Modules 的做法。
下面是一个 React 组件 App.js

1
2
3
4
5
6
7
8
9
10
import React from 'react';
import style from './App.css';

export default () => {
return (
<h1 className={style.title}>
Hello World
</h1>
);
};

将样式文件 App.css 输入到 style 对象,然后引用 style.title 代表一个 class

1
2
3
.title {
color: red;
}

构建工具会将 style.title 编译成一个哈希字符串。

1
2
3
<h1 class="_3zyde4l1yATCOkgn-DBWEL">
Hello World
</h1>

app.css 也会同时被编译。

1
2
3
._3zyde4l1yATCOkgn-DBWEL {
color: red;
}

这样一来,这个类名就变成独一无二了,只对 App 组件有效。

CSS Modules 提供各种插件,支持不同的构建工具。本文使用的是 Webpackcss-loader 插件,因为它对 CSS Modules 的支持最好,而且很容易使用。

示例配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module.exports = {
entry: __dirname + '/index.js',
output: {
publicPath: '/',
filename: './bundle.js'
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', 'stage-0', 'react']
}
},
{
test: /\.css$/,
loader: "style-loader!css-loader?modules"
},
]
}
};

但个人认为使用下面这种方法更加好些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: [
path.resolve(__dirname, "/node_modules/")
],
query: {
presets: ['es2015','react']
}
},
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{loader: "css-loader",options: {modules: true}}
]
}
]
},

上面代码中,关键的一行是 style-loader!css-loader?modules ,它在 css-loader 后面加了一个查询参数 modules ,表示打开 CSS Modules 功能。

全局作用域

CSS Modules 允许使用 :global(.className) 的语法,声明一个全局规则。凡是这样声明的class,都不会被编译成哈希字符串。

App.css加入一个全局class

1
2
3
4
5
6
7
.title {
color: red;
}

:global(.title) {
color: green;
}

CSS Modules 还提供一种显式的局部作用域语法 :local(.className),等同于.className,所以上面的App.css也可以写成下面这样。

1
2
3
4
5
6
7
:local(.title) {
color: red;
}

:global(.title) {
color: green;
}

定制哈希类名

css-loader默认的哈希算法是[hash:base64],这会将.title编译成._3zyde4l1yATCOkgn-DBWEL这样的字符串。
webpack.config.js里面可以定制哈希字符串的格式。

1
2
3
4
5
6
7
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]'
}
}

Class 组合

在 CSS Modules 中,一个选择器可以继承另一个选择器的规则,这称为”组合”(”composition”)。

App.css 中,让 .title 继承 .className

1
2
3
4
5
6
7
8
.className {
background-color: blue;
}

.title {
composes: className;
color: red;
}

输入其他模块

选择器也可以继承其他 CSS 文件里面的规则。

App.css 可以继承 another.css 里面的规则。

1
2
3
4
.title {
composes: className from './another.css';
color: red;
}

输入变量

CSS Modules 支持使用变量,不过需要安装 postcss-loaderpostcss-modules-values

1
npm install postcss-loader postcss-modules-values --save-dev

1
2
3
4
{
test: /\.css$/,
loader: "style-loader!css-loader?modules!postcss-loader"
}

接着,在colors.css里面定义变量

1
2
3
@value blue: #0c77f8;
@value red: #ff0000;
@value green: #aaf200;

App.css 可以引用这些变量。

1
2
3
4
5
6
7
@value colors: "./colors.css";
@value blue, red, green from colors;

.title {
color: red;
background-color: blue;
}

评论和共享

CSS-Modules

根据浏览器CSS渲染原理写出高性能的CSS代码

注意:CSS 引擎查找样式表,对每条规则都按从右到左的顺序去匹配。

例子

示例1

1
#nav li {}

CSS选择符是从右到左进行匹配的,浏览器必须遍历页面上每个li元素并确定其父元素的id是否为nav。

示例2

1
*{}

这种效率是差到极点的做法,因为 * 通配符将匹配所有元素,所以浏览器必须去遍历每一个元素,这样的计算次数可能是上万次

示例3

1
ul#nav{} ul.nav{}

在页面中一个指定的ID只能对应一个元素,所以没有必要添加额外的限定符,而且这会使它更低效。同时也不要用具体的标签限定类选择符,而是要根据实际的情况对类名进行扩展。例如把 ul.nav 改成 .main_nav 更好。

优化方案

  1. 避免使用通配规则。如 *{} 计算次数惊人!只对需要用到的元素进行选择
  2. 尽量少的去对标签进行选择,而是用 class。如: #nav li{} ,可以为 li 加上 nav_item 的类名,如下选择 .nav_item{}

  3. 不要去用标签限定ID或者类选择符。如:ul#nav ,应该简化为 #nav

  4. 尽量少的去使用后代选择器,降低选择器的权重值。后代选择器的开销是最高的,尽量将选择器的深度降到最低,最高不要超过三层,更多的使用类来关联每一个标签元素。

  5. 考虑继承。了解哪些属性是可以通过继承而来的,然后避免对这些属性重复指定规则

评论和共享

CSS-Modules

css-loader & style-loader 的区别

css-loader以一个字符串的形式读入一个css文件。并返回带导入的CSS,并通过webpack的require功能解决url(…):

style-loader会使用这些样式,并在包含这些样式的页面的元素中创建一个

评论和共享

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
(function(){
var video = document.getElementById('video');
var canvas = document.getElementById('canvas');
var canvasContext = canvas.getContext('2d');
var audio_context;

function __log(e, data) {
console.log(e + " " + (data || ''));
}

function createRecorder(stream, handleMessage) {
//获取视频数据
if (navigator.mozGetUserMedia) {
video.mozSrcObject = stream;
} else {
var vendorURL = window.URL || window.webkitURL;
video.src = vendorURL.createObjectURL(stream);
}
video.play();

var input = audio_context.createMediaStreamSource(stream);

var recorder = new Recorder(input, {
serverUrl: "wss://rating.llsstaging.com/llcup/stream/upload",
handleMessage: handleMessage
});

__log('Recorder initialised.');

return recorder;
}

var Page = function() {
var self = this;
var inited = false;
var recorder = null;

var handleMessage = function(resp) {
try {
var respObj = JSON.parse(resp);
// if(respObj.decoded){
// textToEmotion(respObj.decoded);
// }
chat(respObj.decoded);
self.overallScore(respObj.decoded);
respObj.details.forEach(function(wordRate) {
self.wordRates.push({
word: wordRate.word,
score: wordRate.confidence
})
});
} catch (e) {
//self.hasError(true);
self.errorResp(resp);
self.errorInfo(e.message);
}
};

this.inited = ko.observable(false);
initAudioSetting(function(stream){
recorder = createRecorder(stream, handleMessage);
self.inited(true);
});

this.hasError = ko.observable(false);
this.errorResp = ko.observable('');
this.errorInfo = ko.observable('');
this.wordRates = ko.observableArray([]);
this.readingRefText = ko.observable(randomPick(Constants.PreparedTexts));
this.recording = ko.observable(false);
this.overallScore = ko.observable();
this.recordButtonText = ko.computed(function() {
return self.recording() ? "▧" : "▶";
});
this.toggleRecording = function() {
self.hasError(false);
self.wordRates.removeAll();
self.recording(!self.recording());
};

//this.switchRefText = function() {
// self.readingRefText(randomPick(Constants.PreparedTexts));
//}

this.recording.subscribe(function(){
if(self.recording()) {
/*
algConfig = {
type: 'readaloud',
quality: -1,
//reftext: self.readingRefText().replace(/[,.]/g, '')
reference: self.readingRefText().toLowerCase().replace(/[^A-Za-z0-9']/g, ' ').trim()
};
*/
algConfig = {
type: 'asr',
quality: -1
};
console.log(algConfig);
recorder.record({
algConfig: algConfig
});
} else {
recorder.stop();
recorder.clear();
}
});
}

var initAudioSetting = function(startUserMediaCallback) {
try {
// webkit shim
window.AudioContext = window.AudioContext || window.webkitAudioContext;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
window.URL = window.URL || window.webkitURL;

audio_context = new AudioContext;
__log('Audio context set up.');
__log('navigator.getUserMedia ' + (navigator.getUserMedia ? 'available.' : 'not present!'));
} catch (e) {
alert('No web audio support in this browser!');
}

navigator.getUserMedia({
audio: true,
video: {
mandatory: {
maxWidth: 320,
maxHeight: 240
}
}
}, startUserMediaCallback, function(e) {
__log('No live audio input: ' + e);
});
}

window.onload = function init() {
window.page = new Page();
ko.applyBindings(window.page);
setTimeout(imgToEmotion, 1000);
};

var tabButton = document.getElementById("tab-change");
tabButton.addEventListener('click',function () {
if(tabButton.checked){
tab_change_state = false;
}else{
tab_change_state = true;
imgToEmotion();
}
});
var tabVideo = document.getElementById("tab-video");
tabVideo.addEventListener('click',function () {
if(tabVideo.checked){
video.style.display = 'none';
}else{
video.style.display = 'block';
}
});

//聊天API
function chat(str){
var data = {data: str};
$.ajax({
url: "https://www.hupeng.wang/PicServer/re_chat.php",
type: "POST",
data: data
}).done(function(data){
document.getElementById("chat-box").innerText = data;
textToEmotion(data);
}).fail(function(){
console.log('Chat Error!');
})
}

//文本分析得到情感json
function textToEmotion(str){
var data = {data:str};
$.ajax({
url: "https://www.hupeng.wang/PicServer/re_text.php",
type: "POST",
data: data
}).done(function(data){
data = JSON.parse(data);
console.log(data);
var res = {name:"def",value:0};
var emotion = data["document_tone"]["tone_categories"][0]["tones"];
for(var i = 0;i<emotion.length;i++){
if(emotion[i]["score"]>res.value){
res.value = emotion[i]["score"];
res.name = emotion[i]["tone_id"];
}
}
console.log(res.name);
changeState(res.name);
}).fail(function(){
console.log('textToEmotion Error');
})
}

//图片上传得到情感json
function imgToEmotion() {
if(!tab_change_state) return;
canvasContext.drawImage(video, 0, 0, 320, 240);
var element = document.createElement("img");
element.src = canvas.toDataURL();

var pic = {data:element.src};

$.ajax({
url: "https://www.hupeng.wang/PicServer/re_pic.php",
type: "POST",
// Request body
data: pic,
}).done(function(data) {
data = JSON.parse(data);
if(data.length!==0){
var res = {name:"def",value:0};
var scores = data['0']["scores"];
for(var score in scores){
//console.log(score,scores[score]);
if(scores[score]>res.value){
res.value = scores[score];
res.name = score;
}
}
changeState(res.name);
}else{
console.log("未识别到人");
}
setTimeout(imgToEmotion,0);
}).fail(function() {
setTimeout(imgToEmotion,0);
});
}
}).call(window);

评论和共享

队友

名字 学校
胡鹏 内蒙古大学
任文頔 中山大学
岳磊 上海理工大学
额…(我不是故意的 ::>_<::) 上海理工大学

时间

2017-05-05~2017-05-07

赛况

参加 HACKxSJTU 比赛,进过和队友的努力奋斗,最终获得 英语流利说黑马奖(企业第二名)。不幸的是英语流利说这个太坑,最后的奖品是9个月的AI英语课程(正好可以学一波英语),但是最气的是没有证书,没有证书!!没有证书!!!连电子版都没有! Orz 重要的话说三遍。

收获

这次比赛最大的收获其实不是技术上的,如果单从技术上说也不比他们差很多,前端和后端本来也没什么好比的。但是这次真的感觉到了环境对人的重要性,还有一个就是英语真的该好好加强一下了,简直太糟糕了。

作品

Emotion-Journal

憋了一肚子负面情绪却没处释放?还在到处寻找管理、控制情绪的工具?快来emotion-journal吧

Emotion-Journal

项目地址

Hackx-Emotion-Journal

创意来源

情绪是一把双刃剑,良好的情绪控制有助于我们健康的生活。很多时候,我们因为外界各种各样的事情产生许许多多的情绪。“病由心生”由此而来。人不可能永远有好心情,生活中既然有挫折、有失败,就会有消极的情绪。所以我们希望推出一款简单,及时,便捷的产品帮助你放松心情以及随时随地监测自己的情绪变化。

详情描述

得益于人工智能的发展,我们使用微软认知服务和IBM Watson文本情绪分析,多方面感知用户当前的情绪状态。并在屏幕上实时反馈。由此我们实现了监控单天和单月的情绪控制情况。用户有2种可以选择的模式,下面将分别介绍

  • 情绪模仿
    用户任意做出表情,我们平台利用摄像头采集数据,接入微软认知服务,实时模仿用户的表情,并反馈在页面中。

  • 人机交互
    用户使用英语语音与本平台交流,我们使用流利说语音解析服务分析用户输入,结合IBM Watson文本情感分析服务得到用户表达的情感,传送到我们搭建的图灵机器人API,获得文字输出,结合相应的情绪,显示在页面中。

图片展示

人脸表情识别
语音聊天

使用的服务与API

  • 微软认知服务(情绪识别api)
  • IBM watson 文本情绪识别
  • 流利说语音识别服务
  • 青云云服务
  • 图灵机器人api
  • 百度翻译api

解决的问题

  • 机器人实时模仿用户的表情。
  • 机器人与用户语言对话,并根据对话内容反馈表情。
  • 憋了一肚子负面情绪却没处释放?还在到处寻找管理、控制情绪的工具?快来emotion-journal吧

项目使用指南

项目界面有3个可见的交互按钮

  • 视频框的打开和关闭
  • 情绪模仿/人机交互的切换
  • 语音采集
    用户打开界面后默认进入情绪模仿功能,页面中的动画表情可以根据用户的表情一起变换。当按下按钮2(情绪模仿/人机交互的切换按钮)进入人机交互的功能,用户按下按钮3开始录音,说一段话(暂时只能输入英语)后按下按钮3结束录音,本平台会根据输入做出相应的回答与表情,在页面中反馈出来。

功能设计说明

项目整体包含2个功能,下面将具体说明:

  • 情绪感知与反馈功能
    整体过程为:情绪图片捕捉、图片情绪分析、情绪模仿展示
  • 人机交互与情绪反馈功能
    整体实现步骤为:语音输入、语音转文字、文字情感分析、文字反馈生成、模拟情绪反馈展示
    在界面上包括辅助的功能:摄像头打开/关闭 、情绪模仿/人机交互切换 、 语音录制开始与结束

核心记说说明

本项目的后台部分核心技术主要由:用户脸部图像的情感分析,用户对话文本情感分析,用户对话交互以及CSS3动画展示4个部分组成。

  • 用户的脸部情感分析:使用了微软的认知服务中的情感部分,由于需要分析的图像的大小较大,为了加快图像识别的速度,我们团队引入了青云的对象存储服务以及CDN加速服务提高了应用服务器到微软的认知服务器之间的访问速度。
  • 用户对话文本情感分析:使用了流利说的语音识别服务获取了用户的语音的文本结果。在文本结果的情感分析分析,使用了IBM Watson的文本的情感分析Tone Analyzer技术。
  • 用户对话交互:使用了图灵机器人的对话API以及百度的翻译API完成。
  • 大量使用 CSS3 动画绘制机器人表情,并使用 HTML5 Media 技术获取用户的音频和视频。

未来发展规划

设计并完善用户注册及登录服务,以便更好得管理、分析用户的情绪状况

将平台移植到移动端,便于用户随时监控情绪,查看统计状态

评论和共享

Atom 常用插件

插件

web开发

插件名 作用 备注
emmet HTML代码补全 emmet代码展开
autoprefixer css自动浏览器前缀适配
minimap 类似sublime右侧代码地图
minimap-linter 代码地图显示linter
minimap-pigments 代码地图显示颜色
highlight-selected 高亮选中单词
open-in-browser 用默认应用打开文件
color-picker 图形界面选取颜色
regex-railroad-diagram 图形化正则表达式
file-icons 美化默认icons
atom-beautify 一键整理代码
atom-ternjs js代码提示很强大,高度定制化。
docblockr js注释doc
autoclose-html 自动闭合HTML标签
pigments 颜色显示插件 必装
linter 错误检查工具
linter-ui-default linter 界面
linter-eslint es语法检错
linter-jslint js语法检错
linter-htmlhint html语法检错
linter-stylelint css语法检错
linter-sass-lint sass语法检错
simplified-chinese-menu 汉化插件
autocomplete-paths 路径自动完成插件
pretty-json json自动整理
language-gitignore gitignore语法高亮
markdown-preview-plus markdown预览
editorconfig 支持.editorconfig
1
apm install emmet autoprefixer minimap highlight-selected open-in-browser color-picker regex-railroad-diagram file-icons atom-beautify docblockr autoclose-html pigments linter linter-ui-default linter-eslint linter-jslint linter-htmlhint linter-csslint linter-sass-lint simplified-chinese-menu autocomplete-paths pretty-json language-gitignore markdown-preview-plus

React

插件名 作用 备注
react-snippets
react-es6-snippets es6写法的react snippet
language-babel 写React必不可少
language-javascript-jsx JavaScript, ES6, ES7, React JSX, Flow支持
atom-react-autocomplete 项目内,组件名及状态的自动补全

外观

插件名 作用 备注
atom-material-syntax

评论和共享

首先本文书写遵守以下约定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function A(){
// 私有属性
var val = 1; // 私有基本属性
var arr = [1]; // 私有引用属性
function fun(){} // 私有函数(引用属性)

// 实例属性
this.val = 1; // 实例基本属性
this.arr = [1]; // 实例引用属性
this.fun = function(){}; // 实例函数(引用属性)
}

// 原型属性
A.prototype.val = 1; // 原型基本属性
A.prototype.arr = [1]; // 原型引用属性
A.prototype.fun = function(){}; // 原型函数(引用属性)

A.f = function(){
//.... // 类方法
}

其中:A 为被继承的父类, B 为继承 A 的子类。

六种继承简单介绍

简单原型继承

简单原型继承

这是最简单的继承方式,就一行代码可以完成

  • 代码实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function A(){
    this.arr = [1];
    }
    function B(){
    // ...
    }
    B.prototype = new A(); // 核心

    var b1 = new B();
    var b2 = new B();

    sub1.arr.push(2);
    alert(sub1.arr); // 1, 2
    alert(sub2.arr); // 1, 2
  • 优点

    • 非常简单
  • 缺点

    • 来自原型对象的引用属性是所有实例共享的。
    • 创建子类实例时,无法向父类构造函数传参。

借用构造函数

借用构造函数

  • 代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function A(val){
    this.arr = [1];

    this.fun = function(){
    // ...
    }
    }
    function B(val){
    Super.call(this, val); // 核心
    // ...
    }

    var b1 = new B(1);
    var b2 = new B(2);
    b1.arr.push(2);

    alert(sub1.arr); // 1, 2
    alert(sub2.arr); // 1

    alert(sub1.fun === sub2.fun); // false
  • 优点

    • 解决了子类实例共享父类引用属性的问题
    • 创建子类实例时,可以向父类构造函数传参
    • (解决了简单原型继承的问题)
  • 缺点
    • 无法实现函数复用,每个子类实例都持有一个新的函数实例,影响性能

组合继承(最常用)

组合继承

  • 代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    function A(val){
    // 只在此处声明基本属性和引用属性
    this.val = val;
    }
    // 在此处声明函数
    A.prototype.fun1 = function(){};
    A.prototype.fun2 = function(){};

    function B(val){
    A.call(this, val); // 核心
    // ...
    }
    B.prototype = new A(1); // 核心,此处的 1 被覆盖

    var b1 = new B(2);
    var b2 = new B(3);
    alert(b1.fun === b2.fun); // true
    b1.val //2
    b2.val //3
  • 优点

    • 不存在引用属性共享问题
    • 可传参
    • 函数可复用
  • 缺点
    • 父类构造函数被调用了两次,子类原型上的属性被覆盖,形成浪费。(图中 O 处)

寄生组合继承 (最佳方式)

寄生组合继承

  • 代码实现

    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
    /**
    * 返回一个继承自原型对象p的属性的新对象。
    * 本函数出自 《JavaScript权威指南 第6版》 P122,例6-1
    */
    function inherit(p){
    if(p==null) throw TypeError();
    if(Object.create)
    return Object.create(p);
    var t = typeof p;
    if(t !== 'object' && t!== 'function') throw TypeError();
    var f = function(){};
    f.prototype = p;
    return new f();
    }

    function A(){
    // 只在此处声明基本属性和引用属性
    this.val = 1;
    }
    // 在此处声明函数
    A.prototype.fun1 = function(){};
    A.prototype.fun2 = function(){};

    function B(){
    A.call(this); // 核心
    // ...
    }
    var proto = inherit(Super.prototype); // 核心
    proto.constructor = Sub; // 核心
    Sub.prototype = proto; // 核心,完善原型链

    var b = new B();
    alert(b.val);
  • 优点

    • 解决了上面的所有问题
  • 缺点

    • 写法较麻烦

原型式

原型式

  • 代码实现

    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
    /**
    * 返回一个继承自原型对象p的属性的新对象。
    * 本函数出自 《JavaScript权威指南 第6版》 P122,例6-1
    */
    function inherit(p){
    if(p==null) throw TypeError();
    if(Object.create)
    return Object.create(p);
    var t = typeof p;
    if(t !== 'object' && t!== 'function') throw TypeError();
    var f = function(){};
    f.prototype = p;
    return new f();
    }

    function A(){
    this.val = 1;
    }

    // 拿到父类对象
    var a = new A();
    var b = inherit(a); // 核心
    // 增强
    b.attr1 = 1;
    b.attr2 = 2;


    alert(b.val); // 1
  • 优点

    • 从已有对象衍生新对象,不需要创建自定义类型(复制)
  • 缺点
    • 原型引用属性会被所有实例共享
    • 无法实现代码复用(新对象是现取的,属性是现添的,都没用函数封装,怎么复用)

寄生式

  • 代码实现
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
/**
* 返回一个继承自原型对象p的属性的新对象。
* 本函数出自 《JavaScript权威指南 第6版》 P122,例6-1
*/
function inherit(p){
if(p==null) throw TypeError();
if(Object.create)
return Object.create(p);
var t = typeof p;
if(t !== 'object' && t!== 'function') throw TypeError();
var f = function(){};
f.prototype = p;
return new f();
}

function A(){
this.val = 1;
}

function getSubObject(obj){
// 创建新对象
var clone = inherit(obj); // 核心
// 增强
clone.attr1 = 1;
clone.attr2 = 2;

return clone;
}
var b = getSubObject(new A());
alert(b.val); // 1
alert(sub.attr1); // 1

给原型式继承穿了个马甲而已
优缺点同上

六种继承之间的关系

关系

参考文章

重新理解JS的6种继承方式

评论和共享

安装

1
$ npm install -g json-server

数据库文件

新建一个 json 文件,默认文件名为 db.json

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
{
"user": [
{
"id": 10000,
"name": "一韬",
"age": 25,
"gender": "male"
},
{
"id": 10001,
"name": "张三",
"age": 30,
"gender": "female"
}
],
"book": [
{
"id": 10000,
"name": "JavaScript从入门到精通",
"price": 9990,
"owner_id": 10000
},
{
"id": 10001,
"name": "Java从入门到放弃",
"price": 1990,
"owner_id": 10001
}
]
}

Start JSON Server

1
$ json-server --watch db.json -p 3000

get


1
2
3
4
//按照id查
http://localhost:3001/user/10000
//按照属性查
http://localhost:3001/user?age=30

post


1
http://localhost:3001/user?name=killer&age=23

delete


1
http://localhost:3001/user/10000

patch


1
http://localhost:3001/user?name=killer&age=24

注意

  • POST,PUT,PATCH或DELETE请求,则使用lowdb将更改自动安全地保存到db.json。

  • 请求体JSON应该是对象,就像GET输出一样。 (例如{“name”:“Foobar”})

  • Id值不可变。 PUT或PATCH请求的正文中的任何id值将被忽略。 只有在POST请求中设置的值才能被遵守,但只有在尚未占用的情况下才能被遵守。

  • POST,PUT或PATCH请求应包含Content-Type:application / json标头,以在请求正文中使用JSON。 否则将导致200 OK,但不改变数据。

  • 可以用 . 来请求更深层的数据

  • 使用 _page_limit 来分页请求,默认返回10条数据

  • 使用 _sort_order 来对结果排序

更多

查看官方文档

评论和共享

作者的图片

Archie Shi

Nothing to say


Front-End Development Engineer