0%

四个核心概念:

  • 入口(entry)
  • 输出(output)
  • loader
  • 插件(plugins)

入口

指定入口文件的路径,作为构建内部依赖图的起点。

webpack.config.js配置文件中:

1
2
3
module.exports = {
entry: './path/to/my/entry/file.js'
}

出口

在配置中添加output字段,指定打包好的文件放置路径和文件名,默认放在./dist文件夹,文件名默认为bundle.js,

webpack.config.js配置文件中:

1
2
3
4
5
6
7
8
9
const path = require('path');//引入node.js的path模块

module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'), //路径
filename: 'my-first-webpack.bundle.js' //文件名
}
};

loader

loader的引入,可以webpack把那些非js文件也当成模块一样处理,简单来说,就是我们的项目中不止有js文件还会有.css,.html.vue等文件,我们需要引入对应的loader来让webpack能够处理这些文件,并把它们添加到依赖图中去。

loader用在配置文件中的module字段,这里的module是一个对象,使用loader需要对module对象的rules属性进行设置,rules是一个数组,可以在里面进行多个loader配置,rules中的每一项都是一个有两个属性的对象:

  • test — 正则表达式,匹配需要处理的文件
  • run — 对应的loader名

webpack.config.js配置文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//如果我们想要处理.txt的文件。
//当然我们就需要通过npm安装raw-loader

const path = require('path');

const config = {
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
};

module.exports = config;

插件

当我们需要执行特殊任务的时候,就会用到插件,也可以理解成在webpack中引入了一些包,用来让webpack执行特定的任务。

  1. 通过npm安装插件
  2. 通过require()导入插件
  3. 在配置中的plugins字段中new一个插件实例使用它, plugins是个数组,所以可以有多个插件

webpack.config.js配置文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件

const config = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};

module.exports = config;

Promise的基础知识

Promise相当于异步操作结果的占位符,异步操作返回一个Promise,比如:

1
2
let promise = readFile("example.txt");
//readFile承诺将在未来的某个时刻完成

接下来就是对promise进行处理

Promise的生命周期

每个Promise都有一个生命周期,整个生命周期一共有两个状态:进行中(pending)和已处理(settled)。其中settled会有两种可选状态:成功(fulfilled)和失败(rejected)。

用上面的例子来举例,readFile函数返回一个Promise给promise变量,这是还未开始读取文件,Promise就处于pending状态。读取文件结束后Promise就会进入到settled状态中的其中一种状态。

  • Fulfilled Promise异步操作成功完成
  • Rejected 由于程序错误或一些其他原因,Promise异步操作未能成功完成

Promise状态改变时通过调用then()方法和catch()方法来传递相应的处理函数;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
promise.then(function(contents){
//处理完成的数据
}, function(err){
//拒绝
});

//未处理失败情况
promise.then(function(contents){
console.log(contents);
});

//未处理成功情况
promise.then(null, function(err){
console.log(err);
});

//处理失败情况
promise.catch(function(err){
console.log(err);
});

then()方法和catch方法一起使用能更好的处理异步操作结果,这套体系能够清楚地指明操作结果是成功还是失败。

Promise构造函数

构造函数接受一个参数,这个参数叫做Promise的执行器,执行器是一个函数,这个函数传入两个参数,一个resolve()函数,一个reject()函数

示例代码来自书本《深入理解ES6》promise部分

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
let fs = require("fs");

function readFile(filename){
return new Promise(function(resolve, reject){

//触发异步操作
fs.readFile(filname, {encoding: "utf8"}, function(err, contents){

//检查是否有错误
if(err){
reject(err);
return;
}

//成功读取文件
resolve(contents);
});
});
}

let promise = readFile("example.txt");

promise.then(function(contents){
//完成
console.log(contents);
}, function(err){
//拒绝
console.log(err.message);
});

Promise.resolve()和Promise.reject()

这个函数接受一个参数:

  • 参数为一个确定的值,返回一个只能处理fulfilled状态或rejected状态的Promise;
  • 参数为一个带有then方法的对象,返回一个含有这个then方法的Promise;
  • 参数为一个Promise,不做处理,直接返回这个Promise;
1
2
3
4
5
let promise1 = Promise.resolve(12); //只能处理完成状态

promise1.then(function(value){
console.log(value); //12
});

串联Promise

一个Promise被处理了之后会接着返回另一个Promise,就行串联一样,可以一直处理下来,并且上一级的Promise还能返回值给接下来的Promise处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let p1 = new Promise(function(resolve, reject){
resolve(12);
});

