GithubHelp home page GithubHelp logo

blog's People

Contributors

auven avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

Forkers

dahe13

blog's Issues

js高级--面向对象之特点

  • 在js中,有对象,没有类(但有构造函数),因此,有人说js是“基于对象”,而非面向对象。

其实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格式的对象。

js的对象的属性,也是可以任意增添或删除的

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>

nodejs+express+mongodb学习笔记

一、nodejs概述

1.简介

Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

  • Node.js不是一种独立的语言,与PHP、JSP、Python、Perl、Ruby的“既是语言,也是平台”不同,Node.js的使用JavaScript进行编程,运行在JavaScript引擎上(V8)。
  • 与PHP、JSP等相比(PHP、JSP、.net都需要运行在服务器程序上,Apache、Naginx、Tomcat、IIS。
    ),Node.js跳过了Apache、Naginx、IIS等HTTP服务器,它自己不用建设在任何服务器软件之上。 Node.js的许多设计理念与经典架构(LAMP = Linux + Apache + MySQL + PHP)有着很大的不同,可以提供强大的伸缩能力。一会儿我们就将看到,Node.js没有web容器。
  • Node.js自身哲学,是花最小的硬件成本,追求更高的并发,更高的处理性能。

2.特点

  • 单线程
    • 好处:操作系统完全不再有线程创建、销毁的时间开销
    • 坏处:就是一个用户造成了线程的崩溃,整个服务都崩溃了,其他人也崩溃了
  • 非阻塞I/O(non-blocking I/O)
    • I/O阻塞了代码的执行,极大地降低了程序的执行效率
    • 而非阻塞模式下,一个线程永远在执行计算操作,这个线程的CPU核心利用率永远是100%
    • 也就是说,与其人多,但是好多人闲着;还不如一个人玩命,往死里干活儿
  • 事件驱动(event-driven)
    • 在Node中,客户端请求建立连接,提交数据等行为,会触发相应的事件。在Node中,在一个时刻,只能执行一个事件回调函数,但是在执行一个事件回调函数的中途,可以转而处理其他事件(比如,又有新用户连接了),然后返回继续执行原事件的回调函数,这种处理机制,称为“事件环”机制。
    • 底层代码中,近半数都用于事件队列、回调函数队列的构建

单线程,单线程的好处,减少了内存开销,操作系统的内存换页。
如果某一个事情,进入了,但是被I/O阻塞了,所以这个线程就阻塞了。

非阻塞I/O, 不会傻等I/O语句结束,而会执行后面的语句。
非阻塞就能解决问题了么?比如执行着小红的业务,执行过程中,小刚的I/O回调完成了,此时怎么办??

事件机制,事件环,不管是新用户的请求,还是老用户的I/O完成,都将以事件方式加入事件环,等待调度。

说是三个特点,实际上是一个特点,离开谁都不行,都玩儿不转了。
Node.js很像抠门的餐厅老板,只聘请1个服务员,服务很多人。结果,比很多服务员效率还高。
Node.js中所有的I/O都是异步的,回调函数,套回调函数。

3.适合开发什么

善于I/O,不善于计算。因为Node.js最擅长的就是任务调度,如果你的业务有很多的CPU计算,实际上也相当于这个计算阻塞了这个单线程,就不适合Node开发。

当应用程序需要处理大量并发的I/O,而在向客户端发出响应之前,应用程序内部并不需要进行非常复杂的处理的时候,Node.js非常适合。Node.js也非常适合与web socket配合,开发长连接的实时交互应用程序。

比如:

  • 用户表单收集
  • 考试系统
  • 聊天室
  • 图文直播
  • 提供JSON的API(为前台Angular等使用)

4.nodejs无法挑战老牌服务器

Node.js本是就是极客追求性能极致的产物,缺少了很多服务器的健壮考量。所以Node不可能应用在银行、证券、电信等需要极高可靠性的业务中。

