海屿文章列表

Express入门笔记

通过 npm init 命令为你的应用创建一个 package.json 文件
$ npm install express --save //安装Express并将其添加到package以来列表
$ node app.js //启动应用

//引入静态文件(html、css、js)
app.use(express.static('public'));

//项目依赖
var express = require('express');
var app = express();
//监听端口
var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

基本路由

app.METHOD(path, [callback...], callback), app 是 express 对象的一个实例, METHOD 是一个 HTTP 请求方法, path 是服务器上的路径, callback 是当路由匹配时要执行的函数。
1.路由方法:路由方法源于 HTTP 请求方法,和 express 实例相关联。
2.路由路径:路由路径和请求方法一起定义了请求的端点,它可以是字符串、字符串模式或者正则表达式。
3.路由句柄:可以为请求处理提供多个回调函数,其行为类似 中间件。唯一的区别是这些回调函数有可能调用 next('route') 方法而略过其他路由回调函数。可以利用该机制为路由定义前提条件,如果在现有路径上继续执行没有意义,则可将控制权交给剩下的路径。路由句柄有多种形式,可以是一个函数、一个函数数组,或者是两者混合。

响应方法:res.send,res.download(),res.end()等等

// 对网站首页的访问返回 "Hello World!" 字样
app.get('/', function (req, res) {
  res.send('Hello World!');
});
// 网站首页接受 POST 请求
app.post('/', function (req, res) {
  res.send('Got a POST request');
});

理解HTTP

HTTP就是客户端与服务端通讯的一种协议。客户端发送一个请求,服务端响应一个请求,就完成了一次通信。
先来看看客户端和服务端都说什么了吧:

HTTP报文

HTTP请求

http.png

HTTP响应
http响应 .png

HTTP方法

通过HTTP方法我们能告诉服务器我们的意图。

GET方法
GET方法用来请求已经被URI标识的资源,服务器解析后就把资源响应给我们。
GET就是用于信息获取,而且应该是安全的和幂等的。它仅仅是获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。幂等的意思是对同一URL的多个请求应该返回同样的结果。
因为GET请求发送的就是URI,所以它可以被缓存或者存为书签,并且它的长度不能超过URI的长度,即最大2048个字符。
安全性:与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。在发送密码或其他敏感信息时绝不要使用 GET !

POST方法
POST方法用于向指定的资源提交要被处理的数据
POST也是请求,不过它表示可能修改变服务器上的资源的请求。
由于POST发送的消息在内容主体里,所以他不能作为书签,也不能被缓存。优点是他更安全,而且长度没有限制。

HEAD方法
HEAD方法与GET方法一样,只是获得报文头部,用于确认资源URI或者资源更新的日期。

PUT/DELEAT方法
PUT/DELEAT用来上传/删除文件,但是由于这两种方法都不带验证机制,所以一般我们不会使用这两种方法。

OPTIONS方法:用来询问服务器支持什么方法

CONNECT方法:通过代理服务器实现与目标的加密通信。

TRACE方法:回显服务器收到的请求,主要用于测试或诊断。

URI

对于URL我们肯定听说过,那URI是什么?先来看看定义:
URI的定义是:统一资源标识符
URL的定义是:统一资源定位符

URI 标识一个事物 , URL 定位一个事物;然而,位置同样可以标识一个事物,所以,每个URL都是一个 URI,但一个 URI 并不一定是一个 URL。

状态码
状态码就是用以表示网页服务器HTTP响应状态的3位数字代码。一共分为五类:1xx消息,2xx成功,3xx重定向,4xx客户端错误,5xx服务器错误。只要记住200是成功,其他随用随查,查几遍就记住了。

js数组操作总结

1.Array.isArray()
用来判断某个值是否为数组。如果是,则返回 true,否则返回 false。

// 下面的函数调用都返回 true
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
// 鲜为人知的事实:其实 Array.prototype 也是一个数组。
Array.isArray(Array.prototype); 

2.concat()
concat 方法将创建一个新的数组,然后将调用它的对象(this 指向的对象)中的元素以及所有参数中的数组类型的参数中的元素以及非数组类型的参数本身按照顺序放入这个新数组,并返回该数组.

3.every()
every 方法为数组中的每个元素执行一次 callback 函数,直到它找到一个使 callback 返回 falsy(表示可转换为布尔值 false 的值)的元素。如果发现了一个这样的元素,every 方法将会立即返回 false。否则,callback 为每一个元素返回 true,every 就会返回 true。callback 只会为那些已经被赋值的索引调用。不会为那些被删除或从来没被赋值的索引调用。

callback 被调用时传入三个参数:元素值,元素的索引,原数组。

4.filter()
filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或 等价于 true 的值 的元素创建一个新数组。callback 只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过 callback 测试的元素会被跳过,不会被包含在新数组中。

callback 被调用时传入三个参数:元素的值,元素的索引,被遍历的数组。

5.forEach()
forEach 方法按升序为数组中含有效值的每一项执行一次callback 函数,那些已删除(使用delete方法等情况)或者从未赋值的项将被跳过(但不包括哪些值为 undefined 的项)。forEach 不像 every 和 some,它总是返回 undefined。