p1.then(function(value){

console.log(value); //12

return value - 2; //传递给下一个Promise

}).then(function(value2){

console.log(value2); //10

throw new Error("Boom!"); //抛出错误

}).catch(function(err){

console.log(err.message); //"Boom!
});

小贴士:务必在Promise链的末尾留一个拒绝处理函数以确保能够正确处理所有可能发生的错误。

被返回的值也可以是一个Promise,这时接下来的函数就是处理这个Promise。

响应多个Promise

使用Promise.all()和Promise.race()两个方法来监听多个Promise。

Promise.all()

方法接受一个参数,这个参数是个含有多个受监视Promise的可迭代对象(比如:数组,Set,Map,字符串);返回一个Promise。

只有当参数中所有Promise都被解决,返回的Promise才会被解决。传入所有Promise的完成值数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let p1 = new Promise(function(resolve, reject){
resolve(200);
});
let p2 = new Promise(function(resolve, reject){
resolve(201);
});
let p3 = new Promise(function(resolve, reject){
resolve(202);
});

let p4 = Promise.all([p1,p2,p3]);

p4.then(function(valueArr){
console.log(Array.isArray(valueArr)); //true
console.log(valueArr[0]); //200
console.log(valueArr[1]); //201
console.log(valueArr[2]); //202
});

当转入Promise中有一个被拒绝,则返回的Promise被拒绝。

1
2
3
4
5
6
7
8
9
10
let p5 = new Promise(function(resolve, reject){
reject(404);
});

let p6 = Promise.all([p1,p2,p3,p5]);

p6.then(function(value){
console.log(Array.isArray(value)); //false
console.log(value); //404
});

Promise.race()

这个方法与Promise.all()方法接受的参数相同,也返回一个Promise。但是这个方法是,只要传入Promise中有一个被解决,返回的Promise状态就为被解决。

实际上,传给Promise.race()方法的Promise会进行竞选,以决出哪个最先被解决,如果最先被解决的是已完成Promise,则返回已完成Promise;如果先解决的是已拒绝的Promise,则返回已拒绝Promise。

1
2
3
4
5
6
let p7 = Promise.race([p1,p2,p3]);

p7.then(function(value){
例子中没有真正的异步,在真实异步操作过程中,这里的value值是最先解决的Promise接收到的值;
console.log(value); //200
});

NPM是什么

npm是Node.js标准的软件包管理器。使用npm可以将npm仓库里的软件包下载到自己的项目中使用,是前端开发的重要工具。使用npm的前提是安装Node.js.

首先我们要了解一个重要的文件:

package.json

该文件用来管理项目中的所有依赖,npm可以根据package.json中的配置来下载项目所需的所有依赖。是项目的清单。

举个例子,当我们打包项目文件想要发布的时候,并不需要将项目所有的依赖库一起打包,因为这些库都是npm仓库里有的,我们只需要有一个package.json文件,里面有项目依赖包的名称,版本信息等。当其他人下载我们的项目文件后,就可以通过package.json文件重新下载依赖。

package-lock.json

该文件有当前项目中安装的依赖包的具体信息。

node_modules文件夹

用npm下载的依赖包都会放在这个文件夹里,一般打包项目文件时,都会自动忽略这个文件夹,因为可以用package.json里面的信息重新下载。

NPM用法

安装所有依赖

如果项目中有package.json文件,则通过运行:

1
npm install

它会在node_modules文件夹(如果不存在则会创建)中安装项目所需的所有东西。

安装单个软件包

运行下列命令安装单个需要的包:

1
npm install <package-name>

可选参数:

  • –save 安装并添加条目到package.json文件的dependencies;
  • –save-dev 安装并添加条目到package.json文件的devDependencies;
  • -g 安装到全局环境中,而不是安装到当前文件夹,安装某个框架时经常使用。

更新软件包

1
2
npm update
npm update <package-name>

npm会根据package.json和package-lock.json两个文件对比计算出当前可更新版本,并执行更新。这两条命令不会更新主版本。举个例子,不会将vue2更新到vue3

可以通过下面的命令:

1
npm outdated

来查看软件包的新版本。

若想要所有软件包更新到新的主版本,则需要全局地安装npm-check-updates软件包:

1
npm install -g  npm-check-updates

然后运行:

1
ncu -u

这会升级package.json文件和dependencies和devDependencies中地所有版本,以便npm可以安装到新的主版本。这时再运行:

1
npm update

版本控制