**的企业实战中,创业型公司(正处于A轮、B轮)非常爱使用Node做核心业务:

  • 功夫熊的APP,后台是Node.js在伺服
  • 实现网,整站为Node.js搭建

成熟大企业,基本上都是用Node实现某一方面的功能:

  • 知乎用了一个Node进程,跑起了“站内信”功能
  • 百度的很多表单,是用Node保存到数据库的

Node不是银弹,就是你工具箱中的一个小工具而已。

二、安装

1.下载安装

传送门:官网下载 | 国内镜像

安装:打开安装包直接安装

添加环境变量:

windows下,一般会自动添加环境变量,装完即用。如果没有,请手动添加环境变量。点开我的电脑-->右键选择属性-->打开系统信息面板后,选择左边"高级系统设置"-->点击环境变量-->将node.js安装路径添加到Path中。

mac下一般也是自动添加了环境变量,如果没有,自行百度添加环境变量的教程。

测试是否安装成功:在控制台输入node -v输出版本号,表示安装成功了。

2.使用

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原生方法

node.js官方中文文档

1.案例--静态服务器staticRender

这个案例是个演示,不用研究。说明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");

2.http模块

  • req对象的end()方法、write()方法、writeHead()方法
//这个案例简单讲解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");

3.req.url的演示,能够得到用户的请求的地址

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");

4.url模块很好用,里面有url.parse()方法,能够将url拆分成为很多部分。

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处理


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' }

5.简单的路由案例

// 当用户访问/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");

6.fs模块

6.1 这个案例,给每一个访问者加一个id,这样可以探究node事件环机制。

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);

6.2 fs模块的mkdir函数,创建文件夹

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);

6.3 fs模块的stat函数,检测文件状态。回调函数中的stats有isDirectory()方法,可以判断文件夹

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");

7.理解异步与同步

7.1 失败案例。列出album文件夹中的所有子文件夹

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");

7.2 正确的读取所有文件夹的案例。强行把异步变成同步,使用迭代器

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");

8.一个比较完整的静态资源案例

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);
    });
}

9.理解模块

  • 暴露单一变量或函数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();
  • 如果引用的时候(require),没有加路径,则默认从node_modules文件夹中寻找
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文件,让程序可读性强

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");

10.处理post表单提交

10.1 对于post文件上传这样的过程,主要有以下几个部分:

  • 获取http请求报文肿的头部信息,我们可以从中获得是否为POST方法,实体主体的总大小,边界字符串等,这些对于实体主体数据的解析都是非常重要的
  • 获取POST数据(实体主体)
  • 对POST数据进行解析
  • 将数据写入文件

10.2 获取http请求报文头部信息

利用nodejs中的 http.ServerRequest中获取 :

  • request.method用来标识请求类型
  • request.headers获得请求头
    • content-type 包含了表单类型和边界字符串信息
      • get请求的headers中没有content-type这个字段
      • post 的 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' }

10.3 使用nodejs原生处理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");

10.4 使用nodejs表单插件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] } } }

11.其它

path模块提供了一些工具函数,用于处理文件与目录的路径

  • path.extname()

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')
// 返回: ''
  • path.normalize(path)
  • path.format(pathObject)
  • path.parse(path)

util模块主要用于支持 Node.js 内部 API 的需求。 大部分实用工具也可用于应用程序与模块开发者

  • util.inspect() 方法返回 object 的字符串表示,主要用于调试

四、模板引擎--ejs

官网 | 中文文档

案例一

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);

五、express框架

官网 | 中文官网

1.安装

npm install express --save

常用方法:

  • get
    • GET请求的参数在URL中,在原生Node中,需要使用url模块来识别参数字符串。在Express中,不需要使用url模块了。可以直接使用req.query对象。
  • post
    • POST请求在express中不能直接获得,必须使用body-parser模块。使用后,将可以用req.body得到参数。但是如果表单中含有文件上传,那么还是需要使用formidable模块。
  • use
  • set

2.案例

2.1 路由功能

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类数组对象

2.2 静态资源功能

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/就可以访问到