callback 函数会被依次传入三个参数:数组当前项的值,数组当前项的索引,数组对象本身。

6.indexOf()
indexOf()方法返回给定元素能找在数组中找到的第一个索引值,否则返回-1。

indexOf 接受两个参数:要查找的元素,开始查找的位置(可选)。

7.join()
join() 方法将数组中的所有元素连接成一个字符串。

join只接受一个可选参数,用于指定连接每个数组元素的分隔符。分隔符会被转成字符串类型;如果省略的话,默认为一个逗号。如果是一个空字符串,那么数组中的所有元素将被直接连接。

8.lastIndexOf()
lastIndexOf() 方法返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。

lastIndexOf() 方法接受两个参数:被查找的元素,开始查找的位置(可选,默认为数组的长度减 1,即整个数组都被查找。如果该值大于或等于数组的长度,则整个数组会被查找。如果为负值,将其视为从数组末尾向前的偏移。即使该值为负,数组仍然会被从后向前查找。如果该值为负时,其绝对值大于数组长度,则方法返回 -1,即数组不会被查找)。

lastIndexOf 使用严格相等(===)比较 searchElement 和数组中的元素。

var array = [2, 5, 9, 2];
var index = array.lastIndexOf(2);// index is 3
index = array.lastIndexOf(7);// index is -1
index = array.lastIndexOf(2, 3);// index is 3
index = array.lastIndexOf(2, -1);// index is 3

9.map()
map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值组合起来形成一个新数组。 callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。注意:数组元素的范围在 callback 方法第一次调用之前就已经确定。

callback 函数会被自动传入三个参数:数组元素,元素索引,原数组本身。

10.pop()/push()
pop() 方法删除一个数组中的最后的一个元素,并且返回这个元素。
push() 方法添加一个或多个元素到数组的末尾,并返回数组新的长度(length 属性值)。

pop/push 方法有意具有通用性。该方法和 call() 或 apply() 一起使用时,可应用在类似数组的对象上。push 方法根据 length 属性来决定从哪里开始插入给定的值。如果 length 不能被转成一个数值,则插入的元素索引为 0,包括 length 不存在时。当 length 不存在时,将会创建它。

唯一的原生类数组(array-like)对象是 Strings,尽管如此,它们并不适用该方法,因为字符串是不可改变的。

11.reduce()/reduceRight
reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素
reduceRight() 方法接受一个函数作为累加器,让每个值(从右到左,亦即从尾到头)缩减为一个值。(与 reduce() 的执行方向相反)

reduce 接受四个参数和一个可选参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组,第一次调用的初始值(可选)

回调函数第一次执行时,初始值和当前值可以是一个值,如果设置了第一次调用的初始值,初始值等于当前值是一个值,并且等于数组中的第一个值;如果没有设置了第一次调用的初始值,那么初始值等于数组中的第一个值,当前值等于数组中的第二个值。
如果数组为空并且没有提供第一个初始值, 会抛出TypeError 。如果数组仅有一个元素(无论位置如何)并且没有提供第一个初始值, 或者有提供第一个初始值但是数组为空,那么此唯一值将被返回并且callback不会被执行。

[0,1,2,3,4].reduce(function(previousValue, currentValue, index, array){
  return previousValue + currentValue;
});
//第一次 初始值0 当前值1 返回1
//第二次 初始值1(来自第一次的返回值) 当前值2 返回3
//以此类推 最终返回10

var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
    return a.concat(b);
});
// 数组扁平化 返回[0, 1, 2, 3, 4, 5]

12.reverse()
reverse() 方法颠倒数组中元素的位置。

13.shift()/unshift()
shift 方法移除索引为 0 的元素(即第一个元素),并返回被移除的元素,其他元素的索引值随之减 1。如果 length 属性的值为 0 (长度为 0),则返回 undefined。
unshift() 方法在数组的开头添加一个或者多个元素,并返回数组新的 length 值。

shift/unshift 方法并不局限于数组:该方法亦可通过 call 或 apply 作用于对象上。对于不包含 length 属性的对象,将添加一个值为 0 的 length 属性。

14.slice()
slice() 方法把数组中一部分的浅复制(shallow copy)存入一个新的数组对象中,并返回这个新的数组。

slice 接受两个参数:开始提取索引,结束提取索引

如果该开始/结束索引为负数,则表示从原数组中的倒数第几个元素开始/结束提取,slice(-2)表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素),slice(-2,-1)表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素),默认 slice 从索引 0 开始,到末尾结束.

slice 不修改原数组,只会返回一个包含了原数组中提取的部分元素的一个新数组。原数组的元素会按照下述规则被拷贝("一级深拷贝"规则):如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则改变将反应到新的和原来的数组中。
对于字符串和数字来说(不是 String 和 Number 对象),slice 会拷贝字符串和数字到新的数组里。在一个数组里修改这些字符串或数字,不会影响另一个数组。
如果向两个数组任一中添加了新元素,则另一个不会受到影响。