npm可以管理版本控制,有些时候一个库和其他库的不同版本兼容性不一样,为了是开发不造成混乱,npm遵循语义版本控制标准

运行任务

如果我们在项目中安装了可以独立运行的库,比如mocha

可以使用:

1
npm run <task-name>

的形式运行单个任务,此时任务入口在package.json的scripts字段内;

Ajax

Ajax是Asynchronous JavaScript and XML的缩写,即为异步的JavaScript和XML,是不刷新页面的情况下,客户端和服务端也能进行异步通信的一种技术。比如强大的Google map 就是采用了这个技术。

在ES6之前,js中的Ajax的基本流程可以概括为:

  1. 实例化一个XMLHttpRequest对象。
  2. 调用实例的open()方法,配置请求的url,必要的请求参数,请求方法。
  3. 调用实例的send()方法,向服务器发送请求。
  4. 处理响应,当请求状态readyState变化是会调用onreadystatechange方法,所以用实例的onreadystatechange属性指定处理响应的回调函数。

其中的readyState在整个请求的过程有五个状态:一旦readyState改变,就会调用onreadychange方法

0:未初始化的。尚未调用open方法。

1:启动。已经调用open方法,但还未调用send方法。

2:发送。已经调用send方法,尚未收到响应。

3:接收。已经收到部分响应。

4:完成。已经接收到全部响应,并且可以在客户端使用了。这一状态是我们最关注的。

具体的代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function () {
//只关注状态4
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
alert("request was unsuccessful" + xhr.status);
}
}
}
xhr.open('GET', 'https://autumnfish.cn/artist/top/song?id=6452');
xhr.send();

其中还有许多地方需要考虑浏览器的兼容性问题。

什么是作用域?

作用域就是一套规则,用于确定在何处及如何查找变量(标识符)。

js中,函数可以创建作用域;用letconst关键字配合{}可以创建块作用域

用函数创建作用域

1
2
3
4
5
6
7
var a = 1;
function foo(){
var a = 2;
console.log("a = " + a);
}
foo(); //a = 2
console.log(a); //1

函数foo里面新创建的变量a处于foo的作用域里,所以不影响外界的a变量

letconst创建块作用域

1
2
3
4
if(true){
let bar = 3;
}
console.log(bar) //ReferenceError

修改npm源为淘宝源

npm config set registry http://registry.npm.taobao.org/

查看当前源地址

npm config get registry

重置为官方源

npm config set registry https://registry.npmjs.org/

...语法一般用于三种情况:

  1. ​ 对于不确定参数数量的函数
1
2
3
4
5
6
function sum(...num)
{
return num.reduce((cur, next) => cur + next);
}
console.log(1, 2, 3);
//expected output: 6
  1. 快速创建数组, ...运算符可以将任意可枚举的对象转换为数组
1
2
3
4
5
6
7
8
9
function example()
{
return [...arguments];
}
console.log(example(a,b,c));
//expected output: ['a','b','c']
//对string也适用
console.log([...'hello']);
//expected output: ['h','e','l','l','o'];
  1. 合并数组
1
2
3
var all = [1, ...[2,3], 4, 5];
console.log(all);
//expected output: [1,2,3,4,5]

所以,我对...运算符粗浅的看做未知数量的可迭代对象。

示例:这个代码是根据传入的字符串返回它的摩斯密码形式

使用...

1
2
3
4
5
6
7
8
9
var uniqueMorseRepresentations = function (words) {
var mosmap = [".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."]
var str = words.map(res => {
var s = ''
res.split('').map(r => {s += mosmap[r.charCodeAt() - 97]})
return s
})
return [...new Set(str)].length //就是这里看不懂
};

未使用...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var uniqueMorseRepresentations = function(words) {
const morseCode = [".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."];
let map = {};
let num = 0;
let morse = '';
for(let i = 0; i < words.length; i++)
{
for(let j = 0; j < words[i].length; j++)
{
morse += morseCode[words[i].charCodeAt(j) - 97];
}

console.log(morse);
if(!map[morse])
{
map[morse] = 1;
num++;
}
morse = '';
}
return num;
}

习题2-1 水仙花数(daffodil)

输出100~999中的所有水仙花数。若3位数ABC满足ABC=A3+B3+C3,则称其为水仙花 数。例如153=13+53+33,所以153是水仙花数。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int main()
{
for(int i = 111; i <= 999; i++)
{
int a = i / 100;
int b = i /10 % 10;
int c = i % 10;
int s = a*a*a + b*b*b + c*c*c;
if(s == i) printf("%d\n", i);
}
return 0;
}