2.3 使用模板引擎

默认从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!');
  • 如果想使用不同的Content-Type,可以:
    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);

2.4 express默认post中间件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的路由

2.5 路由中间件概念

路由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);

2.6 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);

六、mongodb

1. NoSQL

非结构型数据库。没有行、列的概念。用JSON来存储数据。
集合就相当于“表”,文档就相当于“行”。

NoSQL数据库在以下的这几种情况下比较适用:

  • 数据模型比较简单;
  • 需要灵活性更强的IT系统;
  • 对数据库性能要求较高;
  • 不需要高度的数据一致性;
  • 对于给定key,比较容易映射复杂值的环境。

我们看,有些系统,特别需要筛选。比如,筛选出所有女生大于20岁的。那么SQL型数据库,非常擅长!因为它有行、列的概念。

但是,有些系统,真的不需要进行那么多的筛选,比如站内信。站内信只需要存储就好了。不需要筛选。那么NoSQL的。

NoSQL不是银弹,没有资格挑战老牌数据库,还是特定情况下,是适合的。

2. mongodb安装

官网 | 官方手册 | 中文手册 | 菜鸟教程

去官网找到对应的版本下载安装,然后将安装路径添加到环境变量中(重要)。

3. 在命令行工具中使用

3.1 mongod 开机

开机命令,启动数据库

mongod --dbpath 数据库文件路径

启动 mongodb,默认数据库目录即为 /data/db。注意:如果你的数据库目录不是/data/db,可以通过 --dbpath 来指定。(参考:菜鸟教程)

--dbpath就是选择数据库文档所在的文件夹。

也就是说,mongoDB中,真的有物理文件,对应一个个数据库。U盘可以拷走。

一定要保持,开机这个CMD不能动了,不能关,不能ctrl+c。 一旦这个cmd有问题了,数据库就自动关闭了。

注意:在mac下,开机必须加上sudo,否则会开机失败。

3.2 mongo 使用数据库

1)打开命令行工具

mongo

2)列出所有数据库:

show dbs

3)使用某个数据库

use 数据库名字

如果想新建数据库,也是use。use一个不存在的,就是新建。

4)查看当前所在数据库

db
  1. 查看数据库中的所有集合
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 } );

3.3 mongoimport 导入数据

有时候,我们不可能一条一条的insert。所以,我们希望在外部写好数据库的形式,然后导入数据库:

mongoimport --db test --collection restaurants --drop --file primer-dataset.json
  • --db test 想往哪个数据库里面导入
  • --collection restaurants 想往哪个集合中导入
  • --drop 把集合清空
  • --file primer-dataset.json 哪个文件

这样,我们就能创建一个json文件,然后用mongoimport命令导入,这样学习数据库非常方便。

3.3 查找数据

1)查找数据,用findfind中没有参数,那么将列出这个集合的所有文档:

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 } )

3.4 修改数据

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});

3.5 删除数据

db.restaurants.remove( { "borough": "Manhattan" } )

默认是删除所有的匹配,如果只想删除一行,那么添加{ justOne: true }

db.restaurants.remove( { "borough": "Queens" }, { justOne: true } )

4. Node.js操作MongoDB

建议多查阅:官网nodejs驱动文档

4.1 安装

npm install mongodb --save

4.2 案例

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);

4.3 把常用的增删改查,都封装成为module。

开发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"
}

4.4 具备增删改的留言本案例

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);

七、cookie和session

1. cookie

HTTP是无状态协议。简单地说,当你浏览了一个页面,然后转到同一个网站的另一个页面,服务器无法认识到,这是同一个浏览器在访问同一个网站。每一次的访问,都是没有任何关系的。

那么世界就乱套了,比如我上一次访问,登陆了,下一次访问,又让我登陆,不存在登陆这事儿了。

Cookie是一个简单到爆的想法:当访问一个页面的时候,服务器在下行HTTP报文中,命令浏览器存储一个字符串;浏览器再访问同一个域的时候,将把这个字符串携带到上行HTTP请求中。

