auven / blog Goto Github PK
View Code? Open in Web Editor NEW泛泛而谈
泛泛而谈
其实js的面向对象非常灵活,比起静态语法来,达到异想天开的效果。
在很多语言中,类--->对象。
而在js中,对象不依赖与类而存在。这是第一个灵活的地方(先有鸡还是先有蛋?)
js的对象只是一个“属性字典”,就像php中的关联数组
因此,我们可以直接造对象,不要类
var ori = {}; // 地球上最原始的蛋白质,这就是一个对象
var c = {cell: 1}; // 单细胞动物
var chicken = { leg: 2, song: function () { alert('我是一只小小鸟'); } };
console.log(ori, c, chicken);
chicken.song(); // 我是一只小小鸟
通过上面的例子,可以看出,js中的对象,不依赖于类而存在,可以直接生成的。
以{k:v,k:v}这种格式声明的对象,称为json格式的对象。
chicken.wine = 2; // 增加2条翅膀
console.log(chicken.wine);
// 这只小鸡受到弹弓击打,不会唱歌了!
delete chicken.song;
console.log(chicken);
总结,js中的对象,就是“一组属性与值的集合”,
属性可以任意增减,
方法和属性不必区分。
通过类造的对象,所有的都一个模板,比如 能哭,会笑
如果类改变了,所有对象的方法都改变了,
这也不符合生物的规律
因为,对象是一个以个体为单位的,cat1,cat2,一个会爬树,一个不会
这个java,php中如何实现?
在js中,就很自然
cat1 = {climb: function(爬树)}
cat2 = {} 没有爬树属性
<button id="btn"></button>
<script src="jquery-2.1.1.min.js"></script>
<script>
var i = 0;
$('#btn').on('click', function () {
i++;
setTimeout(function () {
i = 0;
}, 500);
if (i > 1) {
alert('这是双击');
i = 0;
}
})
</script>
Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.
单线程,单线程的好处,减少了内存开销,操作系统的内存换页。
如果某一个事情,进入了,但是被I/O阻塞了,所以这个线程就阻塞了。
非阻塞I/O, 不会傻等I/O语句结束,而会执行后面的语句。
非阻塞就能解决问题了么?比如执行着小红的业务,执行过程中,小刚的I/O回调完成了,此时怎么办??
事件机制,事件环,不管是新用户的请求,还是老用户的I/O完成,都将以事件方式加入事件环,等待调度。
说是三个特点,实际上是一个特点,离开谁都不行,都玩儿不转了。
Node.js很像抠门的餐厅老板,只聘请1个服务员,服务很多人。结果,比很多服务员效率还高。
Node.js中所有的I/O都是异步的,回调函数,套回调函数。
善于I/O,不善于计算。因为Node.js最擅长的就是任务调度,如果你的业务有很多的CPU计算,实际上也相当于这个计算阻塞了这个单线程,就不适合Node开发。
当应用程序需要处理大量并发的I/O,而在向客户端发出响应之前,应用程序内部并不需要进行非常复杂的处理的时候,Node.js非常适合。Node.js也非常适合与web socket配合,开发长连接的实时交互应用程序。
比如:
Node.js本是就是极客追求性能极致的产物,缺少了很多服务器的健壮考量。所以Node不可能应用在银行、证券、电信等需要极高可靠性的业务中。
**的企业实战中,创业型公司(正处于A轮、B轮)非常爱使用Node做核心业务:
成熟大企业,基本上都是用Node实现某一方面的功能:
Node不是银弹,就是你工具箱中的一个小工具而已。
安装:打开安装包直接安装
添加环境变量:
windows下,一般会自动添加环境变量,装完即用。如果没有,请手动添加环境变量。点开我的电脑-->右键选择属性-->打开系统信息面板后,选择左边"高级系统设置"-->点击环境变量-->将node.js安装路径添加到Path中。
mac下一般也是自动添加了环境变量,如果没有,自行百度添加环境变量的教程。
测试是否安装成功:在控制台输入node -v
输出版本号,表示安装成功了。
node [路径/]文件名.js
推荐,不要使用完整的路径名,而是通过先进入案例文件夹,然后node文件相对地址。
Node.js是服务器的程序,写的js语句,都将运行在服务器上。返回给客户的,都是已经处理好的纯html。
//require表示引包,引包就是引用自己的一个特殊功能
var http = require("http");
//创建服务器,参数是一个回调函数,表示如果有请求进来,要做什么
var server = http.createServer(function (req, res) {
//req表示请求,request; res表示响应,response
//设置HTTP头部,状态码是200,文件类型是html,字符集是utf8
res.writeHead(200, {"Content-type": "text/html;charset=UTF-8"});
res.end("哈哈哈哈,我买了一个iPhone" + (1 + 2 + 3) + "s");
});
//运行服务器,监听3000端口(端口号可以任改)
server.listen(3000, "127.0.0.1");
如果想修改程序,必须中断当前运行的服务器,重新node一次,刷新,才行。
ctrl+c,就可以打断挂起的服务器程序。此时按上箭头,能够快速调用最近的node命令。
你会发现,我们本地写一个js,打死都不能直接拖入浏览器运行,但是有了node,我们任何一个js文件,都可以通过node来运行。也就是说,node就是一个js的执行环境。
我们现在,要跑起来一个服务器,这个服务器的脚本,要以.js存储。是一个js文件。用node命令运行这个js文件罢了。
注意:Node.js没有根目录的概念,因为它根本没有任何的web容器!
让node.js提供一个静态服务,都非常难!
也就是说,node.js中,如果看见一个网址是127.0.0.1:3000/fang
,
别再去想,一定有一个文件夹,叫做fang了。可能/fang的物理文件,是同目录的test.html。
URL和真实物理文件,是没有关系的。URL是通过了Node的顶层路由设计,呈递某一个静态文件的。
这个案例是个演示,不用研究。说明Node.js没有web容器的概念,呈递的静态文件和URL没有任何关系。
访问127.0.0.1/fang实际显示的是test文件夹中的xixi.html页面,
访问127.0.0.1/yuan实际显示的是test文件夹中的haha.html页面,
相应的图片,都要有自己的路由。
//require表示引包,引包就是引用自己的一个特殊功能
var http = require("http");
var fs = require("fs");
//创建服务器,参数是一个回调函数,表示如果有请求进来,要做什么
var server = http.createServer(function (req, res) {
if (req.url == "/fang") {
fs.readFile("./test/xixi.html", function (err, data) {
//req表示请求,request; res表示响应,response
//设置HTTP头部,状态码是200,文件类型是html,字符集是utf8
res.writeHead(200, {"Content-type": "text/html;charset=UTF-8"});
res.end(data);
});
} else if (req.url == "/yuan") {
fs.readFile("./test/haha.html", function (err, data) {
//req表示请求,request; res表示响应,response
//设置HTTP头部,状态码是200,文件类型是html,字符集是utf8
res.writeHead(200, {"Content-type": "text/html;charset=UTF-8"});
res.end(data);
});
} else if (req.url == "/0.jpg") {
fs.readFile("./test/0.jpg", function (err, data) {
//req表示请求,request; res表示响应,response
//设置HTTP头部,状态码是200,文件类型是html,字符集是utf8
res.writeHead(200, {"Content-type": "image/jpg"});
res.end(data);
});
} else if (req.url == "/bbbbbb.css") {
fs.readFile("./test/aaaaaa.css", function (err, data) {
//req表示请求,request; res表示响应,response
//设置HTTP头部,状态码是200,文件类型是html,字符集是utf8
res.writeHead(200, {"Content-type": "text/css"});
res.end(data);
});
} else {
res.writeHead(404, {"Content-type": "text/html;charset=UTF-8"});
res.end("嘻嘻,没有这个页面呦");
}
});
//运行服务器,监听3000端口(端口号可以任改)
server.listen(3000, "127.0.0.1");
//这个案例简单讲解http模块
//引用模块
var http = require("http");
//创建一个服务器,回调函数表示接收到请求之后做的事情
var server = http.createServer(function (req, res) {
//req参数表示请求,res表示响应
console.log("服务器接收到了请求" + req.url);
//设置头部
res.writeHead(200, {"Content-Type": "text/html;charset=UTF8"});
res.write("<h1>我是主标题</h1>");
res.write("<h2>我是2标题</h2>");
res.write("<h2>我是2标题</h2>");
res.write("<h2>我是2标题</h2>");
res.write("<h3>我是3标题</h3>");
//必须添加end()方法,不然网页会一直处于加载而没法停止完成
res.end((1 + 2 + 3).toString());
});
//监听端口
server.listen(3000, "127.0.0.1");
var http = require("http");
var server = http.createServer(function (req, res) {
console.log(req.url); // 控制台中输出 "/"、"/favicon.ico"
res.end();
});
server.listen(3000, "127.0.0.1");
var http = require("http");
var url = require("url");
var server = http.createServer(function (req, res) {
//url.parse()可以将一个完整的URL地址,分为很多部分:
//host、port、pathname、path、query
var pathname = url.parse(req.url).pathname;
//url.parse()如果第二个参数是true,那么就可以将所有的查询变为对象
/*
* 这里是通过 querystring 模块的 parse() 方法生成一个对象。
* 如果为 false,则返回的 URL 对象上的 query 属性会是一个未解析、未解码的字符串。
* 默认为 false。
*/
//就可以直接打点得到这个参数
var query1 = url.parse(req.url).query;
var query2 = url.parse(req.url, true).query;
//直接打点得到这个参数
var age = query2.age;
//访问http://localhost:3000/abc?name=auven&age=123
console.log("pathname:" + pathname); // 输出/abc
console.log("query1:" + query1); // 输出 name=auven&age=123
// 这里的JSON.stringify()是讲对象转换为字符串,才能在控制台正常输出
console.log("query2:" + JSON.stringify(query2)); // 输出 {"name":"auven","age":"123"}
console.log("age:" + age); // 输出 123
res.end();
});
server.listen(3000, "127.0.0.1");
字符串查询,用querystring处理
querystring.parse('foo=bar&baz=qux&baz=quux&corge')
// returns
{ foo: 'bar', baz: ['qux', 'quux'], corge: '' }
// Suppose gbkDecodeURIComponent function already exists,
// it can decode `gbk` encoding string
querystring.parse('w=%D6%D0%CE%C4&foo=bar', null, null,
{ decodeURIComponent: gbkDecodeURIComponent })
// returns
{ w: '中文', foo: 'bar' }
// 当用户访问/student/1234567890 的查询此学号的学生信息。
// 当用户方位/teacher/645433 的时候,查询此老师的信息
// 其他的,我们提示错误。如果位数不对,也是提示位数不对
var http = require("http");
var server = http.createServer(function (req, res) {
//得到url
var userurl = req.url;
res.writeHead(200, {"Content-Type": "text/html;charset=UTF8"})
//substr函数来判断此时的开头
if (userurl.substr(0, 9) == "/student/") {
var studentid = userurl.substr(9);
console.log(studentid);
if (/^\d{10}$/.test(studentid)) {
res.end("您要查询学生信息,id为" + studentid);
} else {
res.end("学生学号位数不对");
}
} else if (userurl.substr(0, 9) == "/teacher/") {
var teacherid = userurl.substr(9);
if (/^\d{6}$/.test(teacherid)) {
res.end("您要查询老师信息,id为" + teacherid);
} else {
res.end("老师学号位数不对");
}
} else {
res.end("请检查url");
}
});
server.listen(3000, "127.0.0.1");
var http = require("http");
var fs = require("fs");
var server = http.createServer(function (req, res) {
//不处理小图标
if (req.url == "/favicon.ico") {
return;
}
//给用户加一个五位数的id
var userid = parseInt(Math.random() * 89999) + 10000;
console.log("欢迎" + userid);
res.writeHead(200, {"Content-Type": "text/html;charset=UTF8"});
//两个参数,第一个是完整路径,当前目录写./
//第二个参数,就是回调函数,表示文件读取成功之后,做的事情
fs.readFile("./test/1.txt", function (err, data) {
if (err) {
throw err;
}
console.log(userid + "文件读取完毕");
res.end(data);
});
});
server.listen(3000);
var http = require("http");
var fs = require("fs");
var server = http.createServer(function (req, res) {
//不处理小图标
if (req.url == "/favicon.ico") {
return;
}
fs.mkdir("./album/aaa");
res.end("success");
});
server.listen(3000);
var http = require("http");
var fs = require("fs");
var server = http.createServer(function (req, res) {
//不处理小图标
if (req.url == "/favicon.ico") {
return;
}
//stat检测状态,这个文件或文件夹必须存在,不然data.isDirectory()会报错
fs.stat("./album/bbb", function (err, data) {
//检测这个路径,是不是一个文件夹
console.log(data.isDirectory());
});
});
server.listen(3000, "127.0.0.1");
var http = require("http");
var fs = require("fs");
var server = http.createServer(function (req, res) {
//不处理小图标
if (req.url == "/favicon.ico") {
return;
}
//存储所有的文件夹
var wenjianjia = [];
//stat检测状态
fs.readdir("./album", function (err, files) {
//files是个文件名的数组,并不是文件的数组,表示./album这个文件夹中的所有东西
//包括文件、文件夹
for (var i = 0; i < files.length; i++) {
var thefilename = files[i];
//又要进行一次检测
fs.stat("./album/" + thefilename, function (err, stats) {
//如果他是一个文件夹,那么输出它:
if (stats.isDirectory()) {
wenjianjia.push(thefilename);
}
console.log(wenjianjia); // 输出为空数组,以及重复
});
}
});
});
server.listen(3000, "127.0.0.1");
var http = require("http");
var fs = require("fs");
var server = http.createServer(function (req, res) {
//不处理收藏夹小图标
if (req.url == "/favicon.ico") {
return;
}
//遍历album里面的所有文件、文件夹
fs.readdir("./album/", function (err, files) {
//files : ["0.jpg","1.jpg" ……,"aaa","bbb"];
//files是一个存放文件(夹)名的数组
//存放文件夹的数组
var wenjianjia = [];
//迭代器就是强行把异步的函数,变成同步的函数
//1做完了,再做2;2做完了,再做3
(function iterator(i) {
//遍历结束
if (i == files.length) {
console.log(wenjianjia);
return;
}
fs.stat("./album/" + files[i], function (err, stats) {
//检测成功之后做的事情
if (stats.isDirectory()) {
//如果是文件夹,那么放入数组。不是,什么也不做。
wenjianjia.push(files[i]);
}
iterator(i + 1);
});
})(0);
});
res.end();
});
server.listen(3000, "127.0.0.1");
var http = require("http");
var fs = require("fs");
var url = require("url");
var path = require("path");
var server = http.createServer(function(req,res){
//这里如果不用req.url来if判断,那么用户不管输入什么网址,
//做的事情都一样啊
//得到地址
var pathname = url.parse(req.url).pathname;
//判断此时用户输入的地址是文件夹地址还是文件地址
//如果是文件夹地址,那么自动请求这个文件夹中的index.html
if(pathname.indexOf(".") == -1){
pathname += "/index.html";
}
//输入的网址是127.0.0.1/images/logo.png
//实际请求的是./static/images/logo.png
var fileURL = "./" + path.normalize("./static/" + pathname);
//得到拓展名
var extname = path.extname(pathname);
//把文件读出来
fs.readFile(fileURL,function(err,data){
//读完之后做的事情
if(err){
//文件不存在
res.writeHead(404,{"Content-Type":"text/html;charset=UTF8"})
res.end("404,页面没有找到");
}
//读完之后做的事情
getMime(extname,function(mime){
res.writeHead(200,{"Content-Type":mime})
res.end(data);
});
});
});
server.listen(80,"127.0.0.1");
function getMime(extname,callback){
//读取mime.json文件,得到JSON,根据extname key ,返回对应的value
//读取文件
fs.readFile("./mime.json",function(err,data){
if(err){
throw Error("找不到mime.json文件!");
return;
}
//转成JSON
var mimeJSON = JSON.parse(data);
var mime = mimeJSON[extname] || "text/plain";
//执行回调函数,mime类型字符串,就是它的参数
callback(mime);
});
}
exports.aaa = aaa
foo.js
var bar = require("./bar.js");
var msg = "你好";
var info = "呵呵";
function showInfo(){
console.log(info);
}
exports.msg = msg;
exports.info = info;
exports.showInfo = showInfo;
引用foo.js
var foo = require("./test/foo.js");
console.log(foo.msg);
console.log(foo.info);
foo.showInfo();
module.exports = People
People.js
function People(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
People.prototype = {
sayHello: function () {
console.log(this.name + this.sex + this.age);
}
}
//此时,People就被视为构造函数,可以用new来实例化了。
module.exports = People;
使用People.js
var People = require("./test/People.js");
var xiaoming = new People("小明","男","12");
xiaoming.sayHello();
var foo = require("foo.js"); //没有写./
console.log(foo.msg);
首先,文件夹中应有package.json文件,指定入口js
{
"name": "kaoladebar",
"version": "1.0.1",
"main" : "app.js"
}
引用
var bar = require("bar"); //在引用一个文件夹
console.log(bar.msg);
router.js
exports.showIndex = showIndex;
exports.showStudent = showStudent;
exports.show404 = show404;
function showIndex(req, res) {
res.writeHead(200, {"Content-Type": "text/html;charset=UTF8"});
res.end("我是首页");
}
function showStudent(req, res) {
var id = req.url.substr(9, 6);
res.writeHead(200, {"Content-Type": "text/html;charset=UTF8"});
res.end("我是学生页面" + id);
}
function show404(req, res) {
res.writeHead(404, {"Content-Type": "text/html;charset=UTF8"});
res.end("404");
}
引用
var http = require("http");
var router = require("./router.js");
//创建服务器
var server = http.createServer(function (req, res) {
if (req.url == "/") {
router.showIndex(req, res);
} else if (req.url.substr(0, 9) == "/student/") {
router.showStudent(req, res);
} else {
router.show404(req, res);
}
});
server.listen(80, "127.0.0.1");
利用nodejs中的 http.ServerRequest
中获取 :
request.method
用来标识请求类型request.headers
获得请求头
content-type
包含了表单类型和边界字符串信息
application/x-www-form-urlencoded
这种就是一般的文本表单用post传地数据,只要将得到的data用querystring
解析下就可以了multipart/form-data
文件表单的传输,也是本文介绍的重点content-length
post数据的长度请求头示例
{ host: 'localhost:3000',
connection: 'keep-alive',
'content-length': '674',
'cache-control': 'max-age=0',
origin: 'http://localhost:3000',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36',
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryWnRGByNEWIBy6qgY',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
referer: 'http://localhost:3000/',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.8',
cookie: 'Webstorm-fee3e9e1=fce3dd18-c9e8-47e9-adf5-db4f59ffcdea; _ga=GA1.1.178463939.1491578109' }
application/x-www-form-urlencoded
提交form.html
<form action="dopost" method="post">
<p>
姓名 : <input type="text" name="name">
</p>
<p>
性别 :
<input type="radio" name="sex" value="男">男
<input type="radio" name="sex" value="女">女
</p>
<p>
爱好:
<input type="checkbox" name="hobby" value="睡觉"/>睡觉
<input type="checkbox" name="hobby" value="吃饭"/>吃饭
<input type="checkbox" name="hobby" value="足球"/>足球
</p>
<p>
<input type="submit"/>
</p>
</form>
post.js处理post提交
var http = require("http");
var querystring = require("querystring");
var fs = require('fs');
/创建服务器
var server = http.createServer(function(req,res){
//把文件读出来
fs.readFile('./form.html',function(err,data){
//读完之后做的事情
if(err){
//文件不存在
res.writeHead(404,{"Content-Type":"text/html;charset=UTF8"})
res.end("404,页面没有找到");
}
//读完之后做的事情
res.end(data);
});
//如果你的访问地址是这个,并且请求类型是post
if(req.url == "/dopost" && req.method.toLowerCase() == "post"){
var alldata = "";
//下面是post请求接收的一个公式
//node为了追求极致,它是一个小段一个小段接收的。
//接受了一小段,可能就给别人去服务了。防止一个过大的表单阻塞了整个进程
req.addListener("data",function(chunk){
alldata += chunk;
});
//全部传输完毕
req.addListener("end",function(){
var datastring = alldata.toString();
res.end("success");
//将datastring转为一个对象
var dataObj = querystring.parse(datastring);
console.log(req.headers);
console.log(alldata);
console.log(datastring);
console.log(dataObj);
console.log(dataObj.name);
console.log(dataObj.sex);
console.log(dataObj.hobby);
});
}
});
server.listen(3000,"127.0.0.1");
formidable
处理post提交安装formidable
npm install formidable --save
form.html
<form action="dopost" method="post" enctype="multipart/form-data">
<p>
姓名 : <input type="text" name="name">
</p>
<p>
性别 :
<input type="radio" name="sex" value="男">男
<input type="radio" name="sex" value="女">女
</p>
<p>
爱好:
<input type="checkbox" name="hobby" value="睡觉"/>睡觉
<input type="checkbox" name="hobby" value="吃饭"/>吃饭
<input type="checkbox" name="hobby" value="足球"/>足球
</p>
<p>
图片:
<input type="file" name="tupian"/>
</p>
<p>
<input type="submit"/>
</p>
</form>
post.js处理post
var http = require("http");
var formidable = require('formidable');
var util = require("util");
var fs = require('fs');
//创建服务器
var server = http.createServer(function (req, res) {
//如果你的访问地址是这个,并且请求类型是post
if (req.url == "/dopost" && req.method.toLowerCase() == "post") {
//Creates a new incoming form.
var form = new formidable.IncomingForm();
//设置文件上传存放地址
form.uploadDir = "./uploads";
//执行里面的回调函数的时候,表单已经全部接收完毕了。
form.parse(req, function (err, fields, files) {
if (err) {
throw err;
}
console.log(fields);
console.log(files);
console.log(util.inspect({fields: fields, files: files}));
//所有的文本域、单选框,都在fields存放;
//所有的文件域,files
res.writeHead(200, {'content-type': 'text/plain;charset=UTF8'});
res.end("成功");
});
} else if (req.url == "/") {
//呈递form.html页面
fs.readFile("./form1.html", function (err, data) {
res.writeHead(200, {'content-type': 'text/html'});
res.end(data);
})
} else {
res.writeHead(404, {'content-type': 'text/html'});
res.end("404");
}
});
server.listen(3000, "127.0.0.1");
控制台输出内容
//console.log(fields);
{ name: '张剑', sex: '男', hobby: '足球' }
//console.log(files);
{ tupian:
File {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
size: 11983,
path: 'uploads/upload_f77f8e5444e3b296006b76a4b35e2ffa',
name: '2.jpg',
type: 'image/jpeg',
hash: null,
lastModifiedDate: 2017-04-18T02:24:11.857Z,
_writeStream:
WriteStream {
_writableState: [Object],
writable: false,
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
path: 'uploads/upload_f77f8e5444e3b296006b76a4b35e2ffa',
fd: null,
flags: 'w',
mode: 438,
start: undefined,
autoClose: true,
pos: undefined,
bytesWritten: 11983,
closed: true } } }
//console.log(util.inspect({fields: fields, files: files}));
{ fields: { name: '张剑', sex: '男', hobby: '足球' },
files:
{ tupian:
File {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
size: 11983,
path: 'uploads/upload_f77f8e5444e3b296006b76a4b35e2ffa',
name: '2.jpg',
type: 'image/jpeg',
hash: null,
lastModifiedDate: 2017-04-18T02:24:11.857Z,
_writeStream: [Object] } } }
path
模块提供了一些工具函数,用于处理文件与目录的路径path.extname()
方法返回 path
的扩展名,即从 path
的最后一部分中的最后一个 .
(句号)字符到字符串结束。 如果 path
的最后一部分没有 .
或 path
的文件名(见 path.basename()
)的第一个字符是 .,则返回一个空字符串。
path.extname('index.html')
// 返回: '.html'
path.extname('index.coffee.md')
// 返回: '.md'
path.extname('index.')
// 返回: '.'
path.extname('index')
// 返回: ''
path.extname('.index')
// 返回: ''
util
模块主要用于支持 Node.js 内部 API 的需求。 大部分实用工具也可用于应用程序与模块开发者var ejs = require("ejs");
//模板
var string = "好高兴啊,今天我买了iphone<%= a %>s";
//数据
var data = {
a : 6
};
//数据绑定
var html = ejs.render(string, data);
//输出
console.log(html);
var ejs = require("ejs");
var fs = require("fs");
var http = require("http");
var server = http.createServer(function (req, res) {
fs.readFile("./views/index.ejs", function (err, data) {
//绑定模板
var template = data.toString();
//传递参数
var dictionary = {
a: 6,
news: [
{"title": "陈伟我爱你", "count": 10},
{"title": "哈哈哈哈", "count": 20},
{"title": "逗你玩儿的", "count": 30}
]
};
var html = ejs.render(template, dictionary);
//显示
res.writeHead(200, {"Content-Type": "text/html;charset=UTF8"});
res.end(html);
});
});
server.listen(3000);
npm install express --save
常用方法:
var express = require("express");
var app = express();
// 这里use之后,底下的所有路由都将失效,所有页面在这里就有加载完成,无法进入底下的路由
// app.use(function (req, res) {
// res.send("123456789");
// });
app.get("/", function (req, res) {
res.send("你好");
});
app.get("/haha", function (req, res) {
res.send("这是haha页面,哈哈哈哈哈哈");
});
//无视大小写,? #
app.get("/AAb", function (req, res) {
res.send("你好");
});
// 冒号
app.get("/student/:id", function (req, res) {
var id = req.params["id"];
var reg = /^[\d]{6}$/;
if (reg.test(id)) {
res.send(id);
} else {
res.send("请检查格式");
}
});
//冒号
app.get("/:username/:oid", function (req, res) {
var username = req.params["username"];
var oid = req.params["oid"];
res.write(username);
res.end(oid);
});
app.listen(3000);
如果想处理这个网址的任何method的请求,那么写app.all。
所有的GET参数,? 后面的都已经被忽略。 锚点#也被忽略。你路由到/a
, 实际/a?id=2&sex=nan
也能被处理。
req.params
类数组对象
var express = require("express");
var app = express();
// 通过localhost:3000就可以访问到
app.use(express.static("./public"));
app.get("/haha", function (req, res) {
res.send("haha ");
});
app.listen(3000);
可以看到,之前用nodejs原生来配置静态资源,需要引用fs模块,而且配置起来特别麻烦。但在这里,我们不需要引用fs,因为express内部已经帮我们集成好了。搭建一个静态资源,非常的简单方便!!直接express.static('文件夹')
,然后use
就可以了。
而且,我们还能指定静态文件的路由,app.use('/static', express.static("./public"))
,然后通过localhost/static/
就可以访问到
默认从views文件夹中去寻找模板文件
res.render()
,将会根据views中的模板文件进行渲染。如果不想使用views文件夹,想自己设置文件夹名字,那么app.set("views","aaaa");
res.send()
。这个函数将根据内容,自动帮我们设置了Content-Type
头部和200
状态码。send()
只能用一次,和end
一样。和end
不一样在哪里?能够自动设置MIME
类型。res.status(404).send('Sorry, we cannot find that!');
res.set('Content-Type', 'text/html');
var express = require("express");
var app = express();
// 设置模板引擎
app.set("view engine", "ejs");
// res.render中的'haha',不指定路径时,默认从views文件夹中去寻找haha.ejs文件
app.get("/", function (req, res) {
res.render("haha", {
"news": ["我是小新闻啊", "我也是啊", "哈哈哈哈"]
});
});
app.listen(3000);
body-parser
这个中间件能够处理一些简单的post请求,但是处理不了上传文件。所以这里只需了解一下,平时用得比较多的还是formidable
form.ejs
<form action="#" method="post">
<input type="text" name="name"/>
<input type="text" name="age"/>
<input type="submit"/>
</form>
post.js
var express = require("express");
var bodyParser = require('body-parser');
var app = express();
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({extended: false}))
// parse application/json
app.use(bodyParser.json())
//设置模板引擎
app.set("view engine", "ejs");
app.get("/", function (req, res) {
res.render("form");
});
app.post("/", function (req, res) {
console.log(req.body); // { name: 'dfds', age: 'sgdfg' }
//将数据添加进入数据库
res.send("成功");
});
app.listen(3000);
这里表单自己post到自己。同一个网址,get和post的路由
路由get、post这些东西,就是中间件,中间件讲究顺序,匹配上第一个之后,就不会往后匹配了。next函数才能够继续往后匹配。
中间件的顺序很重要,next()
var express = require("express");
var app = express();
app.get("/:username/:id", function (req, res, next) {
// var username = req.params.username;
// res.send("用户信息: " + username);
next(); // 试着把next注释掉,把上面取消注释,访问/admin/login,看页面显示
});
// 这里的不会被执行,除非上面next()
app.get("/admin/login", function (req, res) {
console.log("2");
res.send("管理员登录");
});
app.listen(3000);
use()
模块var express = require("express");
var app = express();
//当你不写路径的时候,实际上就相当于"/",就是所有网址
app.use(function (req, res, next) {
console.log(new Date());
next();
});
app.get("/haha", function (req, res) {
console.log("哈哈");
});
app.use("/admin", function (req, res) {
// 设置返回的headers,'Content-Type'默认帮我们设置为utf8,我们可加可不加
res.set({'aaa': 'utf8', 'Content-Type': 'text/plain'});
// 访问http://localhost:3000/admin/login
res.write(req.originalUrl + "\n"); // 输出/admin/login
res.write(req.baseUrl + "\n"); // 输出/admin
res.write(req.path + "\n"); // 输出/login
res.end("你好");
});
app.listen(3000);
其中,res.set()
设置返回头。
app.use()自己做一个静态资源服务,原生fs
var express = require("express");
var fs = require("fs");
var app = express();
//当你不写路径的时候,实际上就相当于"/",就是所有网址
app.use(haha);
app.get("/admin", function (req, res) {
res.send("管理员");
})
app.listen(3000);
function haha(req, res, next) {
var filePath = req.originalUrl;
//根据当前的网址,读取public文件夹中的文件
//如果有这个文件,那么渲染这个文件
//如果没有这个文件,那么next();
fs.readFile("./public/" + filePath, function (err, data) {
if (err) {
//文件不存在
next();
return;
}
res.send(data.toString());
});
}
app.use(express.static("./public"))
var express = require("express");
var app = express();
//静态服务
app.use("/jingtai", express.static("./public"));
//新的路由
app.get("/images", function (req, res) {
res.send("哈哈");
});
//会自动识别err参数,如果有,那么就这个函数能捕获err
app.use(function (req, res) {
res.status(404).send("没有这个页面!");
});
app.listen(3000);
req.query获得url携带的参数
var express = require("express");
var app = express();
app.get("/", function (req, res) {
// 访问http://localhost:3000/?aa=123&bb=456&bb=789
console.log(req.query); // 输出{ aa: '123', bb: [ '456', '789' ] }
res.send();
});
app.listen(3000);
非结构型数据库。没有行、列的概念。用JSON来存储数据。
集合就相当于“表”,文档就相当于“行”。
NoSQL数据库在以下的这几种情况下比较适用:
我们看,有些系统,特别需要筛选。比如,筛选出所有女生大于20岁的。那么SQL型数据库,非常擅长!因为它有行、列的概念。
但是,有些系统,真的不需要进行那么多的筛选,比如站内信。站内信只需要存储就好了。不需要筛选。那么NoSQL的。
NoSQL不是银弹,没有资格挑战老牌数据库,还是特定情况下,是适合的。
去官网找到对应的版本下载安装,然后将安装路径添加到环境变量中(重要)。
mongod
开机开机命令,启动数据库
mongod --dbpath 数据库文件路径
启动 mongodb,默认数据库目录即为 /data/db
。注意:如果你的数据库目录不是/data/db
,可以通过 --dbpath
来指定。(参考:菜鸟教程)
--dbpath
就是选择数据库文档所在的文件夹。
也就是说,mongoDB中,真的有物理文件,对应一个个数据库。U盘可以拷走。
一定要保持,开机这个CMD不能动了,不能关,不能ctrl+c
。 一旦这个cmd有问题了,数据库就自动关闭了。
注意:在mac下,开机必须加上sudo
,否则会开机失败。
mongo
使用数据库1)打开命令行工具
mongo
2)列出所有数据库:
show dbs
3)使用某个数据库
use 数据库名字
如果想新建数据库,也是use。use一个不存在的,就是新建。
4)查看当前所在数据库
db
show collections
6)插入数据
db.student.insert({"name": "小明", "age": 12, "sex": "nan"})
student就是所谓的集合。集合中存储着很多json。
student是第一次使用,集合将自动创建。
use一个新的数据库时,不会真的创建成功。如果真的想把这个数据库创建成功,那么必须插入一个数据。
数据库中不能直接插入数据,只能往集合(collections)中插入数据。不需要创建集合,只需要写点语法:
db.student.insert({“name”:”xiaoming”});
db.student
系统发现student是一个陌生的集合名字,所以就自动创建了集合。
7)删除数据库,删除当前所在的数据库
db.dropDatabase();
8)索引Index
数据库中,根据一个字段的值,来寻找一个文档,是很常见的操作。比如根据学号来找一个学生。
这个学号,是唯一的,只要有学号,就能唯一确认一个学生的文档。学号这个属性,就非常适合建立索引,这样一来,查找学生就变得简单了。
这个语句,能够查看检索的过程:
db.student.find({"name":"user888"});
学生的姓名是唯一的,为了快速的进行检索,所以就把name属性建立成为“索引”。
db.student.createIndex({"name":1});
这样,今后通过name寻找student文档的时候,速度非常快。因为能够快速的从索引表中,找到这个文档。
缺点就是插入每条数据的时候,时间变慢了,效率低了。但是换回来的就是寻找的速度快了。
索引这个属性,所有的文档都不能相同:
db.members.createIndex( { "user_id": 1 }, { unique: true } );
mongoimport
导入数据有时候,我们不可能一条一条的insert。所以,我们希望在外部写好数据库的形式,然后导入数据库:
mongoimport --db test --collection restaurants --drop --file primer-dataset.json
--db test
想往哪个数据库里面导入--collection restaurants
想往哪个集合中导入--drop
把集合清空--file primer-dataset.json
哪个文件这样,我们就能创建一个json文件,然后用mongoimport命令导入,这样学习数据库非常方便。
1)查找数据,用find
。find
中没有参数,那么将列出这个集合的所有文档:
db.restaurants.find()
2)精确匹配:
db.student.find({"score.shuxue":70});
3)多个条件:
db.student.find({"score.shuxue":70 , "age":12})
4)条件操作符:
$gt
$lt
$gte
$lte
db.student.find({"score.yuwen":{$gt:50}});
5)或者$or
:寻找所有年龄是9岁,或者11岁的学生
db.student.find({$or:[{"age":9},{"age":11}]});
6)查找完毕之后,打点调用sort,表示升降排序。其中 1 为升序排列,而-1是用于降序排列。
db.restaurants.find().sort( { "borough": 1, "address.zipcode": 1 } )
1)查找名字叫做小明的,把年龄更改为16岁:
db.student.update({"name":"小明"},{$set:{"age":16}});
2)查找数学成绩是70,把年龄更改为33岁:
db.student.update({"score.shuxue":70},{$set:{"age":33}});
3)更改所有匹配项目:
默认是只修改一条匹配数据,如果要修改所有匹配数据,则添加{multi: true}
db.student.update({"sex":"男"},{$set:{"age":33}},{multi: true});
4)完整替换,不出现$set关键字了:
db.student.update({"name":"小明"},{"name":"大明","age":16});
db.restaurants.remove( { "borough": "Manhattan" } )
默认是删除所有的匹配,如果只想删除一行,那么添加{ justOne: true }
db.restaurants.remove( { "borough": "Queens" }, { justOne: true } )
建议多查阅:官网nodejs驱动文档
npm install mongodb --save
1)每次访问127.0.0.1/都会往数据库插入一条数据
var express = require("express");
var app = express();
var MongoClient = require('mongodb').MongoClient;
app.get("/", function (req, res) {
//url就是数据库的地址。/表示数据库
//假如数据库不存在,没有关系,程序会帮你自动创建一个数据库
var url = 'mongodb://localhost:27017/haha';
//连接数据库
MongoClient.connect(url, function (err, db) {
//回调函数表示连接成功做的事情,db参数就是连接上的数据库实体
if (err) {
console.log("数据库连接失败");
return;
}
console.log("数据库连接成功");
//插入数据,集合如果不存在,也没有关系,程序会帮你创建
db.collection('student').insertOne({
"name": "哈哈",
"age": parseInt(Math.random() * 100 + 10)
}, function (err, result) {
if (err) {
console.log("插入失败");
return;
}
//插入之后做的事情,result表示插入结果。
//console.log(result);
res.send(result);
db.close();
});
});
});
app.listen(3000);
2)列出所有数据库中的数据
var express = require("express");
var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
var app = express();
app.set("view engine", "ejs");
//数据库连接的地址,最后的斜杠表示数据库名字
var shujukuURL = 'mongodb://localhost:27017/abc';
app.get("/", function (req, res) {
//先连接数据库,对数据库的所有操作,都要写在他的回调函数里面。
MongoClient.connect(shujukuURL, function (err, db) {
if (err) {
//res.write("数据库连接失败");
return;
}
//res.write("恭喜,数据库已经成功连接 \n");
//查询数据库,cursor游标,游标可以用each方法遍历
//每次表示一条document
var result = [];
var cursor = db.collection('teacher').find();
cursor.each(function (err, doc) {
if (err) {
//res.write("游标遍历错误");
return;
}
if (doc !== null) {
result.push(doc);
} else {
//console.log(result);
//遍历完毕
db.close();
res.render("index", {
"result": result
});
}
});
});
});
app.get("/add", function (req, res) {
res.render("add");
});
app.get("/tijiao", function (req, res) {
//得到参数
var name = req.query.name;
var age = req.query.age;
var yuwenchengji = req.query.yuwenchengji;
var shuxuechengji = req.query.shuxuechengji;
MongoClient.connect(shujukuURL, function (err, db) {
if (err) {
console.log("数据库连接失败");
return;
}
db.collection("teacher").insertOne({
"name": name,
"age": age,
"score": {
"shuxue": shuxuechengji,
"yuwen": yuwenchengji
}
}, function (err, result) {
if (err) {
console.log("数据库写入失败");
return;
}
res.send("恭喜,数据已经成功插入");
res.end();
//关闭数据库
db.close();
});
});
});
app.listen(3000);
开发DAO:J2EE开发人员使用数据访问对象(DAO)设计模式把底层的数据访问逻辑和高层的商务逻辑分开.实现DAO模式能够更加专注于编写数据访问代码.
使用我们自己的DAO模块,来实现数据库插入。代码变得简单。
db.js
//这个模块里面封装了所有对数据库的常用操作
var MongoClient = require('mongodb').MongoClient;
var settings = require("../settings.js");
//不管数据库什么操作,都是先连接数据库,所以我们可以把连接数据库
//封装成为内部函数
function _connectDB(callback) {
var url = settings.dburl; //从settings文件中,都数据库地址
//连接数据库
MongoClient.connect(url, function (err, db) {
if (err) {
callback(err, null);
return;
}
callback(err, db);
});
}
//插入数据
exports.insertOne = function (collectionName, json, callback) {
_connectDB(function (err, db) {
db.collection(collectionName).insertOne(json, function (err, result) {
callback(err, result);
db.close(); //关闭数据库
})
})
};
//查找数据,找到所有数据。args是个对象{"pageamount":10,"page":10}
exports.find = function (collectionName, json, C, D) {
var result = []; //结果数组
if (arguments.length == 3) {
//那么参数C就是callback,参数D没有传。
var callback = C;
var skipnumber = 0;
//数目限制
var limit = 0;
} else if (arguments.length == 4) {
var callback = D;
var args = C;
//应该省略的条数
var skipnumber = args.pageamount * (args.page-1) || 0;
//数目限制
var limit = args.pageamount || 0;
//排序方式
var sort = args.sort || {};
} else {
throw new Error("find函数的参数个数,必须是3个,或者4个。");
return;
}
//连接数据库,连接之后查找所有
_connectDB(function (err, db) {
var cursor = db.collection(collectionName).find(json).skip(skipnumber).limit(limit).sort(sort);
cursor.each(function (err, doc) {
if (err) {
callback(err, null);
db.close(); //关闭数据库
return;
}
if (doc != null) {
result.push(doc); //放入结果数组
} else {
//遍历结束,没有更多的文档了
callback(null, result);
db.close(); //关闭数据库
}
});
});
}
//删除
exports.deleteMany = function (collectionName, json, callback) {
_connectDB(function (err, db) {
//删除
db.collection(collectionName).deleteMany(
json,
function (err, results) {
callback(err, results);
db.close(); //关闭数据库
}
);
});
}
//修改
exports.updateMany = function (collectionName, json1, json2, callback) {
_connectDB(function (err, db) {
db.collection(collectionName).updateMany(
json1,
json2,
function (err, results) {
callback(err, results);
db.close();
});
})
}
exports.getAllCount = function (collectionName, callback) {
_connectDB(function (err, db) {
db.collection(collectionName).count({}).then(function (count) {
callback(count);
db.close();
});
})
}
settings.js
module.exports = {
"dburl": "mongodb://localhost:27017/haha"
}
index.ejs
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>留言本</title>
<link href="css/bootstrap.min.css" rel="stylesheet"/>
<style type="text/css">
#chenggong, #shibai {
display: none;
}
.liuyankuai {
padding: 10px 0;
border-bottom: 1px solid #ccc;
}
</style>
</head>
<body>
<h1>我的留言本</h1>
<div class="container">
<div class="row">
<form class="form-horizontal col-lg-6">
<div class="form-group">
<label for="xingming" class="col-sm-2 control-label">姓名</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="xingming" name="xingming" placeholder="姓名">
</div>
</div>
<div class="form-group">
<label for="liuyan" class="col-sm-2 control-label">留言</label>
<div class="col-sm-10">
<textarea class="form-control" rows="3" name="liuyan" id="liuyan"></textarea>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button id="tijiao" type="button" class="btn btn-success">提交</button>
</div>
</div>
<div class="alert alert-success" role="alert" id="chenggong">
<a href="#" class="alert-link">表单已经成功提交</a>
</div>
<div class="alert alert-danger" role="alert" id="shibai">
<a href="#" class="alert-link">表单提交失败</a>
</div>
</form>
</div>
<nav>
<ul class="pagination">
</ul>
</nav>
<div id="quanbuliuyan">
</div>
</div>
<script type="text/template" id="moban">
<div class="liuyankuai">
<p>【姓名】{{= xingming }}</p>
<p>【留言】{{= liuyan }}</p>
<p>【时间】{{= shijian }}</p>
<p><a href="/shanchu?id={{=id}}" class="shanchu">删除</a></p>
</div>
</script>
<script type="text/template" id="page">
<li class="yemaanniu" data-page="{{=i}}"><a href="#">{{=i}}</a></li>
</script>
<script src="js/jquery-1.11.3.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/underscore-noflect.js"></script>
<script type="text/javascript">
var nowpage = 1;
//默认请求第一页数据
getData(1);
//Ajax请求数据
function getData(page) {
$.get("/du?page=" + page, function (result) {
//这里接收是result,但是这个json里面有一个key叫做result。
//得到模板,弄成模板函数
var compiled = _.template($("#moban").html());
//清空全部留言中的所有节点
$("#quanbuliuyan").html("");
// alert(result.result);
for (var i = 0; i < result.result.length; i++) {
//数据绑定
var html = compiled({
liuyan: result.result[i].liuyan,
xingming: result.result[i].xingming,
shijian: result.result[i].shijian,
id: result.result[i]._id
});
//DOM操作,添加节点
$("#quanbuliuyan").append($(html));
}
});
}
//Ajax提交表单
$("#tijiao").click(function () {
$("#shibai").hide();
$("#chenggong").hide();
$.post("/tijiao", {
"xingming": $("#xingming").val(),
"liuyan": $("#liuyan").val()
}, function (result) {
if (result.result == -1) {
$("#shibai").fadeIn();
} else if (result.result == 1) {
//提交成功
$("#chenggong").fadeIn();
//数据库真的存储了,但是当前页面无法显示。这是因为需要刷新
//才能用ajax从/du中得到新的。所以我们先用一个假盒子凑出来。
// var compiled = _.template($("#moban").html());
// var html = compiled({liuyan: $("#liuyan").val(), xingming: $("#xingming").val(), shijian: new Date()});
// $(html).insertBefore($("#quanbuliuyan"));
getData(1);
getAllPage();
}
});
});
//Ajax请求数据
function getAllPage() {
$.get("/getAllPage", function (result) {
var compiled = _.template($("#page").html());
//清空全部留言中的所有节点
$(".pagination").html("");
// alert(result.result);
for (var i = 1; i <= result.result; i++) {
//数据绑定
var html = compiled({
i: i
});
//DOM操作,添加节点
$(".pagination").append($(html));
}
// alert(result.result);
//给第一个页面,补一个active
$(".yemaanniu:first").addClass("active");
//给所有的页码按钮,添加监听
$(".yemaanniu").click(function () {
nowpage = parseInt($(this).attr("data-page"));
//重新发起请求,即可
getData(nowpage);
$(this).addClass("active").siblings().removeClass("active");
});
});
}
getAllPage();
</script>
</body>
</html>
index.js
var express = require("express");
var app = express();
var db = require("./model/db.js");
var formidable = require('formidable');
var ObjectId = require('mongodb').ObjectID; // 可以将字符串转换为mongodb里的_id格式
//设置模板引擎
app.set("view engine", "ejs");
//静态
app.use(express.static("./public"));
//显示留言列表
app.get("/", function (req, res, next) {
res.render("index");
});
app.get("/getAllPage", function (req, res, next) {
db.getAllCount("liuyanben", function (count) {
res.json({"result": Math.ceil(count / 5)})
});
});
//读取所有留言,这个页面是供Ajax使用的
app.get("/du", function (req, res, next) {
//可以接受一个参数
var page = parseInt(req.query.page);
db.find("liuyanben", {}, {"sort": {"shijian": -1}, "pageamount": 5, "page": page}, function (err, result) {
// if(err){
// console.log('失败');
// }
// console.log(result);
res.json({"result": result});
});
});
//处理留言
app.post("/tijiao", function (req, res, next) {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields) {
//写入数据库
db.insertOne("liuyanben", {
"xingming": fields.xingming,
"liuyan": fields.liuyan,
"shijian": new Date()
}, function (err, result) {
if (err) {
res.send({"result": -1}); //-1是给Ajax看的
return;
}
res.json({"result": 1});
});
});
});
//删除
app.get("/shanchu", function (req, res, next) {
//得到参数
var id = req.query.id;
db.deleteMany("liuyanben", {"_id": ObjectId(id)}, function (err, result) {
res.redirect("/");
});
})
app.listen(3000);
HTTP是无状态协议。简单地说,当你浏览了一个页面,然后转到同一个网站的另一个页面,服务器无法认识到,这是同一个浏览器在访问同一个网站。每一次的访问,都是没有任何关系的。
那么世界就乱套了,比如我上一次访问,登陆了,下一次访问,又让我登陆,不存在登陆这事儿了。
Cookie是一个简单到爆的想法:当访问一个页面的时候,服务器在下行HTTP报文中,命令浏览器存储一个字符串;浏览器再访问同一个域的时候,将把这个字符串携带到上行HTTP请求中。
第一次访问一个服务器,不可能携带cookie。 必须是服务器得到这次请求,在下行响应报头中,携带cookie信息,此后每一次浏览器往这个服务器发出的请求,都会携带这个cookie。
特点:
res负责设置cookie, req负责识别cookie。
安装:
npm install cookie-parser --save
使用:
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
//使用cookie必须要使用cookie-parser中间件
app.use(cookieParser());
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
//使用cookie必须要使用cookie-parser中间件
app.use(cookieParser());
app.get("/", function (req, res) {
res.send("猜你喜欢" + req.cookies.mudidi);
});
//查询一个地方的攻略,URL语法: http://127.0.0.1/gonglue?mudidi=北京
//此时北京就能记录在cookie中
app.get("/gonglue", function (req, res) {
//得到get请求,用户查询的目的地
var mudidi = req.query.mudidi;
//console.log(mudidi);
//记录用户喜好
//先读取用户的喜好,然后把新的数据push进入数组,然后设置新的cookie
var mudidiarry = req.cookies.mudidi || [];
//console.log(mudidiarry);
mudidiarry.push(mudidi);
//maxAge在Express中以毫秒为单位
res.cookie("mudidi", mudidiarry, {maxAge: 900000, httpOnly: true});
res.send(mudidi + "旅游攻略");
});
app.listen(3000);
Session不是一个天生就有的技术,而是依赖cookie。
session依赖cookie,当一个浏览器禁用cookie的时候,登陆效果消失; 或者用户清除了cookie,登陆也消失。
session比cookie不一样在哪里呢? session下发的是乱码,并且服务器自己缓存一些东西,下次浏览器的请求带着乱码上来,此时与缓存进行比较,看看是谁。所以,一个乱码,可以对应无限大的数据。
任何语言中,session的使用,是“机理透明”的。他是帮你设置cookie的,但是足够方便,让你感觉不到这事儿和cookie有关。
安装:
npm install express-session --save
使用:
var express = require("express");
var app = express();
var session = require("express-session");
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}));
var express = require("express");
var app = express();
var session = require("express-session");
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}));
app.get("/", function (req, res) {
if (req.session.login == "1") {
res.send("欢迎" + req.session.username);
} else {
res.send("没有成功登陆");
}
});
app.get("/login", function (req, res) {
req.session.login = "1"; //设置这个session
req.session.username = "考拉";
res.send("你已经成功登陆");
});
app.listen(3000);
永远不要用明码写密码。
黑客拿到的用户的密码的加密信息,所以也没用。因为他无法翻译成为明码。
MD5加密是函数型加密。就是每次加密的结果一定相同,没有随机位。
特点:
MD5常用于作为版本校验。可以比对两个软件、文件是否完全一致。
node中自带了一个模块,叫做crypto模块,负责加密。
首先创建hash,然后update和digest:
var md5 = crypto.createHash('md5');
var password = md5.update(fields.password).digest('base64');
md5.js
var crypto = require("crypto");
module.exports = function(mingma){
var md5 = crypto.createHash('md5');
var password = md5.update(mingma).digest('base64');
return password;
}
使用brew
安装
brew
官网:https://brew.sh/index_zh-cn.html
先安装brew
:
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
安装libjpeg
,目录 /usr/local/Cellar/jpeg/8d
brew install libjpeg
安装jasper
,目录 /uer/local/Cellar/jasper/2.0.12
brew install jasper
安装libpng
,目录 /usr/local/Cellar/libpng/1.6.29
brew install libpng
安装freetype
,目录 /usr/local/Cellar/freetype/2.7.1
brew install freetype
安装zlib
,系统自带,不用安装
安装GraphicsMagick
brew install GraphicsMagick
测试是否安装成功
gm -version
只要服务器需要处理图片,那么这个服务器就要安装graphicsmagick软件。免费的。
装完之后,可视化工具一点用都没有,从桌面上删除。我们要把安装目录设置为环境变量。
//格式转换
gm convert a.bmp a.jpg
//更改当前目录下*.jpg的尺寸大小,并保存于目录.thumb里面
gm mogrify -resize 320x200 danny.jpg
安装:
npm install gm --save
使用:
var fs = require('fs');
var gm = require('gm');
gm('./danny.jpg')
.resize(50, 50,"!")
.write('./danny2.jpg', function (err) {
if (err) {
console.log(err);
}
});
gm("./danny.jpg").crop(141,96,152,181).write("./2.jpg",function(err){
//141 96 是宽高 。 152 181是坐标
});
mongoose是一个将JavaScript对象与数据库产生关系的一个框架,object related model。操作对象,就是操作数据库了;对象产生了,同时也持久化了。
这个思路是Java三大框架SSH中Hibernate框架的思路。彻底改变了人们使用数据库的方式。
npm install mongoose --save
解决报错:DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html
mongoose.Promise = global.Promise;
官网上的hello world
//引包,并不需要引用mongodb这个包
var mongoose = require('mongoose');
//链接数据库,haha是数据库名字
mongoose.connect('mongodb://localhost/haha');
//创建了一个模型。猫的模型。所有的猫,都有名字,是字符串。“类”。
var Cat = mongoose.model('Cat', { name: String });
//实例化一只猫
var kitty = new Cat({ name: 'Zildjian' });
//调用这只猫的save方法,保存这只猫
kitty.save(function (err) {
console.log('喵喵喵');
});
var tom = new Cat({"name":"汤姆"});
tom.save(function(){
console.log('喵喵喵');
});
上面的代码中,没有一个语句是明显的操作数据库,感觉都在创建类、实例化类、调用类的方法。都在操作对象,但是数据库同步被持久了。
mongoose的哲学,就是让你用操作对象的方式操作数据库。
创建一个模型
mongoose.model("Cat",{"name" : String , "age" : Integer});
就可以被实例化
var kitty = new Cat({ name: 'Zildjian' });
然后这个实例就可以被save。
公式
var mongoose = require('mongoose');
//创建数据库连接
var db = mongoose.createConnection('mongodb://127.0.0.1:27017/haha');
//监听open事件
db.once('open', function (callback) {
console.log("数据库成功连接");
});
创造schema → 定义一些schema的静态方法 → 创造模型
1)创造schema用什么语句?
new mongoose.schema({});
2)创造模型用什么语句?
db.model(“Student”,schema名字);
//创建了一个schema结构。
var studentSchema = new mongoose.Schema({
name : {type : String},
age : {type : Number},
sex : {type : String}
});
//创建静态方法
studentSchema.statics.zhaoren = function(name, callback) {
this.model('Student').find({name: name}, callback); //this.model('Student')指的是当前这个类
};
//创建修改的静态方法
studentSchema.statics.xiugai = function(conditions,update,options,callback){
this.model("Student").update(conditions, update, options, callback);
}
//创建了一个模型,就是学生模型,就是学生类。
//类是基于schema创建的。
var studentModel = db.model('Student', studentSchema);
解释一下,什么是静态方法,什么是类方法:
//类
function Student(){
}
//实例化一个学生
var xiaoming = new Student();
//实例方法,因为这个sleep方法的执行者是类的实例
xiaoming.sleep();
//静态方法(类方法),这个方法的执行者是这个类,不是这个类的实例。
Student.findAllBuJiGe();
statics
静态方法:
//创建静态方法
studentSchema.statics.zhaoren = function(name, callback) {
this.model('Student').find({name: name}, callback);
};
methods
实例方法:
animalSchema.methods.zhaotonglei = function(callback){
this.model('Animal').find({"type":this.type},callback);
}
数组元素的删除($pull
)、添加($addToSet
),将字符串转为ObjectId类型(mongoose.Types.ObjectId()
)
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var db = mongoose.connection;
db.once('open', function (callback) {
console.log("数据库成功打开");
});
//博客的结构
var blogSchema = new mongoose.Schema({
title: String,
author: String,
body: String,
comments: [{body: String, date: Date}]
});
//发表评论
blogSchema.methods.pinglun = function (obj, callback) {
this.comments.push(obj);
this.save();
}
var Blog = mongoose.model('Blog', blogSchema);
// var blog = new Blog({
// "title" : "哈哈哈",
// "author" : "考拉",
// "body" : "哈哈哈哈"
// });
//
// blog.save();
//寻找一个标题是哈哈哈的博客,然后发表评论
Blog.findOne({"title": "哈哈哈"}, function (err, blog) {
if (!blog) {
return;
}
blog.pinglun({"body": "通过_id来删除评论", "date": new Date()});
});
//删除评论
// Blog.update({"title":"哈哈哈"},{$pull:{"comments": {_id:"再来一个评论"}}},function(err){
// if(err){
// res.send(500);
// console.log(err);
// }
// });
// 通过_id来删除,字符串转不转ObjectId类型无所谓,均可删除
// Blog.update({"title":"哈哈哈"},{$pull:{"comments": {_id: "58f773e3e5261602f1fe821a"}}},function(err){
// if(err){
// res.send(500);
// console.log(err);
// }
// });
// 使用mongoose.Types.ObjectId()将字符串转为ObjectId类型
// Blog.update({"title":"哈哈哈"},{$pull:{"comments": {_id: mongoose.Types.ObjectId("58f7744754c0bc02f971a6fc")}}},function(err){
// if(err){
// res.send(500);
// console.log(err);
// }
// });
方法:
npm install socket.io
写原生的JS,搭建一个服务器,server创建好之后,创建一个io对象
var http = require("http");
var server = http.createServer(function(req,res){
res.end("你好");
});
var io = require('socket.io')(server);
//监听连接事件
io.on("connection",function(socket){
console.log("1个客户端连接了");
})
server.listen(3000,"127.0.0.1");
写完这句话之后,你就会发现,http://127.0.0.1:3000/socket.io/socket.io.js
就是一个js文件的地址了。
现在需要制作一个index页面,这个页面中,必须引用秘密js文件。调用io函数,取得socket对象。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>我是index页面,我引用了秘密script文件</h1>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io();
</script>
</body>
</html>
此时,在服务器上,app.js中就要书写静态文件呈递程序,能够呈递静态页面。
var server = http.createServer(function(req,res){
if(req.url == "/"){
//显示首页
fs.readFile("./index.html",function(err,data){
res.end(data);
});
}
});
至此,服务器和客户端都有socket对象了。
服务器端的:
var io = require('socket.io')(server);
//监听连接事件
io.on("connection",function(socket){
console.log("1个客户端连接了");
socket.on("tiwen",function(msg){
console.log("本服务器得到了一个提问" + msg);
socket.emit("huida","吃了");
});
});
每一个连接上来的用户,都有一个socket
。 由于我们的emit
语句,是socket.emit()
发出的,所以指的是向这个客户端发出语句。
广播,就是给所有当前连接的用户发送信息,使用io
对象:
//创建一个io对象
var io = require('socket.io')(server);
//监听连接事件
io.on("connection",function(socket){
console.log("1个客户端连接了");
socket.on("tiwen",function(msg){
console.log("本服务器得到了一个提问" + msg);
io.emit("huida","吃了");
});
});
express与Scoket.IO结合
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
http.listen(3000);
Node.js特点:单线程、异步I/O(非阻塞I/O)、事件驱动(事件环)。
适合的程序:就是没有太多的计算,I/O比较多的业务。举例:留言本、考试系统、说说、图片裁切服务器
Node.js原生: http、fs、path、url。 静态服务、简单路由、GET、POST请求。
模块:formidable、gm、express
Express:中间件、MVC建站、模板引擎ejs、静态服务、简单路由、GET、POST请求、MD5加密、图片上传。
服务器的一些概念:Cookie、Session
持久化NoSQL: 非关系型数据库,Not Only SQL。
特点:没有schema,没有行和列。用文档(JSON)来存储。
MongoDB:安装、开启、导入数据、Shell管理数据库、Mongo Vue、Node.js做CRUD(增删改查)、DAO层的封装、索引、操作符$set $lt $gt $push $pull
Mongoose: ODM,不用直接操作数据库,操作对象,这个对象自动持久。
function t1() {
var age = 20;
function t2() {
alert(age);
}
return t2;
}
var tmp = t1();
/*
1:在大部分的语言中,t1被调用执行,则申请内存
并把局部变量push入栈!
t1函数执行完毕,内部的局部变量,随着函数的退出而销毁
因此,age = 20的局部变量,依靠已经消失了。
但是在js中,age = 20这个变量,却被t2捕捉,
即使t1执行完毕,通过t2,依然可以访问该变量。
这种情况--返回的函数,并非孤立的函数,甚至把周围的变量环境,
形成了一个封闭的“环境包”,共同返回,所以叫“闭包”。
----一句话概括-----函数的作用域取决于声明时,而不取决于调用时。
2.在js中,t1执行过程中,又生成了t2,而从作用域上来说,t2能访问到age = 20,于是---“age = 20”没有消失,而是于返回的t1的函数形成了一个“环境包”,这个包属于t2,所以叫“闭包”。
返回的t2是有自己的生态环境。
*/
var age = 99;
tmp(); // 20
再看函数的作用域取决于声明时
function closure() {
var sister = '大桃花';
var mysister = function() {
alert(sister);
}
return mysister;
}
function place() {
var sister = '大福晋';
var girl = closure();
girl();
}
place(); // 大桃花
多个人开发js程序,需要一个全局的计数器
多个人的函数共用一个计数器,计数器一直增长
解决办法:
1:设立一个全局变量
window.cnt = 0
调用 ++window.cnt
这个办法可行,但是污染了全局变量。
其次,引入了多人的js程序,别人的程序里,也有一个window.cnt = 'hello'
;
改计数器就损坏了。(所以要尽量避免用全局变量)
2:闭包维护一个别人污染不到的变量,做计数器。
function counter() {
var cnt = 0; // 当counter执行完毕后,除了返回的cnter函数,谁也别想碰到cnt变量了
var cnter = function () {
return ++cnt;
}
return cnter;
}
var inc = counter();
alert(int());
alert(int());
alert(int());
// 简化版
var cnt = (function () {
var cnt = 0;
return function() {
return ++cnt;
}
})();
alert(cnt());
alert(cnt());
alert(cnt());
/* 第三版
在工作中,一般如何避免全局污染或冲突
1:统一放在一个全局对象上,如jquery->$
2:每个人用自己的命名空间
*/
// jquery的计数器插件形式
var $ = {};
$.cnt = (function () {
var cnt = 0;
return function() {
return ++cnt;
}
})();
alert($.cnt());
alert($.cnt());
alert($.cnt());
// 个人命名空间,再团队中也很常见
//其实就是把自己的变量,函数,都放在一个对象里。
var Y18 = {};
Y18.cnt = (function () {
var cnt = 0;
return function() {
return ++cnt;
}
})();
alert(Y18.cnt());
alert(Y18.cnt());
alert(Y18.cnt());
<ul>
<li>男</li>
<li>女</li>
<li>老</li>
<li>少</li>
</ul>
<script>
for(var i = 0, lis = document.getElementByTagName('li'), len = lis.length; i < len; i++) {
lis[i].onclick = function () {
alert(i);
}
}
</script>
如何解决弹出来的都是“4”这个问题
for(var i = 0, lis = document.getElementByTagName('li'), len = lis.length; i < len; i++) {
lis[i].onclick = function(num){
return function(){
alert(num);
}
}(i)
}
思考:事件(捕捉/冒泡)的过程中,能否停止事件的传播?
比如,被bj捕捉后,就结束,hd别想捕捉了
或hd冒泡后,事件就结束,bj无法冒泡了
答:当然能,可以停止事件的传播
用事件的对象的stopPropagation()
<body>
<form action="" id='fm' >
<p><input type="text" name="age" id="age" /></p>
<input type="submit" value="提交"/>
</form>
<div id="china">
<div id="bj">
<div id="hd">
</div>
</div>
</div>
<p id='p'>请点击</p>
<p>
<input type="button" value="让你哭" onclick="addcry()" />
<input type="button" value="让你笑" onclick="addla()" />
<input type="button" value="你别哭" onclick="rmcry()" />
<input type="button" value="你别笑" onclick="rmla()" />
</p>
<script>
function $(id) {
return document.getElementById(id);
}
$('china').addEventListener('click', function() {
alert("进入china");
}, true);
$('bj').addEventListener('click', function(ev) {
alert("进入bj");
ev.stopPropagation();
}, true); // 进入这里之后就停止了
$('hd').addEventListener('click', function() {
alert("进入hd");
}, true);
$('china').addEventListener('click', function(ev) {
console.log(ev);
alert("离开china");
}, true);
$('bj').addEventListener('click', function() {
alert("离开bj");
}, true);
$('hd').addEventListener('click', function() {
alert("离开hd");
}, true);
</script>
</body>
思路:以表单为例,我想点击onsubmit时,检查是否填写完全
如果不完全,不让他提交,
即----取消事件本应有的效果!能否做到?
答:用事件对象的preventDefault()
$('fm').addEventListener('submit', function(ev) {
if($('age').value == '') {
alert(“你不能提交”);
ev.preventDefault(); // 阻止事件的默认效果
}
}, false);
function cry() {
alert('呜呜,命苦啊!');
}
function la() {
alert('哈哈,高富帅!');
}
function addcry() {
$('p').addEventListener('click', cry, false);
}
function addla() {
$('p').addEventListener('click', la, false);
}
function rmcry() {
$('p').removeEventListener('click', cry, false);
}
function rmla() {
$('p').removeEventListener('click', la, false);
}
js中函数的4种调用方式:
1:作为普通函数调用时,
this的值指向 -> window
准确的说,this为null,但被解释成window
在ESMA标准中,如果this为null,则解释成undefined
alert(window.xx);
function t() {
this.xx = 333;
}
t();
alert(window.xx);
2:作为对象的方法调用
this指向其调用那一刻的调用者
不管被调用函数,声明时属于方法,还是函数。
var obj = {xx: 999, yy: 888, t: function() {alert(this.xx)}};
obj.t(); // 999
var dog = {xx: 'wangwang'};
dog.t = obj.t;
dog.t(); // wangwang
show = function() {
alert('show ' + this.xx);
}
dog.t = show;
dog.t(); // show wangwang
3:作为构造函数调用时
js中没有类的概念,创建对象是用构造函数来完成的,或者是直接用json格式{}来写对象。
function Dog(name, age) {
this.name = name;
this.age = age;
this.bark = function() {
alert('i am ' + this.name + '!');
}
}
var dog = new Dog('huzi', 2);
/*
new Dog 发生了以下几个步骤
a: 系统创建空对象{},(空对象construcor属性指向Dog函数,先不管)
b: 把函数的this---指向--》该空对象
c: 执行该函数
d: 返回该对象
*/
// 下面这个返回什么?
function Pig() {
this.age = 99;
return 'abc';
}
var pig = new Pig(); // 此处得到的是Pig对象,因为函数作为构造函数运行时,return 的值是忽略的,还是返回对象。
console.log(pig);
4:函数通过call、apply调用
语法格式:函数.call(对象,参数1,参数2......参数n)
function t(num) {
alert('我的真实年龄是' + this.age);
alert('但我一般告诉别人我' + (this.age + num));
}
var human = {name: 'lisi', age: 28};
human.t = t;
human.t(-10); // this指向了human,但是human多了一个方法。
//接下来,我们不把t赋为human的属性,也能把this指向human
var wangwu = {name: 'wangwu', age: 30};
t.call(wangwu, 5);
/*
解释:
fn函数.call(对象, 参数1,参数2,。。。参数n);
运行如下:
a): fn函数中的this----指向--》对象obj
b): 运行fn(参数1,参数2。。。参数n)
*/
name = 'this is window';
var obj = {name: 'php', t: function () {alert(this.name)}};
var dog = {name: 'huzi'};
obj.t(); // php
var tmp = obj.t;
tmp(); // 相当于window.tmp() -----> this is window
dog.t = obj.t;
dog.t(); // huzi
(dog.t = obj.t)(); // this is window
/*
首先括号里是一个表达式,返回值是“值”,即t函数
强调是值,说明不是通过引用调用的,而是立即使用函数本身
效果等同于 (function () {alert(this.name)})(),即null
null又被解释成window
*/
dog.t.call(obj); // php
有一些有经验,但对js理解不深的同学说:
有this操作的,如 this.age = xx 的函数不能直接调用,而是要new来调用。
为什么?
答:因为直接调用,this指向window,将会污染全局变量
function Dog() {
this.leg = 4;
this.bark = function() {
alert('旺旺');
}
}
var huzi = new Dog();
huzi.bark();
/*
上面我们并没有完成面向对象的“封装”
所谓封装---就要封闭一部分,外界无法访问,
开放一部分,通过开放部分间接访问是有部分
*/
function Girl(name, bf) {
this.name = name;
this.love = bf;
}
var girl= new Girl('林黛玉', '贾宝玉');
alert(girl.name + '喜欢' + girl.bf);
//如何让bf不能外部访问
function Girl(name, bf) {
var secret = bf;
this.name = name;
// 通过showlove做接口,来读取私有属性secret
this.showlove = function() {
return secret;
}
// 移情别恋
this.movelove = function() {
secret = '薛潘';
}
}
var girl= new Girl('林黛玉', '贾宝玉');
alert(girl.name + '喜欢' + girl.bf);
alert(girl.name + '喜欢' + girl.showlove);
girl.movelove();
alert(girl.name + '喜欢' + girl.showlove);
以上就是通过闭包来完成js面向对象的私有属性与封装
这个方法是js大牛,Douglas Crockford 提出来的,
他以前在Yahoo,现在再paypal,
json格式也是他倡导的,
他的书---the Javascript good parts《javascript语言精粹》
开发一个五子棋游戏
问: 在255个td上,全都加上onclick可以么?
答: 不可以这样做,如果都加上,将会增加浏览器的解析负担
那又应该加在哪儿呢?
答: 根据冒泡的原理,在table上加
点击table区域,必然也点中了某个td
td的事件,必须回冒泡上去,被table的事件捕捉
这种 子元素上不绑定事件,利用冒泡原理,冒泡到父元素时再执行的特点-----称为事件委托。
即----把子元素的事件委托给父元素
问:你点击table区域时,如何判断当时点中的是哪个td呢?
答:利用事件对象中的target
,在IE中是srcElement
window.onload = function () {
document.getElementsByTagName('table')[0].onclick = function (ev) {
console.log(ev);
console.log(ev.target);
console.log(ev.srcElement);
}
}
由上,在灵活的动态语言中,谈不上“多态”的概念。
例如 在java中
class Dog {
void bark() {
}
}
class Jingba extend Dog {
void bark() {
System.out.printIn('汪汪');
}
}
class Hashiqi() extend Dog {
void bark() {
System.out.printf("呜呜");
}
}
某个方法,大致如下:
void call1(Jingba j) { // 此处,j变量前,声明了j必须是Jingba类的实例
j->bark();
}
void call2(Hashiqi h) { // 此处,h变量前,声明了h必须是Hashiqi类的实例
h->bark();
}
如上-----不够灵活
void call(Dog d) { // 此处,声明d必须是狗类的实例,所以传j,h都行,因为“哈士奇”也是狗
d->bark();
}
经过上面的改造-------本来只能接受Hashiqi,Jingba实例的,
现在能接受的范围宽了,只要是Dog的实例或Dog子类的实例,都可以。
----同时,将导致一个后果,传一个Dog实例过来
有可能是Hashiqi对象,呜呜 叫
有可能是Jingba对象,汪汪 叫
-----都是传Dog对象进来,竟然有不同的叫声,称之为多态。
如上可知,js里谈不上多态,----因为js就没有限制参数类型
function Dog() {
this.leg = 4;
this.bark = null;
}
function Hashiqi() {
this.bark = function() {
alert("呜呜");
}
}
function Jingba() {
this.bark = function() {
alert("汪汪");
}
}
Hashiqi.prototype = Jingba.prototype = new Dog();
var h = new Hashiqi();
var j = new Jingba();
function test(dog) {
dog.bark();
}
test(h);
test(j);
// 传进来狗对象,叫声却不同---因为传的是狗的不同子类
js没有类的概念,因此---js的继承,不是通过类的继承来实现的,而是通过“原型”的概念来完成的。
// 这个构造函数用来制造对象
function tiger() {
this.bark = function() {
alert('我是百兽之王');
}
}
var hu = new tiger();
/*
以java为例,应该让tiger继承cat类,但js中做不到
那,如何让老虎有猫的属性呢?
----照猫画虎
我们明确的对“tiger函数”指定,用某个具体的“cat对象”做老虎的原型,并创建“老虎对象”
*/
function cat() {
this.climb = function() {
alert('我会爬树');
}
}
var bosi = new cat();
hu.bark();
bosi.climb();
hu.climb(); // 报错,没有这个方法
// 开始继承
tiger.prototype = new cat();
var hnhu = new tiger();
hnhu.climb(); // 我会爬树
alert(hnhu.valueOf()); // 正常运行
console.log(hnhu); // hnhu里面并没有climb函数,但是可以看到__proto__指向了cat
/*
老虎是如何爬树和valueOf的?
答:老虎首先在自身对象上寻找,没有爬树方法,valueOf方法
去找原型,原型cat对象上有此方法,因此得以调用climb()方法
但valueOf()方法仍没找到,
于是继续沿着原型查找,
找到cat空对象,仍没有valueOf,
再找,找到Object对象,有valueOf方法,调用成功。
对象->原型->原型的原型->Object对象-->Null
这样的一条链----称为原型链
对象的属性和方法,就是沿着原型链来查找和调用的!
也就是JS中的原型继承
*/
/*
在这个过程中,发生了什么?
1:构造空对象hnhu {}
2:hnhu.bark = function () {}
3:hnhu.__proto__ = tiger.prototype(即cat对象) ---》这是继承的关键
就是说----js中,每个对象,都有一个__proto__指向其原型
原型对象也是对象,也有__proto__
。。。原型的原型的原型的原型。。。。。是谁?
*/
function cat() {
this.climb = function() {
alert('我会爬树');
}
}
console.log(cat.prototype); // 空对象,显示为cat
console.log(cat.prototype.constructor); // 因为原型对象的constructor指向为cat,所以显示为cat
console.log(cat.prototype.__proto__); // 空对象 Object
console.log(cat.prototype.__proto__.__proto__); // null
一个fn函数,默认有一个原型,其实就是空对象
而这个空对象有一个constructor属性,这个属性又回过来指向fn函数
并且,这个空对象也有一个原型,是object
object最终指向了null
答:老虎首先在自身对象上寻找,没有爬树方法,valueOf方法
去找原型,原型cat对象上有此方法,因此得以调用climb()方法
但valueOf()方法仍没找到,
于是继续沿着原型查找,
找到cat空对象,仍没有valueOf,
再找,找到Object对象,有valueOf方法,调用成功。
对象->原型->原型的原型->Object对象-->Null
这样的一条链----称为原型链
对象的属性和方法,就是沿着原型链来查找和调用的!
也就是JS中的原型继承
Object.prototype.sing = function() {
alert('出卖我的最爱');
}
function Pig() {
this.eat = '30kg'; // 每次吃30千克
}
var zhu = new Pig();
zhu.sing();
function Dog() {
this.bark = function() {
alert('汪汪');
}
}
function peter() {
this.fire = function() {
alert('第一斗犬');
}
}
//原型继承
peter.prototype = new Dog();
var huzi = new peter();
huzi.bark(); // 汪汪
huzi.fire();
function zangao() {
this.eat = function() {
alert('会吃,会吹');
}
}
zangao.prototype = new Dog();
zangao.prototype.bark = function() { alert('呜呜'); };
var za = new zangao();
za.bark(); // 呜呜
huzi.bark(); // 汪汪
1:构造函数通过new来制造对象
2:函数本身也是对象
比如:一个豆浆机,转动(new调用)时,能返回一杯豆浆机(制造出来的对象)
但反观豆浆机本身,也是台机器,也是个对象
豆浆机身上也有属性--如开关,进水口等。
function Hashiqi() {
this.bark = function() {
alert('旺旺');
}
}
Hashiqi.ajax = function () {
alert('我会ajax');
}
var h = new Hashiqi(); //h里面是否有ajax方法?
// 答案是没有,豆浆机上的开关会跑到豆浆里吗?
console.log(h);
console.log(Hashiqi.ajax);
/*
即-----
1:ajax()方法是属于“函数”本身的,和返回的对象没有关系
2:bark要调用,必须要new Hashiqi() 得到对象,且由返回对象才能调用
3:ajax()方法要调用,不需要new对象,直接用Hashiqi来调用。
*/
h.bark();
Hashiqi.ajax();
/*
我们之前有没有接触过静态方法?
答:有
1:Math.randmon(); 静态方法
2:$.ajax();静态方法
3:写jquery插件,2种方法
3.1 通过闭包,把方法写到jquer上的原型上(稍复杂)
3.2 直接增加$对象的静态方法
*/
// 通过静态方法给jquery增加唱歌插件
$.sing = function () {
alert("一千个伤心的理由");
};
$.sing();
js的语法非常灵活,不仅可以用原型继承,还有其他办法
如,原型冒充,或复制继承
function Cat(leg, tail) {
this.leg = leg;
this.tail = tail;
this.climb = function () {
alert('我会爬树');
}
}
function Tiger(leg, tail, color) {
// 把要继承的类的语句,拿来执行一遍
this.parent = Cat; // 把父类构造函数引入到一个parent属性上
this.parent.apply(this, arguments);
delete this.parent;
this.color = color;
}
var tiger = new Tiger(4, 1, 'yellow'); //
console.log(tiger);
tiger.climb();
/*
这是怎么回事?
答:其实就是,在用Tiger造对象时,用Tiger的语句影响一个空对象{},
在此过程中,Tiger影响空对象前,先由Cat函数实施影响,
因此,最终得到的对象,是由Cat和Tiger两者共同作用过的对象
*/
复制继承,就是把父对象的所有属性,直接复制到自己的对象上
function Cat(leg, tail) {
this.leg = leg;
this.tail = tail;
this.climb = function () {
alert('我会爬树');
}
}
function Tiger(color) {
this.color = color;
this.extend = function (parent) {
for(var key in parent) {
// console.log(key);
this[key] = parent[key];
}
}
}
var tiger = new Tiger();
console.log(tiger);
// tiger.climb(); // 老虎不会爬树,出错
tiger.extend(new Cat());
console.log(tiger); // 老虎会爬树了。
tiger.climb();
<script>
function t() {
alert("我被点穴了");
}
/*
在这个过程中,a标签上绑定了onclick事件,
用句柄来描述就是------a标签的onclick句柄上,绑定了t函数
分析:
1:DOM对象的句柄
2:句柄上绑定的函数
3:事件发生的那一瞬间,关于事件的各种信息,如时间,如发生时鼠标在屏幕上的坐标,事件类型等等
这些信息,被打包成一个对象,便于我们获取 -----这个对象称为事件对象。
*/
</script>
<a href="#" onclick="t();">点我</a>
即把事件写在标签的属性里 如:<a href="#" onclick="t()"></a>
这是DOM 0 级的标准(非常古老的标准)
好处:大家都会,几乎所有的浏览器都支持。
坏处:夹杂在html代码中,代码不简洁;这种事件写法,效率不高;不符合“行为,结构,样式相分离”。
用事件属性来绑定事件函数
window.onload = function() {
document.getElementById("test").onclick = function() {
alert("有人点击我");
};
document.getElementById("test1").onclick = function() {
this.style.background = 'green';
}
document.getElementById("test2").onclick = function(auven) {
// console.log(auven); // 此处的auven就是事件对象
var test3 = document.getElementById('test3');
test3.style.left = auven.clientX + "px";
test3.style.top = auven.clientY + "px";
}
}
好处:完成行为的分离;
便于操作当事对象,因为function是作为对象的on**属性出现的,
因此,函数里操作该对象,直接用this就能引用当事对象;
可以方便的读取“事件对象”,事件对象在事件触发时,系统自动把“事件对象”传递给“事件函数”,以其第1个参数来传
<link href="favicon.ico" mce_href="favicon.ico" rel="bookmark" type="image/x-icon" />
<link href="favicon.ico" mce_href="favicon.ico" rel="icon" type="image/x-icon" />
<link href="favicon.ico" mce_href="favicon.ico" rel="shortcut icon" type="image/x-icon" />
在如今响应式布局的要求下,很多能自动调整尺寸的元素能够做到高宽自适应,如img
,通过{width:50%;height:auto;}
实现图片高度跟随宽度比例调整。
而div等元素,要自动调整,要么从父级继承,要么自行指定px,要么给百分比!但是这个百分比是根据父级的高度来计算的,根本不是根据元素自身的宽度,那么就做不到Div的宽高达成一定的比例。
有如下几种实现方式:
1,直接指定div的宽高+zoom来实现自适应
div{width:50px;heigth:50px;zoom:1.1;}
这样能达到初步的等宽高div,但是局限性太大,PASS!
2,通过js动态判断div的宽度来设置高度
div{width:50%;}
window.onresize = function(){div.height(div.width);}
也能实现等宽高div,但是总觉得有点别扭,PASS!
3,通过宽高单位来设置
div{width:20vw;height:20vw;/*20vw为viewport width的20%*/}
但是很多设备不支持这个属性,兼容性太差,PASS!
4,通过float来设置
#aa{background:#aaa;;}
#bb{background:#ddd;;float:left}
#cc{background:#eee;;float:right}
<div id="aa">父div
<div id="bb">子div</div>
<div id="cc">子div</div>
<div style="clear:both">就是这个用于clear错误的</div>
</div>
能够让父级元素aa根据子元素的高度自动改变高度(在子元素里放置自适应元素)来调整高宽比一致,然而太麻烦,PASS!
5,终于到最终大杀器了,通过padding
来实现此功能
通过以上几个方案的实验,发现宽度的自适应是根据viewport
的width
来调整的,比如{width:50%}
就是浏览器可视区域的50%,resize
之后也会自动调整。
而height
指定百分比后,他会自行找到viewport
的height
来调整,跟width
一毛钱关系没有,自然两者不能达到比例关系了。通过这个思路,要找到一个能跟viewport
的width
扯上裙带关系的属性,就能解决这个问题了。
这个属性就是padding
,padding
是根据viewport
的width
来调整的,巧就巧在padding-top
和padding-bottom
也是根据viewport
的width
来计算的,那么通过设置这个属性就能跟width
达成某种比例关系了,
我们首先指定元素的width
为父级元素的50%
(父级元素为任意有高宽的元素,不能指定特定父级元素,否则影响此方案的通用性)
.father{width:100px;height:100px;background:#222}
.element{width:50%;background:#eee;}
这个时候我们再设置element
的height为0,padding-bottom:50%;
.element{width:50%;height:0;padding-bottom:50%;background:#eee;}
element
就变成了一个宽度50%,高度为0(但是他有50%width的padding-bottom)的正方形了。
这个时候可能有人要问了,这个div的高度为0,那如果我要在element
里放置元素呢,那岂不是overflow
了,这里就要提到overflow
属性了,它的计算是包括div
的content
和padding
的,也就是说,
原来你的div可能是个{width:50px;height:50px;padding:0}
的div
,现在变成{width:50px;height:0;padding-bottom:50px;}
的div
了,尺寸还是一样的,通过指定这个div的子元素的定位,一样可以正常显示。
这样就可以通过设定父级元素father
、我们需要的元素element
、子级元素datail
来实现任意情况下该需求(div宽高定比例)。
根据Draggabilly官方文档翻译,由于英文水平有限,部分内容可能有误。本文档只翻译了主要的选项配置等,想了解更多请查看官方文档。
js拖拽插件
1.0版本支持ie8+和多点触控
2.0版本支持ie10+和多点触控
使用npm安装:npm install draggabilly
使用Bower安装:bower install draggabilly
通过cdn直接链接到Draggabilly文件
<!-- 1.0版本 -->
<script src="//cdnjs.cloudflare.com/ajax/libs/draggabilly/1.2.4/draggabilly.pkgd.min.js"></script>
<!-- 或者 -->
<script src="//cdnjs.cloudflare.com/ajax/libs/draggabilly/1.2.4/draggabilly.pkgd.js"></script>
<!-- 2.0版本 -->
<script src="https://unpkg.com/draggabilly@2/dist/draggabilly.pkgd.min.js"></script>
<!-- 或者 -->
<script src="https://unpkg.com/draggabilly@2/dist/draggabilly.pkgd.js"></script>
初始化draggabilly为一个jQuery插件
var $draggable = $('.draggable').draggabilly({
// 选项(配置)...
})
使用原生js初始化
var elem = document.querySelector('.draggable');
var draggie = new Draggabilly( elem, {
// 选项...
});
// 或者直接将元素作为第一个参数
var draggie = new Draggabilly( '.draggable', {
// 选项...
});
// 如果你有多个.draggable元素
// 首先获取所有的拖拽元素
var draggableElems = document.querySelectorAll('.draggable');
// 设置一个数组用来存放初始化后的所有拖拽元素
var draggies = []
// 初始化
for ( var i=0, len = draggableElems.length; i < len; i++ ) {
var draggableElem = draggableElems[i];
var draggie = new Draggabilly( draggableElem, {
// 选项...
});
draggies.push( draggie );
}
相关的class
.is-pointer-down
当用户鼠标第一次点击(或触摸)时添加.is-dragging
当元素开始拖拽时添加类型: 字符串(String)
值: 'x' 或者 'y'
axis: 'x'
元素允许在水平或垂直方向上拖动,默认是水平垂直均可
类型:元素、选择器字符串 或 布尔值
containment: '.container'
包含元素边界的移动。如果true
,容器将是父元素。
类型: 数组(Array)
值: [ x, y ]
grid: [ 20, 20 ]
网格式移动元素,每x和y像素移动元素
类型: 选择器字符串
handle: '.handle'
指定拖动交互开始的元素。
句柄(操作器)是有用的,当你不想所有的内部元素被用于拖动,如输入和表单。
使用jquery标准事件.on()
、.off()
、和.one()
.,事件里面的this
指向当前拖拽的元素。
// jQuery
function listener(/* 参数 */) {
// 获取拖拽元素实例
var draggie = $(this).data('draggabilly');
console.log( 'eventName happened', draggie.position.x, draggie.position.y );
}
// 绑定事件监听
$draggable.on( 'eventName', listener );
// 移除事件监听
$draggable.off( 'eventName', listener );
// 只触发一次事件监听。注意是one不是on
$draggable.one( 'eventName', function() {
console.log('eventName happened just once');
});
使用原生js绑定事件.on()
、.off()
、和.once()
,在事件里this
指向当前拖拽的元素。
// 原生JS
function listener(/* 参数 */) {
console.log( 'eventName happened', this.position.x, this.position.y );
}
// 绑定事件监听
draggie.on( 'eventName', listener );
// 移除事件监听
draggie.off( 'eventName', listener );
// 只触发一次事件监听。注意是once不是one或者on
draggie.once( 'eventName', function() {
console.log('eventName happened just once');
});
拖动开始移动前触发,当移动小于2像素时表现为点击。
// jQuery
$draggable.on( 'dragStart', function( event, pointer ) {...})
// 原生JS
draggie.on( 'dragStart', function( event, pointer ) {...})
event
- 类型: 事件 - 原生的mousedown
或touchstart
事件pointer
- 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 这个事件对象有.pageX
和.pageY
当元素移动时触发。
// jQuery
$draggable.on( 'dragMove', function( event, pointer, moveVector ) {...})
// 原生JS
draggie.on( 'dragMove', function( event, pointer, moveVector ) {...})
event
- 类型: 事件 - 原生的mousemove
或者touchmove
事件pointer
- 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 事件对象有.pageX
和.pageY
moveVector
- 类型: 对象 - 当前位置离初始位置的位移 { x: 20, y: -30 }
当元素停止移动时触发。
// jQuery
$draggable.on( 'dragEnd', function( event, pointer ) {...})
// 原生JS
draggie.on( 'dragEnd', function( event, pointer ) {...})
event
- 类型: 事件 - the original mouseup or touchend eventpointer
- 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 事件对象有.pageX
和.pageY
当用户指针按下(鼠标、触摸)时触发。
// jQuery
$draggable.on( 'pointerDown', function( event, pointer ) {...})
// 原生JS
draggie.on( 'pointerDown', function( event, pointer ) {...})
event
- 类型: 事件 - the original mouseup or touchend eventpointer
- 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 事件对象有.pageX
和.pageY
当用户的指针移动时触发。
// jQuery
$draggable.on( 'pointerMove', function( event, pointer, moveVector ) {...})
// 原生JS
draggie.on( 'pointerMove', function( event, pointer, moveVector ) {...})
event
- 类型: 事件 - 原生的mousemove
或者touchmove
事件pointer
- 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 事件对象有.pageX
和.pageY
moveVector
- 类型: 对象 - 当前位置离初始位置的位移 { x: 20, y: -30 }
当用户指针离开元素时触发。
// jQuery
$draggable.on( 'pointerUp', function( event, pointer ) {...})
// 原生JS
draggie.on( 'pointerUp', function( event, pointer ) {...})
event
- 类型: 事件 - the original mouseup or touchend eventpointer
- 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 事件对象有.pageX
和.pageY
用户的指针压下并没有开始拖动。
单击事件很难拖动界面检测,因为他们是当用户的鼠标触发。draggabilly的staticclick事件解决了,因为它被触发时,用户没有拖。
// jQuery
$draggable.on( 'staticClick', function( event, pointer ) {...})
// 原生JS
draggie.on( 'staticClick', function( event, pointer ) {...})
event
- 类型: 事件 - the original mouseup or touchend eventpointer
- 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 事件对象有.pageX
和.pageY
// jQuery
$draggable.draggabilly('disable')
// vanilla JS
draggie.disable()
// jQuery
$draggable.draggabilly('enable')
// vanilla JS
draggie.enable()
// jQuery
$draggable.draggabilly('destroy')
// vanilla JS
draggie.destroy()
从一个jQuery对象得到draggabilly实例。draggabilly实例访问draggabilly有用的属性。
var draggie = $('.draggable').data('draggabilly')
// access Draggabilly properties
console.log( 'draggie at ' + draggie.position.x + ', ' + draggie.position.y )
{ x: 20, y: -30 }
x
整数y
整数<style type="text/css">
.parent {
width: 640px;
height: 480px;
background: gray;
}
.child {
}
</style>
<div class="parent">
<div class="child">
不定宽高,让这个div水平垂直居中
</div>
</div>
如何让它水平垂直居中呢?
.parent {
width: 640px;
height: 480px;
background: gray;
}
.child {
/* 注意:这种方法必须指定宽高 */
width: 200px;
height: 200px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
这种方法必须指定宽高,那如果我不想指定宽高,又该如何处理呢?
flex布局
,外层容器加上如下样式即可.parent {
display: flex;
justify-content: center;
align-items: center;
}
.child {
}
table-cell
.parent {
display:table-cell;
text-align:center;
vertical-align:middle;
}
.child {
vertical-align:middle;
display:inline-block;
}
效果同上
transform
.parent {
position: relative
}
.child {
transform: translate(-50%,-50%);
position: absolute;
top: 50%;
left: 50%;
}
效果同上
arguments是什么?
答: 是一个对象,是一个长得很像数组的对象。
arguments内容是什么?
答:arguments是函数运行时的实参列表。arguments可以获取函数运行时,收到的实参个数(在多态里可以用到)
// arguments收集了“所有”的实参,即使没有与之相对应的形参。
(function (d, e, f) {
console.log(arguments); // 对象
console.log(arguments.length);
console.log(arguments[0]); // hello
console.log(arguments[2]); // !
//在此函数内,无法用d,e,f形参来取得'haha',因为没有与之相对应的形参
//但我们可以用arguments来获取任意多个的实参
console.log(arguments[3]);
arguments[0] = 'china';
console.log(d); // china -->由此可以看到,形参与对应的arguments单元,其实是相互映射的,互相影响。
})('hello', 'world', '!', 'haha');
当一个函数运行时,函数内部能够引用的变量有这么几种: AO、arguments、this。
arguments.callee
属性代表“当前运行的函数”,指向arguments所属的函数。
(function (d, e, f) {
console.log(arguments.callee);
})('hello', 'world', '!');
function t(n) {
if(n <= 1) {
return 1;
} else {
return n + t(n-1);
}
};
alert(t(100));
// 不用函数名,匿名函数,立即执行,完成递归
alert((function (n) {
if(n <= 1) {
return 1;
} else {
return n + arguments.callee(n-1);
}
})(100)); // 5050
// 再思考,如果是匿名函数,又不允许用callee,也要完成递归,该怎么办?
// 提示:Y算子!
AO ---》本函数AO上没有某属性,则继续去外层函数的AO上找,查到全局对象,叫做 作用于链
arguments ---》每个函数有自己的callee,但 不 向外层接着找arguments的相关属性,即 不形成链
this
<body>
<div id="china">
<div id="bj">
<div id="hd">
</div>
</div>
</div>
<script>
function $(id) {
return document.getElementById(id);
}
$('china').addEventListener('click', function() {
alert("china");
}, false); //china
$('bj').addEventListener('click', function() {
alert("bj");
}, false); //bj, china
$('hd').addEventListener('click', function() {
alert("hd");
}, false); // hd,bj,china
/*
如果我在“海淀”div点击了一下
那么
----点击的效果,
是向内聚焦,china->bj->hd
还是向外扩散,hd->bj->china
W3C标准中,同时支持这2种方式,分别称为
外->内,称为“捕捉模型”,
内->外,称为“冒泡模型”
*/
$('china').addEventListener('click', function() {
alert("china");
}, true); //china, bj, hd
$('bj').addEventListener('click', function() {
alert("bj");
}, true); //bj, hd
$('hd').addEventListener('click', function() {
alert("hd");
}, true); // hd
/* ----------------------------------- */
$('china').addEventListener('click', function() {
alert("进入china");
}, true); //进入china, 进入bj, 进入 hd,离开hd, 离开bj,离开china
$('bj').addEventListener('click', function() {
alert("进入bj");
}, true);
$('hd').addEventListener('click', function() {
alert("进入hd");
}, true);
$('china').addEventListener('click', function(ev) {
console.log(ev);
alert("离开china");
}, true);
$('bj').addEventListener('click', function() {
alert("离开bj");
}, true);
$('hd').addEventListener('click', function() {
alert("离开hd");
}, true);
</script>
</body>
总结:
1:第3个参数true/false 代表 捕捉/冒泡 ,不填写时默认是false,考虑兼容性,一般要填写
2:系统会为事件函数,自动传入事件对象,作为第一个参数传入
一个页面的js由多人团队开发,A,B都写了onload事件,则 后面的事件,把前面的onload属性值给覆盖了。
那么如何解决这个问题呢?
W3C中的标准
1:绑在哪个事件上? click, load, change, blur, focus ...
2:绑定什么函数-----自定义事件函数
3:什么方式监听执行事件函数?捕捉?冒泡?
语法:xxDomObject.addEventListener();
注意细节:
1:事件名一律不带on
2:绑定事件函数中的“this”指当前被绑定的对象
3:执行顺序按绑定顺序来执行的!
<div id="test1"></div>
<script>
var test1 = document.getElementById("test1");
test1.addEventListener('click', function() {
this.style.backgroundColor = 'gray';
}, false);
test1.addEventListener('click', function() {
alert("自学");
}, false);
test1.addEventListener('click', function() {
alert("IT");
}, false);
</script>
var c = 5;
function t1() {
var d = 6;
function t2() {
var e = 7;
// var d = 3; 注释再去掉注释,观察结果变化
alert(c + d + e);
}
}
t1(); //15, 18
在js中,函数嵌套是非常普遍的,在函数中,对变量是如何寻找的?
答:首先是函数内,然后外层,再外层...最后直到....全局(window)区域。
alert(window.d); //underfine
alert(window.e); //underfine
function t() {
d = 5;
var e = 6;
}
t(); // d没有加var,仅仅是一个复制操作,寻找t域内的函数,没找到,继续寻找....>window,window.d = 5
alert(window.d); //5
alert(window.e); //undefined
var是在函数运行的上下文中,声明一个变量,如果不加var,则是一个复制操作,但不要狭隘的理解为声明了一个全局变量。
function t1() {
var d;
function t2() {
d = 5;
e = 6;
}
t2();
}
t1();
console.log(e); // 6
console.log(d); // undefined
注:这是一个极容易出错,又极基础的js面试题
var str1 = 'global';
function t1() {
console.log(str1);
console.log(str2);
str2 = 'local';
}
t1();
/*
常见的三种答案:
global,local
global,undefined
global,str2 is not defined错误
分析:
执行到t1函数内的第一个console,寻找str1--》没有,又在window上寻找str1有,打印global。
执行到t1函数内的第二个console,寻找str2--》没有,又在window上寻找str2没有,报str2 is not defined错误。
*/
var str1 = 'global';
function t1() {
console.log(str1);
console.log(str2);
var str2 = 'local';
}
t1(); // global, undefined
/*
解释:
js代码的执行,自上而下执行,
但是-----------
js代码在整体运行分:
词法分析期
运行期
自上而下执行之前,先有一个”词法分析过程“
以上面的结果为例:
1步:分析t1函数
t1{
var str2 //分析出t1内有str2局部变量,注意此时函数未执行,一次此时str2的值是undefined
}
2步:执行t1函数
console.log(str1); //global
console.log(str2); //undefined
str2 = 'loacl'; //此时str2的值是local
*/
虽然flex布局早在2009年就有了,而现在是2015年8月10日,使用最新的flex语法会发现支持程度并不好,即使是在“高端”浏览器上也是如此,比如Chrome、Firefox、Safari、Android、IOS Safari下支持程度各不相同。
网上现有的代码中充斥着各种版本,在Chrome下运行一般都没有问题,Firefox一般也还好,但Android与IOS Safari下就显得非常无力了。之所以会出现这样的局面,主要是历史原因,从2009年到2015年,期间W3C规范有了多次更新,浏览器支持程度也就有了差异。
Flexbox布局的语法经过几年发生了很大的变化,也给Flexbox的使用带来一定的局限性,因为语法版本众多,浏览器支持不一致,致使Flexbox布局使用不多。
Flexbox布局主要有三种语法版本:
2009版本,我们称之为老版本,使用的是“display:box”或者“display:inline-box”;
2011版本,我们称之为混合版本,使用的是“display:flexbox”或者“display:inline-flexbox”;
2013版本,也就是最新语法版本,使用的是“display:flex”或者“display:inline-flex”。
我们需要把Flexbox旧的语法、中间过渡语法和最新的语法混在一起使用,在这里他们的顺序显得非常重要。“display”属性本身并不添加任何浏览器前缀,我们需要确保我们老语法不要覆盖新语法让浏览器(可能总是会)同时支持。
.box {
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
display: -ms-flexbox; /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Chrome */
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
}
.main-content {
-webkit-box-ordinal-group: 2; /* OLD - iOS 6-, Safari 3.1-6 */
-moz-box-ordinal-group: 2; /* OLD - Firefox 19- */
-ms-flex-order: 2; /* TWEENER - IE 10 */
-webkit-order: 2; /* NEW - Chrome */
order: 2; /* NEW, Spec - Opera 12.1, Firefox 20+ */
}
.main-nav {
-webkit-box-ordinal-group: 1;
-moz-box-ordinal-group: 1;
-ms-flex-order: 1;
-webkit-order: 1;
order: 1;
}
.main-sidebar {
-webkit-box-ordinal-group: 3;
-moz-box-ordinal-group: 3;
-ms-flex-order: 3;
-webkit-order: 3;
order: 3;
}
关于flex的W3C规范: http://dev.w3.org/csswg/css-flexbox-1/
浏览器兼容性可以参考CanIUse: http://caniuse.com/#feat=flexbox
根据CanIUse的数据可以总结如下:
IE10部分支持2012,需要-ms-前缀
Android4.1/4.2-4.3部分支持2009 ,需要-webkit-前缀
Safari7/7.1/8部分支持2012, 需要-webkit-前缀
IOS Safari7.0-7.1/8.1-8.3部分支持2012,需要-webkit-前缀
所以需要考虑新版本2012: http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/
而Android需要考虑旧版本2009: http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/
/* 子元素-平均分栏 */
.flex1 {
-webkit-box-flex: 1; /* OLD - iOS 6-, Safari 3.1-6 */
-moz-box-flex: 1; /* OLD - Firefox 19- */
width: 20%; /* For old syntax, otherwise collapses. */
-webkit-flex: 1; /* Chrome */
-ms-flex: 1; /* IE 10 */
flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */
}
/* 父元素-横向排列(主轴) */
.flex-h {
display: box; /* OLD - Android 4.4- */
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
display: -ms-flexbox; /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Chrome */
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
/* 09版 */
-webkit-box-orient: horizontal;
/* 12版 */
-webkit-flex-direction: row;
-moz-flex-direction: row;
-ms-flex-direction: row;
-o-flex-direction: row;
flex-direction: row;
}
/* 父元素-横向换行 */
.flex-hw {
/* 09版 */
/*-webkit-box-lines: multiple;*/
/* 12版 */
-webkit-flex-wrap: wrap;
-moz-flex-wrap: wrap;
-ms-flex-wrap: wrap;
-o-flex-wrap: wrap;
flex-wrap: wrap;
}
/* 父元素-水平居中(主轴是横向才生效) */
.flex-hc {
/* 09版 */
-webkit-box-pack: center;
/* 12版 */
-webkit-justify-content: center;
-moz-justify-content: center;
-ms-justify-content: center;
-o-justify-content: center;
justify-content: center;
/* 其它取值如下:
align-items 主轴原点方向对齐
flex-end 主轴延伸方向对齐
space-between 等间距排列,首尾不留白
space-around 等间距排列,首尾留白
*/
}
/* 父元素-纵向排列(主轴) */
.flex-v {
display: box; /* OLD - Android 4.4- */
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
display: -ms-flexbox; /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Chrome */
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
/* 09版 */
-webkit-box-orient: vertical;
/* 12版 */
-webkit-flex-direction: column;
-moz-flex-direction: column;
-ms-flex-direction: column;
-o-flex-direction: column;
flex-direction: column;
}
/* 父元素-纵向换行 */
.flex-vw {
/* 09版 */
/*-webkit-box-lines: multiple;*/
/* 12版 */
-webkit-flex-wrap: wrap;
-moz-flex-wrap: wrap;
-ms-flex-wrap: wrap;
-o-flex-wrap: wrap;
flex-wrap: wrap;
}
/* 父元素-竖直居中(主轴是横向才生效) */
.flex-vc {
/* 09版 */
-webkit-box-align: center;
/* 12版 */
-webkit-align-items: center;
-moz-align-items: center;
-ms-align-items: center;
-o-align-items: center;
align-items: center;
}
/* 子元素-显示在从左向右(从上向下)第1个位置,用于改变源文档顺序显示 */
.flex-1 {
-webkit-box-ordinal-group: 1; /* OLD - iOS 6-, Safari 3.1-6 */
-moz-box-ordinal-group: 1; /* OLD - Firefox 19- */
-ms-flex-order: 1; /* TWEENER - IE 10 */
-webkit-order: 1; /* NEW - Chrome */
order: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */
}
/* 子元素-显示在从左向右(从上向下)第2个位置,用于改变源文档顺序显示 */
.flex-2 {
-webkit-box-ordinal-group: 2; /* OLD - iOS 6-, Safari 3.1-6 */
-moz-box-ordinal-group: 2; /* OLD - Firefox 19- */
-ms-flex-order: 2; /* TWEENER - IE 10 */
-webkit-order: 2; /* NEW - Chrome */
order: 2; /* NEW, Spec - Opera 12.1, Firefox 20+ */
}
为了更好的兼容性,我们需要给容器声明flex-h/flex-v,而不是一般的flex:
/* 父元素-flex容器 */
.flex {
display: box; /* OLD - Android 4.4- */
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
display: -ms-flexbox; /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Chrome */
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
}
建议在需要兼容Android时(2009版语法)采用flex-h/flex-v声明容器使用flex模式,在不需要兼容Android时(2012版语法)使用flex设置容器。
注意:上面给的代码并不是完全兼容各个高端浏览器的,但要比任何其它现有代码兼容性好
function t(age) {
alert(age);
}
t(5); // 5
t() // undefined
/*
词法分析过程:
AO {age:undefiend}
运行过程:
t(5)--> AO.age=5; alert(AO.age); //5
t()--> AO.age没有得到赋值,还是undefiend
*/
词法分析,分析三样东西:
第一步,先分析参数,
第二步,再分析变量声明
第三步,分析函数声明
一个函数能使用的局部变量,就从上面的3步分析而来
具体步骤:
0:函数运行前的一瞬间,生成active object(活动对象),称AO
1:
1.1 把函数声明的参数,形成AO的属性,值全是undefined,
1.2 接收实参,形成AO相应属性的值
2:分析变量声明!如 var age,
如果AO上还没有age属性,则添加AO属性,值是undefined
如果AO上已经有age属性,则不做任何影响
3:分析函数声明,如function foo() {},
则把函数赋给AO.foo属性,
注:如果此前foo属性已存在,则被无情的覆盖!
function t2(age) {
var age = 99;
alert(age);
}
t2(5);
/*
分析过程:
0:形成AO={}
1:
1.1 分析形参AO={age:undefined}
1.2 接收形参 AO = {age: 5}
2: 分析var age,发现AO已有age属性,不做任何影响
执行过程:
AO.age = 99;
alert(99);
*/
function t3(greet) {
var greet = 'hello'; // 试着把这一句改成var greet再做分析
alert(greet);
function greet() {
}
alert(greet);
}
t3(null);
/*
词法分析过程:
0:AO={}
1:
1.1 分析参数 AO={greet:undefined}
1.2 接收形参 AO = {greet: null}
2: 分析greet变量的声明,AO已经有greet属性,因此不做任何影响
3:分析greet函数声明,AO.greet = function() {},被覆盖成函数
执行过程:
greet = 'hello'; // 将greet赋值为hello
alert(greet); // hello
alert(greet); // hello
*/
//再看这道题
function a(b) {
alert(b);
function b() {
alert(b);
}
b();
}
a(1);
/*
分析期:
0:AO = {}
1:
1.1分析参数 AO = {b:undefined}
1.2接收参数 AO = {b:1}
2:分析var声明,此函数没有var
3:分析函数声明,AO = {b: function(){alert(b)}}
执行期:
alert(b); //function
b(); //由作用域寻找到a函数中的b,即function,alert()出来
*/
function a(b) {
alert(b);
b = function () {
alert(b);
}
b();
}
a(1);
/*
词法分析过程:
0:AO = {}
1:分析参数 AO = {b:undefined} ---》 {b:1}
2:分析var声明,没有
3:分析函数声明,问题是此处有没有函数声明?答案是???---》没有!b = function() {}是一个赋值过程,是一个函数表达式的返回值赋给b变量,在执行期才有用
执行过程:
alert(b); //1
b = function() {
alert(b);
}
b(); //function
*/
js被称为披着C外衣的Lisp语言,sp是一种强大的函数式语言。
函数的地位比较高--函数可以赋值给变量,可以作为参数来传递
function t1() {
}
t2 = function() {
}
/*这2种方式效果是不同的
t1是函数声明,虽然全局内也得到一个t1变量,值是function
而t2只是一个复制过程-----值是谁?值是右边的表达式的返回结果,即函数
就是说 function() {} 在js看来,就和 3*2 6/3 一样是一个表达式,返回一个结果
因此,t1 t2 ;两中方式在词法分析时,有着本质的区别
前者在词法分析阶段,就发挥作用
而后者,在运行阶段,才发挥作用
*/
知道了函数表达式的概念,再看看一个你以前看不懂的东西
(function(window, undefined) {
})(window);
这是jquery的最外层代码
(function(window, undefined){}) //内层表达式,返回值是函数,抱在小括号里,当成表达式来执行
(function(window, undefined){})() //立即调用
思路:为什么传window,而又不会传undefined?
答:传window是为了速度
function() {
function() {
function() {
function() {
function() {
document.getElementById... //这个document将会向作用域层层上找,直到最外层
}
}
}
}
}
jquery就是为了加快内部查找全局变量的速度,而直接把window以参数的形式传进来
这样window就在jquery内部的AO上
不传undefined是为了安全
因为在IE,FF低版本中,undefined竟然可以重新复制,如undefined = 3;
声明undefined局部变量(名字是undefined而已),同时又不传参,值自然是undefined,
防止了外界对undefied的污染。
arguments是什么?
答:是1个对象,是长得很像数组的对象
arguments内容是什么?
答:arguments是函数运行时的实参列表。
(function (d, e, f) {
console.log(arguments);
})('hello', 'world', '');
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.