习题2-2 韩信点兵(hanxin)

相传韩信才智过人,从不直接清点自己军队的人数,只要让士兵先后以三人一排、五人 一排、七人一排地变换队形,而他每次只掠一眼队伍的排尾就知道总人数了。输入包含多组 数据,每组数据包含3个非负整数a,b,c,表示每种队形排尾的人数(a<3,b<5,c< 7),输出总人数的最小值(或报告无解)。已知总人数不小于10,不超过100。输入到文件 结束为止。
样例输入:
2 1 6
2 1 3
样例输出:
Case 1: 41
Case 2: No answer

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
#include <stdio.h>
#define LOCAL
int main()
{
#ifdef LOCAL
freopen("data.in","r", stdin);
freopen("data.out", "w", stdout);
#endif
int i,a, b, c;
int kase = 0;
while(scanf("%d %d %d", &a,&b,&c) != EOF)
{
// printf("a = %d b = %d c = %d", a, b, c);
for(i = 10; i<=100; i++)
{
if(i % 3 == a && i % 5 == b && i % 7 == c)
{
if(kase) printf("\n");
printf("Case %d: %d\n", ++kase,i);
break;
}
}
if(i >= 101)
{
if(kase) printf("\n");
printf("Case %d: No answer\n", ++kase);
}
}
return 0;
}

习题2-3 倒三角形(triangle)

输入正整数n≤20,输出一个n层的倒三角形。例如,n=5时输出如下:

#########

#######

#####

​ ###

​ #

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
#include <stdio.h>
int main()
{
int n,tag = 0;
scanf("%d", &n);
for(int i = n; i > 0; i--)
{
int kase = 2*(i-1)+1;
for(int j = 0; j < tag; j++)
{
printf(" ");
}
//打印空格高级一点的写法
/* for(int k = 0; k < n-i; k++)
{
printf(" ");
}
*/
while(kase)
{
printf("#");
kase--;
}
printf("\n");
tag++;
}
}

习题2-4 子序列的和(subsequence)

输入两个正整数n<m<106,输出 ,保留5位小数。输入包含多组数据, 结束标记为n=m=0。提示:本题有陷阱。
样例输入:
2 4
65536 655360
0 0
样例输出:
Case 1: 0.42361
Case 2: 0.00001

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#define LOCAL
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
freopen("data.out","w", stdout);
#endif
int k = 0;
int n, m;
double s;
while(scanf("%d %d", &n, &m) != EOF && n && m)
{
s = 0; //s要清零
for(int i = n; i <= m; i++)
{
//s += (1.0/(i*i)); //乘法溢出
s += (1.0/i/i);
}
if(k) printf("\n");
printf("Case %d: %.5f\n",++k,s);
}
return 0;
}

习题2-5 分数化小数(decimal)

输入正整数a,b,c,输出a/b的小数形式,精确到小数点后c位。a,b≤106,c≤100。输 入包含多组数据,结束标记为a=b=c=0。
样例输入:
1 6 4
0 0 0
样例输出:
Case 1: 0.1667

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int main()
{
FILE *fin, *fout;
fin = fopen("data.in","rb");
fout = fopen("data.out","wb");
int a, b, c, kase = 0;
while(fscanf(fin,"%d %d %d",&a, &b, &c) != EOF && a && b && c)
{
double d = (double)a/b;
if(kase) fprintf(fout,"\n");
fprintf(fout,"Case %d: %.*lf\n", ++kase, c, d);
//用%.*lf控制输出长度,小数点后面的*表示输出位数,具体数字来自参数表
}
fclose(fin);
fclose(fout);
return 0;
}

习题2-6 排列(permutation)

用1,2,3,…,9组成3个三位数abcdefghi,每个数字恰好使用一次,要 求abc:def:ghi=1:2:3。按照“abc def ghi”的格式输出所有解,每行一个解。提示:不必 太动脑筋。

没有思路

算法比赛中的输入输出框架

标准输入输出

标准输入输出就是控制台输入输出,在c语言中使用scanf()printf() 两个函数即可实现;

文件重定向输入输出

文件的重定向需要用到函数freopen() , 在mian 函数的入口处使用该函数即可,一般的写法为:

1
2
3
4
5
6
int main()
{
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
}
// 程序最后的输入输出分别在data.in 和data.out 两个文件里

搭配使用#define LOCAL

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#define LOCAL
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif
return 0;
}