第一次访问一个服务器,不可能携带cookie。 必须是服务器得到这次请求,在下行响应报头中,携带cookie信息,此后每一次浏览器往这个服务器发出的请求,都会携带这个cookie。

特点:

  • cookie是不加密的,用户可以自由看到;
  • 用户可以删除cookie,或者禁用它
  • cookie可以被篡改
  • cookie可以用于攻击
  • cookie存储量很小。未来实际上要被localStorage替代,但是后者IE9兼容。

express中的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);

2. session

Session不是一个天生就有的技术,而是依赖cookie。

session依赖cookie,当一个浏览器禁用cookie的时候,登陆效果消失; 或者用户清除了cookie,登陆也消失。

session比cookie不一样在哪里呢? session下发的是乱码,并且服务器自己缓存一些东西,下次浏览器的请求带着乱码上来,此时与缓存进行比较,看看是谁。所以,一个乱码,可以对应无限大的数据。

任何语言中,session的使用,是“机理透明”的。他是帮你设置cookie的,但是足够方便,让你感觉不到这事儿和cookie有关。

express中的session

安装:

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加密是函数型加密。就是每次加密的结果一定相同,没有随机位。

特点:

  • 不管加密的文字,多长多短,永远都是32位英语字母、数字混合。
  • 哪怕只改一个字,密文都会大变。
  • MD5没有反函数破解的可能,网上的破解工具,都是通过字典的模式,通过大量列出明-密对应的字典,找到明码。两次加密网上也有对应的字典。所以我们不要直接用一层md5,这样对黑客来说和明码是一样。

MD5常用于作为版本校验。可以比对两个软件、文件是否完全一致。

node中自带了一个模块,叫做crypto模块,负责加密。

首先创建hash,然后update和digest:

var md5 = crypto.createHash('md5');
var password = md5.update(fields.password).digest('base64');

封装一个md5模块

md5.js

var crypto = require("crypto");
module.exports = function(mingma){
    var md5 = crypto.createHash('md5');
    var password = md5.update(mingma).digest('base64');
    return password;
}

九、图像处理

gm官网

安装

mac下安装教程

使用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软件。免费的。

windows下

装完之后,可视化工具一点用都没有,从桌面上删除。我们要把安装目录设置为环境变量。

控制台命令

//格式转换
gm convert a.bmp a.jpg

//更改当前目录下*.jpg的尺寸大小,并保存于目录.thumb里面
gm mogrify -resize 320x200 danny.jpg

nodejs中使用gm

安装:

npm install gm --save

使用:

  • node.js缩略图的制作:
  var fs = require('fs');
  var gm = require('gm');

  gm('./danny.jpg')
      .resize(50, 50,"!")
      .write('./danny2.jpg', function (err) {
          if (err) {
              console.log(err);
          }
      });
  • node.js头像裁切:
gm("./danny.jpg").crop(141,96,152,181).write("./2.jpg",function(err){
     //141  96 是宽高 。  152  181是坐标
});

十、mongoose

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);
//     }
// });

十一、web Socket和Socket.IO框架

Socket.IO

HTTP无法轻松实现实时应用:

  • HTTP协议是无状态的,服务器只会响应来自客户端的请求,但是它与客户端之间不具备持续连接。
  • 我们可以非常轻松的捕获浏览器上发生的事件(比如用户点击了盒子),这个事件可以轻松产生与服务器的数据交互(比如Ajax)。但是,反过来却是不可能的:服务器端发生了一个事件,服务器无法将这个事件的信息实时主动通知它的客户端。只有在客户端查询服务器的当前状态的时候,所发生事件的信息才会从服务器传递到客户端。

但是,确实聊天室确实存在。