15.toLocaleString()/toString()
toLocaleString() 返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 ",")隔开。
toString() 返回一个字符串,表示指定的数组及其元素。

关于复制对象

复制对象有深复制和浅复制,有什么区别?

  • 浅复制:把数据的地址赋值给对应变量,而没有把具体的数据复制给变量,只随这个地址上的数据变化而变化。
  • 深复制:把数据赋值给对应的变量,并给这个变量开辟一个新的地址

理解浅复制

var a = 1; var b = {s1:2,s2:3}; var c=[0,1,2,3]; var d = {s1:2,s2:3}
var a1 = a; var b1= b; var c1=c; var d1=d;
console.log(a1,b1,c1,d1); //1 s1:2,s2:3 [0,1,2,3] //s1:2,s2:3
a=11; b1.s1=22; c[3]=33; d={x1:2,x2:3};
console.log(a1,b1,c1,d1); //1 s1:22,s2:3 [0,1,2,33] //s1:2,s2:3

对象赋值其实都是引用传值,传递的是一个地址。对于a和d我们给它传递了一个新地址,但是a1和d1还是指向的原来那个地址,原来地址中的值没变,所以a1和d1也就不会变。浅复制不会随着存储数据地址的变化而变化,只会随着数据值的变化而变化。
还是弄不清除?举个例子吧:在考场里有个表,所有人看时间都要去找这个表,这个表时间变了(数据值变了)所有人得到的时间也都随之而变。

理解深复制

还是上边的例子,如果把时间深复制给你就相当于给你戴个手表,你手表的时间就与考场的表无关了,你可以随便调你手表(开辟了一个新地址存储时间)的时间而不会影响其他人得到的时间。
深复制时间需要戴个手表,那么深复制数据我们也需要借助一些工具,比如jQuery。

jQuery的$.extend()方法实现深复制

var x = {
    a: 1,
    b: { f: { g: 1 } },
    c: [ 1, 2, 3 ]
};
var y = $.extend({}, x),          //浅复制
    z = $.extend(true, {}, x);    //深复制

y.b.f === x.b.f       // true
z.b.f === x.b.f       // false

$.extend作用是合并两个或多个对象,它可以接受第一个参数true,合并成为递归(深拷贝)

关于遍历

1.for

var list = [1, 2, 3, 4, 5, ...... 100000000];
for(var i = 0, l = list.length; i < l; i++) {
    console.log(list[i]);
}

for可以说是最标准的循环遍历数组的方法了,不过在使用时注意缓存length(上面代码中的l = list.length),如果写成for(var i = 0; i < list.length; i++)那么每次遍历都要额外计算一次length的值。

2.forEach()

function logArrayElements(element, index, array) {
    console.log("a[" + index + "] = " + element);
}
[2, 5, 9].forEach(logArrayElements);
// logs:
// a[0] = 2
// a[1] = 5
// a[2] = 9

forEach() 方法让数组的每一项都执行一次给定的函数。回调函数接受三个参数currentValue:当前项(指遍历时正在被处理那个数组项)的值,index:当前项的索引(或下标),array:数组本身。
注意:使用forEach()是没法终止或跳出循环的,除非抛出异常。

3.every()/some()

function isBigEnough(element, index, array) {
  return (element >= 10);
}
var passed = [12, 5, 8, 130, 44].every(isBigEnough); // passed is false
passed = [12, 54, 18, 130, 44].every(isBigEnough);  // passed is true
passed = [12, 54, 8, 130, 44].some(isBigEnough);  // passed is true

every 方法为数组中的每个元素执行一次 callback 函数,直到它找到一个使 callback 返回 falsy(表示可转换为布尔值 false 的值)的元素。如果发现了一个这样的元素,every 方法将会立即返回 false。否则,callback 为每一个元素返回 true,every 就会返回 true,而some正好与之相反。

4.for..in
for...in 用来循环遍历可枚举属性(包括原型链上的属性),循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作。
语法:
for (变量 in 对象){ //do something }

Object.prototype.bar = 0;
var obj = {a:1, b:2, c:3};
    for (var prop in obj) {
      console.log("obj." + prop + " = " + obj[prop]);
    }
    for(var i in obj) {
       if (obj.hasOwnProperty(i)) {
       console.log(i);}
    }
    // Output:
    // "obj.a = 1"
    // "obj.b = 2"
    // "obj.c = 3"
    // "obj.bar = 0"
    // a b c

由于for..in会遍历原型链上的属性,我们可以使用 hasOwnProperty 过滤(hasOwnProperty 判断一个对象是否包含自定义属性而不是原型链上的属性)。

5.for..of
之前我们用for遍历数组,其是我们只是遍历的是数组下标,用下标来指向值。在ES6中增加了for..of语法,它能直接遍历数组的值。目前这个只有Firefox支持。

var arr = [ 3, 5, 7 ];
arr.foo = "hello";
for (var i in arr) {
   console.log(i); //  "0", "1", "2", "foo"
}
for (var j of arr) {
   console.log(j); //  "3", "5", "7"
}