这种写法的效果是,当源文件开头没有写#define LOCAL 这一句的时候,#ifdef#endif 之间的代码将不被编译,所以使得重定向的方法可以在本地测试时使用

文件输入输出

文件输入输出涉及到FILE* 的使用,但是我们并不用关系为什么,可以直接使用,一般的写法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#define INF 100000000
int main()
{
FILE *fin, *fout;
fin = fopen("data.in","rb"); //打开文件
fout = fopen("data.out","wb"); //打开文件
int x, n = 0, min = INF, max = -INF, s = 0;
while(fscanf(fin, "%d", &x) == 1) //这里的scanf变成了fscanf,并且多了一个参数fin
{
s += x;
if(x < min) min = x;
if(x > max) max = x;
n++;
}
fprintf(fout,"%d %d %.3f\n", min, max, (double)s/n);
//这里的printf变成了fprintf,并且多了一个参数fout
fclose(fin); //关闭文件
fclose(fout);//关闭文件
return 0;
}

文件输入输出很容易切换成标准输入输出,只需要将 fin = stdin;fout = out;即可,不要调用fopen()fclose()

注意:如果使用fopen("con","r")的方式打开标准输入输出,这种方法并不是可移植的—在Linux下无效

关于前端学习的一些总结

心路历程

现在是一名大二下的学生,从这学期开始,就定了入前端门的目标,到现在为止自己已经零零碎碎学了些前端的知识,在知乎 上看到一个回答 Web前端怎样入门?推荐百度前端学院的课程,就屁颠屁颠跑来了,可是今年迟迟没有开课,等得我那是一个焦急啊,现在终于等到开课了,想要重新制定一个计划,不仅是总结之前自己零散学到的知识,也希望通过这个平台能够有一个更好的开始。

接下来我想怎么继续学习

  • 在百度前端学院学习,只是一个契机,在学习过程中应该要扩充很多其他的知识,在github上star了一个项目(developer-roadmap),我觉得非常好,也尝试按照这上面的各个阶段来学习,但是之前没有认真对待,所以现在想要做出一些改变,就从这里开始。

  • 学到一个知识点的时候,就要动起手来,打代码,写个demo之类的,并且认真写笔记,我之前也通过hexo搭了一个个人博客,记录自己的一些笔记,但是我以前是个懒鬼(我也想改变),就写了几篇笔记。现在觉得每天要应该把自己学到的东西进行总结,有时候往往自己觉得懂了,但是可能只是看懂了,并不能运用,而通过写博客相当于把这些东西讲给别人听,这也是对自己的一个小测试

  • 在学习过程中摆脱 “学生气”,不要等什么都弄懂了才开始写代码,学习是一个并不轻松的过程,如果自己太舒服了,那可能真的没在学习,多练习才是真理,代码要多打才能提升。

  • 之前数据结构与算法没有学好,所以会把这个给补回来,并且尝试在LeetCode上刷题,一天一道吧现在,为了提高自己做题的兴趣,我是开始从通过率高的题做起(不想一来就把自己难的喘不过气),兴趣和坚持下去才是最重要的。

  • 多看优秀代码,有时候多思考别人为什么这样写,可能会有意外收获,遇到问题多尝试自己解决。

  • 在我的学习过程中,一直都没做过什么能称得上是个项目的东西,所以我打算自己从个小demo做起,尝试自己从零开始搭个网站,从后端到前端,然后慢慢往里头加东西,希望自己能坚持下来。

  • 作为一个CS专业的学生,基础还是非常重要的,我马上要步入大三了,时间越来越紧,也还有很多课程还没学,比如操作系统,编译原理,计算机网络等,所以得加油才是。

关于我看过的一些书籍

  • 深入理解计算机系统(CS:APP):对应了计算机组成原理这门课程,非常好的书,但是我还没看完,这是一本需要慢慢领悟的书

  • javascript高级程序设计:我学习js看的书,还没看完,以前真是的什么都想学,啥也学不好

  • 算法(第四版):我用来学数据结构与算法的,Coursera上有配套的视频,简直不能再好了,可是我(就是个弟弟)

结语

新的开始,新的气象,既然要做些改变,那就要行动起来,每天坚持写笔记,把自己从生活的舒适区拉出来,进入学习的状态。

学习过程中的方法可能会变化,但是我会定期检验自己是不是有提高,希望自己能坚持下来

希望我能在这个平台结识一些一起学习的小伙伴

语言组织能力不是很好,我会多写笔记提升自己的

最后,谢谢各位看到我笔记的同学,能够强忍我这粗糙的语言把这个看完