方法:

  • 长轮询:客户端每隔很短的时间,都会对服务器发出请求,查看是否有新的消息,只要轮询速度足够快,例如1秒,就能给人造成交互是实时进行的印象。这种做法是无奈之举,实际上对服务器、客户端双方都造成了大量的性能浪费。
  • 长连接:客户端只请求一次,但是服务器会将连接保持,不会返回结果(想象一下我们没有写res.end()时,浏览器一直转小菊花)。服务器有了新数据,就将数据发回来,又有了新数据,就将数据发回来,而一直保持挂起状态。这种做法的也造成了大量的性能浪费。

WebSocket协议能够让浏览器和服务器全双工实时通信,互相的,服务器也能主动通知客户端了。

  • WebSocket的原理非常的简单:利用HTTP请求产生握手,HTTP头部中含有WebSocket协议的请求,所以握手之后,二者转用TCP协议进行交流(QQ的协议)。现在的浏览器和服务器之间,就是QQ和QQ服务器的关系了。
    所以WebSocket协议,需要浏览器支持,更需要服务器支持。
  • 支持WebSocket协议的浏览器有:Chrome 4、火狐4、IE10、Safari5
  • 支持WebSocket协议的服务器有:Node 0、Apach7.0.2、Nginx1.3

Node.js上需要写一些程序,来处理TCP请求。

  • Node.js从诞生之日起,就支持WebSocket协议。不过,从底层一步一步搭建一个Socket服务器很费劲(想象一下Node写一个静态文件服务都那么费劲)。所以,有大神帮我们写了一个库Socket.IO。
  • Socket.IO是业界良心,新手福音。它屏蔽了所有底层细节,让顶层调用非常简单。并且还为不支持WebSocket协议的浏览器,提供了长轮询的透明模拟机制。
  • Node的单线程、非阻塞I/O、事件驱动机制,使它非常适合Socket服务器。

安装

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,不用直接操作数据库,操作对象,这个对象自动持久。

js高级--闭包

闭包

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)
}

js DOM事件--事件停止传播与效果阻止,事件的解绑定

阻止事件传播

思考:事件(捕捉/冒泡)的过程中,能否停止事件的传播?
比如,被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高级--this

js的this到底是谁?

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)
*/

this练习

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,将会污染全局变量

js高级--面向对象之封装

用构造函数来创建对象

function Dog() {
  this.leg = 4;
  this.bark = function() {
    alert('旺旺');
  }
}

var huzi = new Dog();
huzi.bark();

/*
上面我们并没有完成面向对象的“封装”

所谓封装---就要封闭一部分,外界无法访问,
开放一部分,通过开放部分间接访问是有部分
*/

用闭包来完成js面向对象之私有属性

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语言精粹》

js DOM事件--事件委托

开发一个五子棋游戏

  1. 造棋盘 table+背景图片就可以实现
  2. 开发下棋功能
  3. 判断胜负

问: 在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);
  }
}

js高级--动态语言谈不上多态

多态不是“神奇的效果”,而是在静态语言相对动态语言,类型不灵活的一个体现。

由上,在灵活的动态语言中,谈不上“多态”的概念。

例如 在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();
}

经过上面的改造-------本来只能接受HashiqiJingba实例的,
现在能接受的范围宽了,只要是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没有类的概念,因此---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的?

答:老虎首先在自身对象上寻找,没有爬树方法,valueOf方法
去找原型,原型cat对象上有此方法,因此得以调用climb()方法
但valueOf()方法仍没找到,
于是继续沿着原型查找,
找到cat空对象,仍没有valueOf,
再找,找到Object对象,有valueOf方法,调用成功。

对象->原型->原型的原型->Object对象-->Null
这样的一条链----称为原型链

对象的属性和方法,就是沿着原型链来查找和调用的!
也就是JS中的原型继承

image

练习:给所有的对象,加一个唱歌方法

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(); // 汪汪

js高级--面向对象之静态方法

js面向对象的静态方法

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高级--原型冒充及复制继承

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();

js DOM事件--绑定事件的2种常见方式

<script>
function t() {
  alert("我被点穴了")
}

/*
在这个过程中,a标签上绑定了onclick事件,
用句柄来描述就是------a标签的onclick句柄上,绑定了t函数

分析:
1:DOM对象的句柄
2:句柄上绑定的函数
3:事件发生的那一瞬间,关于事件的各种信息,如时间,如发生时鼠标在屏幕上的坐标,事件类型等等
这些信息,被打包成一个对象,便于我们获取 -----这个对象称为事件对象。
*/
</script>

<a href="#" onclick="t();">点我</a>

第1种绑定事件的方式

即把事件写在标签的属性里 如:<a href="#" onclick="t()"></a>

这是DOM 0 级的标准(非常古老的标准)

好处:大家都会,几乎所有的浏览器都支持。
坏处:夹杂在html代码中,代码不简洁;这种事件写法,效率不高;不符合“行为,结构,样式相分离”。

第2种绑定事件的方式

用事件属性来绑定事件函数

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个参数来传

html导入favicon

<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" />

css技巧--元素高度与宽度等比例变化

引用:http://www.cnblogs.com/heyach/p/6494872.html

在如今响应式布局的要求下,很多能自动调整尺寸的元素能够做到高宽自适应,如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来实现此功能

通过以上几个方案的实验,发现宽度的自适应是根据viewportwidth来调整的,比如{width:50%}就是浏览器可视区域的50%,resize之后也会自动调整。

height指定百分比后,他会自行找到viewportheight来调整,跟width一毛钱关系没有,自然两者不能达到比例关系了。通过这个思路,要找到一个能跟viewportwidth扯上裙带关系的属性,就能解决这个问题了。

这个属性就是paddingpadding是根据viewportwidth来调整的,巧就巧在padding-toppadding-bottom也是根据viewportwidth来计算的,那么通过设置这个属性就能跟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属性了,它的计算是包括divcontentpadding的,也就是说,

原来你的div可能是个{width:50px;height:50px;padding:0}div,现在变成{width:50px;height:0;padding-bottom:50px;}div了,尺寸还是一样的,通过指定这个div的子元素的定位,一样可以正常显示。

这样就可以通过设定父级元素father、我们需要的元素element、子级元素datail来实现任意情况下该需求(div宽高定比例)。

Draggabilly中文文档

Draggabilly中文文档

根据Draggabilly官方文档翻译,由于英文水平有限,部分内容可能有误。本文档只翻译了主要的选项配置等,想了解更多请查看官方文档。

js拖拽插件

1.0版本支持ie8+和多点触控
2.0版本支持ie10+和多点触控

github地址

安装

获取源码打包文件

包管理工具

使用npm安装:npm install draggabilly
使用Bower安装:bower install draggabilly

CDN

通过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 当元素开始拖拽时添加

选项(配置)

axis

类型: 字符串(String)
值: 'x' 或者 'y'

axis: 'x'

元素允许在水平或垂直方向上拖动,默认是水平垂直均可

containment

类型:元素、选择器字符串 或 布尔值

containment: '.container'

包含元素边界的移动。如果true,容器将是父元素。

grid

类型: 数组(Array)
值: [ x, y ]

grid: [ 20, 20 ]

网格式移动元素,每x和y像素移动元素

handle

类型: 选择器字符串

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');
});

dragStart

拖动开始移动前触发,当移动小于2像素时表现为点击。

// jQuery
$draggable.on( 'dragStart', function( event, pointer ) {...})
// 原生JS
draggie.on( 'dragStart', function( event, pointer ) {...})
  • event - 类型: 事件 - 原生的mousedowntouchstart事件
  • pointer - 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 这个事件对象有.pageX.pageY

dragMove

当元素移动时触发。

// 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 }

dragEnd

当元素停止移动时触发。

// jQuery
$draggable.on( 'dragEnd', function( event, pointer ) {...})
// 原生JS
draggie.on( 'dragEnd', function( event, pointer ) {...})
  • event - 类型: 事件 - the original mouseup or touchend event
  • pointer - 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 事件对象有.pageX.pageY

pointerDown

当用户指针按下(鼠标、触摸)时触发。

// jQuery
$draggable.on( 'pointerDown', function( event, pointer ) {...})
// 原生JS
draggie.on( 'pointerDown', function( event, pointer ) {...})
  • event - 类型: 事件 - the original mouseup or touchend event
  • pointer - 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 事件对象有.pageX.pageY

pointerMove

当用户的指针移动时触发。

// 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 }

pointerUp

当用户指针离开元素时触发。

// jQuery
$draggable.on( 'pointerUp', function( event, pointer ) {...})
// 原生JS
draggie.on( 'pointerUp', function( event, pointer ) {...})
  • event - 类型: 事件 - the original mouseup or touchend event
  • pointer - 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 事件对象有.pageX.pageY

staticClick

用户的指针压下并没有开始拖动。
单击事件很难拖动界面检测,因为他们是当用户的鼠标触发。draggabilly的staticclick事件解决了,因为它被触发时,用户没有拖。

// jQuery
$draggable.on( 'staticClick', function( event, pointer ) {...})
// 原生JS
draggie.on( 'staticClick', function( event, pointer ) {...})
  • event - 类型: 事件 - the original mouseup or touchend event
  • pointer - 类型: 鼠标事件(MouseEvent)或触控事件(Touch)对象 - 事件对象有.pageX.pageY

方法

disable

// jQuery
$draggable.draggabilly('disable')
// vanilla JS
draggie.disable()

enable

// jQuery
$draggable.draggabilly('enable')
// vanilla JS
draggie.enable()

destroy

// jQuery
$draggable.draggabilly('destroy')
// vanilla JS
draggie.destroy()

jQuery.fn.data('draggabilly')

从一个jQuery对象得到draggabilly实例。draggabilly实例访问draggabilly有用的属性。

var draggie = $('.draggable').data('draggabilly')
// access Draggabilly properties
console.log( 'draggie at ' + draggie.position.x + ', ' + draggie.position.y )

Properties(属性)

position

{ 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>

0

如何让它水平垂直居中呢?

先说一说 最经典的水平垂直居中 的方法

.parent {
  width: 640px;
  height: 480px;
  background: gray;
}

.child {
  /* 注意:这种方法必须指定宽高 */
  width: 200px;
  height: 200px;

  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto;
}

image

这种方法必须指定宽高,那如果我不想指定宽高,又该如何处理呢?

方法1 最简单的flex布局,外层容器加上如下样式即可

.parent {
  display: flex;
  justify-content: center;
  align-items: center;
}

.child {

}

image

方法2 利用table-cell

.parent {
  display:table-cell;
  text-align:center;
  vertical-align:middle;
}

.child {
  vertical-align:middle;
  display:inline-block;
}

效果同上

方法3 使用CSS3 transform

.parent {
  position: relative
}

.child {
  transform: translate(-50%,-50%);
  position: absolute;
  top: 50%;
  left: 50%;
}

效果同上

js高级--arguments

arguments

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.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

js DOM事件--详解捕捉模型与冒泡模型

<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 DOM事件--addEventListener高级事件绑定

一个页面的js由多人团队开发,A,B都写了onload事件,则 后面的事件,把前面的onload属性值给覆盖了。

那么如何解决这个问题呢?

第3种绑定:addEventListener

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>

js高级--作用域

作用域

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)区域。

声明变量var的作用

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布局兼容性解决方案

摘自:http://www.webzsky.com/?p=689S

引言

虽然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/

flex兼容常用的布局代码

/* 子元素-平均分栏 */
.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设置容器。

注意:上面给的代码并不是完全兼容各个高端浏览器的,但要比任何其它现有代码兼容性好

js高级--词法分析

词法分析

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

arguments是什么?
答:是1个对象,是长得很像数组的对象

arguments内容是什么?
答:arguments是函数运行时的实参列表。

(function (d, e, f) {
	console.log(arguments);
})('hello', 'world', '');

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.