Eric Chang's Blog


  • 首页

  • 归档

  • 分类

  • 标签

  • 生活

  • 关于

  • 搜索

javascript 函数解析

发表于 2015-10-26 | 分类于 Web |

引言

  • javascript中,函数是重要的机制,可以避免页面载入时执行该脚本,这些代码只能被事件激活,或者在函数被调用时才会执行;
  • 本文总结了javascript函数的定义和调用方法,以供参考;

javascript函数的定义

  • 直接定义函数:
1
2
3
function test(){   
alert("...");
}
  • 通过函数字面量定义
1
2
3
var func = function(x){  
alert(x);
}
  • 通过构造函数定义:
1
2
var x = 1
var func2 = new Function(x,alert(x));

javascript函数的调用

  • 直接通过函数名调用:
1
2
3
4
5
6
function test(){   
alert("...");
}

test();
window.onload = test();
  • 通过变量调用:
1
2
3
4
5
6
function test(){   
alert("...");
}

var fun = test;
fun();
  • 下面两种方法可让函数自动执行
1
2
3
4
5
6
7
(function test(){   
alert("...");
})
()


(function test(){
alert("...");
}())

  • 下面的代码会发生错误,请注意括号
1
2
3
function test(){   //error
alert("...");
}()

JavaScript 闭包详解

发表于 2015-10-26 | 分类于 Web |

js变量作用域

  • 变量的作用域无非就是两种:全局变量和局部变量;
  • Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量:
1
2
3
4
5
var n=999;
  function f1(){
    alert(n);
  }

f1(); // 999

  • 但是,在函数外部自然无法读取函数内的局部变量:
1
2
3
4
function f1(){
    var n=999;
  }
alert(n); // error
  • 除非不用var声明:
1
2
3
4
5
function f1(){
    n=999;
  }

  f1();

alert(n); // 999
  • 总之,js中作用域是链式的,内部可获取外部变量,反之不行;
  • 如果函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的,但是反过来就不行,f2内部的局部变量,对f1就是不可见的;
  • 既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗;
  • 这种打破常规,实现在函数外部访问函数私有变量的机制叫做闭包;

js什么时候进行垃圾回收?

  • 在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;
  • 如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收;

闭包(Closure)的定义

  • 闭包就是能够读取其他函数内部变量的函数;
  • 由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成”定义在一个函数内部的函数”;
  • 在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁;

闭包的深入理解

看下面程序:

1
2
3
4
5
6
7
8
9
10
11
12
function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
  • result实际上就是闭包f2函数,它一共运行了两次,第一次的值是999,第二次的值是1000,这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除;
  • 原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中(不满足上面任何一条垃圾回收条件),而f2的存在依赖于f1,因此f1也始终在内存中(不满足上面任何一条垃圾回收条件),不会在调用结束后被垃圾回收机制(garbage collection)回收;
  • 这段代码中另一个值得注意的地方,就是”nAdd=function(){n+=1}”这一行;
    • 首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量;
    • 其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作;

使用闭包需要注意

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露,解决方法是,在退出函数之前,将不使用的局部变量全部删除;
  • 闭包会在父函数外部,改变父函数内部变量的值,所以如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值;

本文参考:学习Javascript闭包(Closure)

JavaScript中this关键字详解

发表于 2015-10-25 | 分类于 Web |

引言

  • js中,this关键是令人头疼的问题,它的指代对象和this所处的上下文环境、函数调用者等多重因素有关;
  • 我们要解决的问题是,this关键字代指哪个对象;
  • 本文用丰富的实例让您清晰、明确的判断各种情况下,this关键字所指代的对象;

判断原则:

  • 原则一:this指的是直接调用函数的那个对象,没有的话就值window;
  • 原则二:this代指的对象就是它使用的时候(不是定义的时候)所处的环境的上下文;
  • 原则三:当new关键字出现时,this指代new出来的那个对象;
  • this是js关键字,某些程序中出现的that仅仅是普通的变量,不是关键字;
  • 下面,我们用实例说话,一一验证上述原则;

无对象调用函数时this指代window的例子

  • 例一:
1
2
3
4
5
6
function test(){
this.x = 1;
alert(this.x);
}
test(); //1
alert(x); //1
  • 例二:
1
2
3
4
5
var x = 1;
function test(){
alert(this.x);
}
test(); //1
  • 例三:
1
2
3
4
5
6
var x = 1;
function test(){
this.x = 0;
}
test();
alert(x); //0
  • 例四:
1
2
3
4
5
6
7
8
9
var x = 0;
function test(){
alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m.apply(); //0 apply参数为空时,默认调用全局对象,故this指向window
o.m.apply(o); //1 test函数的调用者是o,故this指代对象o
  • 例五:
    • 执行第一个this时,function调用者为obj,故this指代obj对象;
    • 执行f函数和匿名函数时,函数没有直接的调用者,故this指代window;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var context = "global";

var obj = {
context: "object",
method: function () {
console.log(this + ":" +this.context); // [object Object]:object

function f() {
var context = "function";
console.log(this + ":" +this.context); // [object Window]:global
};
f();

(function(){
var context = "function";
console.log(this + ":" +this.context); // [object Window]:global
})();
}
};

obj.method();

有对象调用函数时this指代调用函数者的例子

  • 例一:
    • o调用了test函数,故this指代对象o
1
2
3
4
5
6
7
function test(){
alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m(); //1
  • 例二:
    • getInfo()函数的调用者是ext1,this指代ext1;

this1

  • 例三:
    • getInfo()函数的调用者是ext1,this指代ext1;
    • this.id和this.name都是代指ext1对象的id和name,但是由于ext1对象本身没有name属性,所以需要到原型链上查找name属性;

this2

  • 例四:
    • 内层的funcion中包含了this,但是,内层function没有直接调用者;
    • 故这里的this指window;

this3

  • 例五:
    • object对象调用了外层函数,所以var that = this中的this指代object对象,执行完此句后,that也指代object对象;
    • that并不是js的关键字,仅仅是一个普通的变量,所以下面return that.name的时候,返回的是object.name;

this4

  • 例六:
    • object对象直接调用了外层的function,故var that = this中的this指代object对象,执行完此句后,that也指代object对象;
    • 内层function没有直接调用者,故内层函数中的this指代window;

this5

  • 例七:
    • point对象调用moveTo方法,故this指代point对象;
1
2
3
4
5
6
7
8
9
10
11
12
var point = { 
x : 0,
y : 0,
moveTo : function(x, y) {
this.x = this.x + x;
this.y = this.y + y;
}
};
point.moveTo(1,1);
console.log(point.x); //1
console.log(point.y); //1
console.log(x); //x is not defined
  • 例八:
    • moveX和moveY函数没有调用者,所以其中的this指代window,而不是point对象;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var point = { 
x : 0,
y : 0,
moveTo : function(x, y) {
var moveX = function(x) {
this.x = x;
};

var moveY = function(y) {
this.y = y;
};

moveX(x);
moveY(y);
}
};
point.moveTo(1,1);
point.x; //=>0
point.y; //=>0
x; //=>1
y; //=>1

this指代new出来的那个对象

  • 例一:
    • 由于存在new关键字,this指代的对象是new出来的对象,即对象o;
1
2
3
4
5
function test(){
this.x = 1;
}
var o = new test();
alert(o.x); //1
  • 例二:
    • this指代对象o,而不是window;
1
2
3
4
5
6
var x = 2;
function test(){
this.x = 1;
}
var o = new test();
alert(x); //2
  • 例三:
    • np对象是new出来的,所以this指代np对象;
    • p对象不是new出来的,且Point函数无调用者,故this指代window;
1
2
3
4
5
6
7
8
9
function Point(x,y){ 
this.x = x;
this.y = y;
}
var np=new Point(1,1);
np.x; //1
var p=Point(2,2);
p.x; //error, p是一个空对象undefined
window.x; //2
  • 例四:
    • apply使得Point函数的调用者为p2,故this指代p2对象;
1
2
3
4
5
6
7
8
9
10
11
12
13
function Point(x, y){ 
this.x = x;
this.y = y;
this.moveTo = function(x, y){
this.x = x;
this.y = y;
}
}

var p1 = new Point(0, 0);
var p2 = {x: 0, y: 0};
p1.moveTo.apply(p2, [10, 10]); //apply实际上为p2.moveTo(10,10)
p2.x; //10

bind/call/apply重定向this关键字指向

  • 例一:
    • apply和call将函数的调用者转变了,所以this指代的对象随之转变;
    • bind创建一个新的函数,这个函数中的this对象指代的内容也由bind函数指定;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function add(numA, numB){
console.log( this.original + numA + numB);
}

add(1, 2); // NaN

var obj = {original: 10};
add.apply(obj, [1, 2]); // 13
add.call(obj, 1, 2); // 13

var f1 = add.bind(obj);
f1(2, 3); // 15

var f2 = add.bind(obj, 2);
f2(3); // 15

js事件中的this指代

  • 例一:
    • 前面的一个,console调用者是button对象,故this指代button对象;
    • 中间的一个,匿名函数没有直接调用者,所以this指代window;
    • 第三个,通过bind重定向函数中的this指向,所以this代表button对象;
1
2
3
4
5
6
7
8
document.write('<button onclick="console.log(this)">Show this</button>');
// <button onclick="console.log(this)">Show this</button>

document.write('<button onclick="(function(){console.log(this);})()">Show this</button>');
// window

document.write('<button onclick="((function(){console.log(this);}).bind(this))()">Show this</button>');
// <button onclick="((function(){console.log(this);}).bind(this))()">Show this</button>
  • 其他的js事件中的this关键字指代请参考这里;

this关键字指代对象的暂存方法

  • 说明:
    • 有些时候希望在this指代正确的对象的时候将this指代的对象暂存起来,备用;
  • 例一:
    • 下面程序将this指代的bar对象暂存到self对象中;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var bar = {
name: "bar",
body: document.getElementsByTagName("body")[0],

greeting: function(){
console.log("Hi there, I'm " + this + ":" + this.name);
},

anotherMethod: function () {
var self = this;
this.body.addEventListener("click", function(){
self.greeting();
});
}
};

bar.anotherMethod(); // Hi there, I'm [object Object]:bar
  • 上面的程序也可以用bind实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var bar = {
name: "bar",
body: document.getElementsByTagName("body")[0],

greeting: function(){
console.log("Hi there, I'm " + this + ":" + this.name);
},

anotherMethod: function () {
this.body.addEventListener("click", (function(){
this.greeting();
}).bind(this));
}
};

bar.anotherMethod(); // Hi there, I'm [object Object]:bar

node.js开发环境配置

发表于 2015-10-24 | 分类于 Web |

什么是node.js

  • 维基百科的介绍:Node.js是谷歌V8引擎、libuv平台抽象层以及主体使用Javscript编写的核心库三者集合的一个包装外壳;
  • node.js在国内外的使用情况:
    • linkedin移动版正在向node.js迁移;
    • twitter的消息队列用node.js实现;
    • 知乎、网易的推送信息用node.js实现;
    • 阿里的云计算平台也要部分向node.js迁移;
    • …..
  • 上面的介绍听起来很抽象,其实,node.js就是一个能够在服务器端运行javascript的框架,实现了众多web开发者在server端使用javascript开发的梦想;
  • node.js有很多优势,比如:
    • 模块化;
    • client和server双向实时通信;
    • 高并发(单线程处理多个请求);
    • IO非阻塞(一件事情正在完成时可以去干另一件事);
  • node.js的这些优势,使得它的应用场景十分广泛:
    • 聊天;
    • 队列输入;
    • 推送信息;
    • 数据流传输;
    • 代理;
    • 只要是符合“计算简单、高并发、需要client和server双向通信”的业务模型都可以用!
  • 本文重点介绍了node.js在windows下的开发环境搭建;

node.js开发环境配置

  • node.js开发环境需要配置如下内容:
    • 在windows上安装Sublime和Webstorm;
    • 在windows上安装VirtualBox和Centos虚拟机;
    • 在windows上安装xShell和xFtp,方便在本机和虚拟机之间传送消息或文件;
    • 在虚拟机上安装Redis和MongoDB;
  • 下面会一一讲解这些步骤;

VirtualBox的安装

  • 下载VirtualBox
  • 直接双击安装即可;

在VirtualBox中创建虚拟机

  • 内存分配1024M;
  • 磁盘种类选择默认种类;
  • 安装CentOs basic web server版本,同时安装development tolls;
  • CentOs7的iso镜像文件可从网上下载,添加到虚拟机配置中;
  • 如果你所在的局域网路由器DHCP功能启用了,就设置网卡为桥接网卡,系统会自动分配IP地址;
  • 如果你所在的局域网路由器DHCP功能禁用了,就设置网卡为NAT模式,需要进一步进行端口映射的配置(下面会将具体配置方法);
  • 安装完reboot虚拟机;

虚拟机的配置

  • 网卡自启动的配置:
    • 进入下列配置文件,并将最后一行ONBOOT设置为yes;
1
vi /etc/sysconfig/network-scripts/ifcfg-enp0s3
  • 如果你所在的局域网路由器DHCP功能启用了,此时就可通过ifconfig命令查询到ipv4地址,记下这个地址(当然你可以将它和它对应的域名配置到host文件中);
  • 如果你所在的局域网路由器DHCP功能禁用了,此时就将虚拟机网卡配置改为NAT网络模式,点击“端口转发按钮”,按照下图方式配置端口映射,这个设置的意思是,只要是访问本机(127.0.0.1)5905端口的消息全部转发给ip为10.0.2.15(这个ip是在虚拟机上运行ifconfig得到的,这是虚拟机在NAT模式下自行分配的ip)的22端口(SSH专用端口为22);

port-map

  • 安装epel:
1
yum install epel-release

xShell和xFtp安装

  • 介绍:
    • xShell是一款在windows上连接远程终端的软件,类似于SecureCRT,这里我们用它来连接本机上的虚拟机;
    • xFtp用来在windows PC和linux终端间传输文件,这里我们用xFtp来实现本机和上面CentOs之间的文件传输;
  • xShell安装:
    • 下载地址(需先注册获取下载链接):https://www.netsarang.com/xshell_download.html;
    • 下载后双击安装即可;
  • xFtp安装:
    • 下载地址(需先注册获取下载链接):https://www.netsarang.com/download/down_xfp.html;
    • 下载后双击安装即可;
  • xShell连接虚拟机:
    • 如果你所在的局域网路由器DHCP功能启用了,直接连接虚拟机的ipv4地址(通过ifconfig命令查询到),端口22;
    • 如果你所在的局域网路由器DHCP功能禁用了,此时连接127.0.0.1,端口5905,系统会自动根据上面的配置将数据映射到虚拟机;
    • 连接成功后,输入用户名密码即可登录虚拟机;

在虚拟机上安装相应软件

  • 安装Nodejs
    • 安装yum install nodejs
    • 查看是否正常安装node --version
  • 安装MongoDB
    • 安装yum install mongodb-server(mongodb服务器端)yum install mongodb(mongodb客户端)
    • 查看是否正常安装mongo --version
  • 安装redis
    • 安装yum install redis
    • 查看是否正常安装redis-cli --version

在windows安装相应软件

  • 应当安装下列IDE或编辑器,不再赘述:
    • sublime text;
    • WebStorm;

第一个node程序

  • 在虚拟机上新建文件test.js,内容为:
1
console.log("hello world!")
  • 在node运行这个文件node test.js
  • 执行结果为hello world!,终于实现了在server端运行javascript的梦想!

javascript map的实现

发表于 2015-10-03 | 分类于 Web |

引言

  • 在java语言中,map是一种重要的数据结构;
  • 在js中,没有现成的map实现;
  • 本文就带您来实现js中的map;

map的具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function Map(){
this.keys = new Array();
this.data = new Object();

this.put = function(key,value){
if(this.data[key]==null){
this.keys.push(key);
}
this.data[key] = value;
}

this.get = function(key){
return this.data[key];
}

this.remove = function(key){
var index = this.keys.indexOf(key);
this.keys.splice(index,1);
this.data[key] = null;
}

this.containsKey = function(key){
if(this.keys.indexOf(key)!= -1){
return true;
}else{
return false;
}
}

this.size = function(){
return this.keys.length;
}

this.entrySet = function(){
var len = this.keys.length;
var entrys = new Array(len);
for (var i = 0; i < len; i++) {
entrys[i] = {
key : this.keys[i],
value : this.data[this.keys[i]]
};
}
return entrys;
}

this.isEmpty = function(){
return this.keys.length == 0;
}
}

自定义map的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//定义map
var map = new Map();
map.put("name","xiaozhang");
map.put("age",20);
map.put("home","beijing");

//遍历map
var arr = map.entrySet();
for(var i =0; i<arr.length; i++){
document.write(arr[i].key+":"+arr[i].value+"<br>");
}

//获取 移除元素
map.get("name"); // xiaozhang
map.remove("home");

//其他方法
map.containsKey("name"); // true
map.size(); // 2
map.isEmpty(); // false

javascript 事件详解

发表于 2015-10-02 | 分类于 Web |

引言

  • javascript事件是用户在访问页面时执行的操作,事件处理程序用于捕捉用户在页面执行的操作;
  • javascript事件是动态化页面的重要方式;
  • 本文主要总结了重要的javascript事件,以及DOM0和DOM2对事件处理的异同;

事件大全

  • 下面总结了javascript事件,格式:事件名称+浏览器支持+说明

一般事件

  • onclick IE3、N2 鼠标点击时触发此事件
  • ondblclick IE4、N4 鼠标双击时触发此事件
  • onmousedown IE4、N4 按下鼠标时触发此事件
  • onmouseup IE4、N4 鼠标按下后松开鼠标时触发此事件
  • onmouseover IE3、N2 当鼠标移动到某对象范围的上方时触发此事件
  • onmousemove IE4、N4 鼠标移动时触发此事件
  • onmouseout IE4、N3 当鼠标离开某对象范围时触发此事件
  • onkeypress IE4、N4 当键盘上的某个键被按下并且释放时触发此事件.
  • onkeydown IE4、N4 当键盘上某个按键被按下时触发此事件
  • onkeyup IE4、N4 当键盘上某个按键被按放开时触发此事件

页面相关事件

  • onabort IE4、N3 图片在下载时被用户中断
  • onbeforeunload IE4、N 当前页面的内容将要被改变时触发此事件
  • onerror IE4、N3 出现错误时触发此事件
  • onload IE3、N2 页面内容完成时触发此事件
  • onmove IE、N4 浏览器的窗口被移动时触发此事件
  • onresize IE4、N4 当浏览器的窗口大小被改变时触发此事件
  • onscroll IE4、N 浏览器的滚动条位置发生变化时触发此事件
  • onstop IE5、N 浏览器的停止按钮被按下时触发此事件或者正在下载的文件被中断
  • onunload IE3、N2 当前页面将被改变时触发此事件

表单相关事件

  • onblur IE3、N2 当前元素失去焦点时触发此事件
  • onchange IE3、N2 当前元素失去焦点并且元素的内容发生改变而触发此事件
  • onfocus IE3 、N2 当某个元素获得焦点时触发此事件
  • onreset IE4 、N3 当表单中RESET的属性被激发时触发此事件
  • onsubmit IE3 、N2 一个表单被递交时触发此事件

滚动字幕事件

  • onbounce IE4、N 在Marquee内的内容移动至Marquee显示范围之外时触发此事件
  • onfinish IE4、N 当Marquee元素完成需要显示的内容后触发此事件
  • onstart IE4、 N 当Marquee元素开始显示内容时触发此事件

编辑事件

  • onbeforecopy IE5、N 当页面当前的被选择内容将要复制到浏览者系统的剪贴板前触发此事件
  • onbeforecut IE5、 N 当页面中的一部分或者全部的内容将被移离当前页面[剪贴]并移动到浏览者的系统剪贴板时触发此事件
  • onbeforeeditfocus IE5、N 当前元素将要进入编辑状态
  • onbeforepaste IE5、 N 内容将要从浏览者的系统剪贴板传送[粘贴]到页面中时触发此事件
  • onbeforeupdate IE5、 N 当浏览者粘贴系统剪贴板中的内容时通知目标对象
  • oncontextmenu IE5、N 当浏览者按下鼠标右键出现菜单时或者通过键盘的按键触发页面菜单时触发的事件
  • oncopy IE5、N 当页面当前的被选择内容被复制后触发此事件
  • oncut IE5、N 当页面当前的被选择内容被剪切时触发此事件
  • ondrag IE5、N 当某个对象被拖动时触发此事件 [活动事件]
  • ondragdrop IE、N4 一个外部对象被鼠标拖进当前窗口或者帧
  • ondragend IE5、N 当鼠标拖动结束时触发此事件,即鼠标的按钮被释放了
  • ondragenter IE5、N 当对象被鼠标拖动的对象进入其容器范围内时触发此事件
  • ondragleave IE5、N 当对象被鼠标拖动的对象离开其容器范围内时触发此事件
  • ondragover IE5、N 当某被拖动的对象在另一对象容器范围内拖动时触发此事件
  • ondragstart IE4、N 当某对象将被拖动时触发此事件
  • ondrop IE5、N 在一个拖动过程中,释放鼠标键时触发此事件
  • onlosecapture IE5、N 当元素失去鼠标移动所形成的选择焦点时触发此事件
  • onpaste IE5、N 当内容被粘贴时触发此事件
  • onselect IE4、N 当文本内容被选择时的事件
  • onselectstart IE4、N 当文本内容选择将开始发生时触发的事件

数据绑定

  • onafterupdate IE4、N 当数据完成由数据源到对象的传送时触发此事件
  • oncellchange IE5、N 当数据来源发生变化时
  • ondataavailable IE4、N 当数据接收完成时触发事件
  • ondatasetchanged IE4、N 数据在数据源发生变化时触发的事件
  • ondatasetcomplete IE4、N 当来子数据源的全部有效数据读取完毕时触发此事件
  • onerrorupdate IE4、N 当使用onBeforeUpdate事件触发取消了数据传送时,代替onAfterUpdate事件
  • onrowenter IE5、N 当前数据源的数据发生变化并且有新的有效数据时触发的事件
  • onrowexit IE5、N 当前数据源的数据将要发生变化时触发的事件
  • onrowsdelete IE5、N 当前数据记录将被删除时触发此事件
  • onrowsinserted IE5、N 当前数据源将要插入新数据记录时触发此事件

外部事件

  • onafterprint IE5、N 当文档被打印后触发此事件
  • onbeforeprint IE5、N 当文档即将打印时触发此事件
  • onfilterchange IE4、N 当某个对象的滤镜效果发生变化时触发的事件
  • onhelp IE4、N 当浏览者按下F1或者浏览器的帮助选择时触发此事件
  • onpropertychange IE5、N 当对象的属性之一发生变化时触发此事件
  • onreadystatechange IE4、N 当对象的初始化属性值发生变化时触发此事件

事件的使用

和html内联使用

1
2
3
<button id="btn" type="button" value="button" onclick="alert('aaa')">
button
</button>
1
2
3
4
5
6
7
8
<body>
<button onclick="demo()">button</button>
<script>
function demo(){
alert("aaa");
}
</script>

</body>

外部js使用

1
2
3
document.getElementById("txt").onchange = function(){
alert("change");
}

取消事件绑定

1
2
3
4
5
btn.onclick = function(e){
alert("ok");
};

btn.onclick = null; //取消绑定

DOM2事件详解

DOM2事件的特点

  • DOM2支持同一dom元素注册多个同种事件;
  • DOM2新增了捕获和冒泡的概念;
  • DOM2事件通过addEventListener和removeEventListener管理;

注册事件(addEventListener)

  • 参数一:事件名称,比上面DOM0的事件去掉”on”;
  • 参数二:事件内容,fucntion中;
  • 事件三:布尔型,true代表捕获事件,false代表冒泡事件;
1
2
3
outer.addEventListener('click',function(e){
alert("outer");
},false);

捕获和冒泡事件

  • 如果外层和内层的多层DOM结构中,不同的层都注册有事件,先执行谁呢?
  • 从外到内执行叫做捕获类型事件,从内到外执行叫做冒泡类型事件;
  • 捕获类型事件总是比冒泡类型事件先执行;
  • 如果两种类型的事件集中在了同一个元素身上,就不分这两种类型了,此时,哪个事件注册在前(在js中写在前面),他就先执行;
  • 对于一个页面元素来说,他的身上如果集中了多个相同的事件,不会覆盖,都会执行,且定义在前的先执行;
  • 如果普通事件和DOM2事件同时作用于一个元素,则不会出现后注册的覆盖先注册的,都执行,而且定义在前的先执行;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<body>
<div id="outer" style="width:300px;height:300px;">
<div id="inner">
<button id="btn" type="button" value="button" onclick="alert('aaa')">
button
</button>
</div>
</div>

<script>
var outer = document.getElementById("outer");
var inner = document.getElementById("inner");
var btn = document.getElementById("btn");

outer.addEventListener('click',function(e){
alert("outer"); //先执行
},true);

inner.addEventListener('click',function(e){
alert("inner"); //最后执行
},false);

btn.addEventListener('click',function(e){
alert("btn"); //再执行
},false);
</script>

</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<div id="outer" style="width:300px;height:300px;">
<div id="inner">
<button id="btn" type="button" value="button" onclick="alert('aaa')">
button
</button>
</div>
</div>

<script>
var outer = document.getElementById("outer");
var inner = document.getElementById("inner");
var btn = document.getElementById("btn");

inner.addEventListener('click',function(e){
alert("inner1"); //先执行
},false);

inner.addEventListener('click',function(e){
alert("inner2"); //后执行
},true);
</script>

</body>

stopPropagation阻止冒泡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<div id="outer" style="width:300px;height:300px;">
<div id="inner">
<button id="btn" type="button" value="button" onclick="alert('aaa')">
button
</button>
</div>
</div>
<script>
var outer = document.getElementById("outer");
var inner = document.getElementById("inner");
var btn = document.getElementById("btn");

outer.addEventListener('click',function(e){
alert("outer"); //不执行
},false);

inner.addEventListener('click',function(e){
e.stopPropagation(); //阻止冒泡
alert("inner"); //执行
},false);
</script>

</body>

removeEventListener解除事件绑定

1
2
3
4
5
6
7
var btn = document.getElementById("btn");
var fn = function(e){
alert("aa");
}

btn.addEventListener("click",fn,false); //绑定
btn.removeEventListener("click",fn,false); //解绑

IE8及之前版本浏览器的DOM2事件

說明

  • IE8以及其之前版本的浏览器并不支持addEventListener()和removeEventListener();
  • 相应的,IE定义了类似的方法attachEvent()和detachEvent();
  • attachEvent()和detachEvent()要求只有两个参数:事件类型(前面带上”on”)和事件处理函数,默认冒泡,不支持捕获;
  • 同一个元素定义的同种类型的事件,都会执行,不会覆盖,且定义在后的先执行;
  • stopPropagation()不起作用;

####示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<html>
<head>
<meta charset="UTF-8">
<title>test</title>
<style>
#outter{
width: 300px;
height: 300px;
background-color: #f00;
}

#inner{
width: 200px;
height: 200px;
background-color: #0f0;
}

#in{
width: 100px;
height: 100px;
background-color: #00f;
}

</style>

</head>
<body>
<div id="outter">
<div id="inner">
<div id="in">

</div>
</div>
</div>
<script>
document.getElementById("outter").attachEvent('onclick',function(){
alert("outter"); //最后執行
});
document.getElementById("inner").attachEvent('onclick',function(){
alert("inner"); //再执行
});

function test(){
alert("in1");
}
document.getElementById("in").attachEvent('onclick',test); //先执行
</script>

<script type="text/javascript" src="app.js"></script>
</body>
</html>

兼容性说明

  • 添加事件的兼容性:需要先做判断浏览器支持哪种事件处理机制;
1
2
3
4
5
if(div1.addEventListener){
div1.addEventListener('click', div1Fun, false);
}else if(div1.attachEvent){
div1.attachEvent('onclick', div1Fun);
}
  • 访问事件对象的兼容性:
1
2
3
4
5
6
 function myFun(event){
event = event || window.event;
var target = event.target || event.srcElement;
console.log(event.type);
console.log(target);
}

数据库并发冲突的解决

发表于 2015-10-02 | 分类于 Database |

引言

  • 数据库读写看似简单,其实蕴含着许多值得思考的问题;
  • 并发读写是令数据库设计者头疼的问题,其实,这个问题不仅令数据库设计者困惑,对于数据库的使用者来说,如何从业务层面高效处理并发读写问题也是不小的挑战;
  • 本文就此问题做简单探讨;

问题描述

  • 在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突,具体为:
    • 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新;
    • 脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改为2,用户A读到的值仍为6;

数据库层面的并发控制-悲观锁

  • 说明:
    • 假定本次读写一定会发生并发冲突,屏蔽一切可能违反数据完整性的操作;
    • 适用于实际业务中冲突发生可能性较大的情况,比如很有可能不同的人同时修改同一条数据库记录;
    • 如果实际业务中冲突发生可能性较小,则用悲观锁效率很低(因为上锁的代价大);
  • 使用:
    • 操作之前先上锁;
    • 需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL Server将在整个表上置排它锁直至该命令或事务结束,这将防止其他进程读取或修改表中的数据;

数据库层面的并发控制-乐观锁

  • 说明:
    • 假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性,乐观锁不能解决脏读的问题;
    • 适用于实际业务中冲突发生可能性较小的情况,比如几乎不可能不同的人同时修改同一条数据库记录;
  • 使用:
    • 数据表中专门有一个version字段(int型)记录版本信息;
    • 每当这条记录改动时,这条记录对应的version字段增1;
    • 每次更新时检查版本号是否一致,比如数据库中数据版本为6,更新提交时version=6+1,使用该version值(=7)与数据库version+1(=7)作比较,如果相等,则可以更新,如果不等则有可能其他程序已更新该记录,所以返回错误;

结论

  • 在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;
  • 但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法;
  • 上面介绍的锁都是数据库层面的锁,其实,如果从业务层面来看,上面的并发控制机制还是不够的,在数据库设计时,可以设计lock字段,如果要暂时锁定某些行,令lock=1时视为锁定,业务锁面向业务,更加可靠;

面向HTTP的Web性能提升方案

发表于 2015-10-01 | 分类于 Web |

引言

  • 前端优化是个永恒的课题
  • 前端优化的方案非常多,诸如加快脚本加载速度、缓存优化、提升js执行效率、运用ajax技术等等;
  • 本文重点介绍面向HTTP的一些Web性能提升方案;

到底发生什么了?

  • 建立TCP连接
    • 为了进行可靠的数据传输,TCP在进行发送数据之前,会进行TCP三次握手,以此确定接收方能够成功收取传输的数据,而建立连接的过程,必然是要耗费系统资源,以及时间资源的。
  • 服务端处理并响应
    • 当服务端接收到客户端发送来的请求之后,如果请求内容是静态资源,服务端会从硬盘中取出静态资源,然后将静态资源放在响应主体中,发送给客户端。如果是动态资源,服务端首先取出资源,并通过业务逻辑操作,动态生成最终的响应主体,然后发送给客户端。
  • 客户端渲染
    • 客户端接受到服务端传输过来的网络资源,然后进行渲染,绘制等,最终展示给用户。

优化点一:持久连接Keep-Alive

  • HTTP连接设计之初是请求-响应-关闭,也就是每建立一次HTTP连接,只能进行一次资源请求,当需要在同一目标服务器上获取多个资源的时候,就需要多次建立HTTP连接,而这个多次建立连接的过程,便降低了网站的性能。
  • 于是,出现了Connection:Keep-Alive,人称持久连接。Keep-Alive避免了建立或者说重新建立连接的过程,减少了HTTP连接。
  • 而与此配套的有Keep-Alive:timeout=120,max=5
  • 其中,timeout=120 是指这个TCP通道保持120S,max=5 指这个TCP通道最多接收5个HTTP请求,之后便自动关闭该连接。

优化点二:修改时间Last-Modified 和 If-Modified-Since

  • Last-Modified首部是服务端对客户端的HTTP响应所加的一个与缓存有关的HTTP首部,该首部标记了所请求资源在服务端的最后修改时间。类似:
1
Last-Modified : Fri , 12 May 2015 13:10:33 GMT
  • 当客户端发现HTTP响应头中有Last-Modified,会对资源进行缓存,在下次请求资源时,在HTTP请求头中添加If-Modified-Since首部,首部中将会添加上次成功请求资源时响应头部的Last-Modified属性值,即:
1
If-Modified-Since : Fri , 12 May 2015 13:10:33 GMT
  • 当服务端接收到的HTTP请求中,发现有If-Modified-Since头部时,会将该属性值与请求资源的最后修改时间进行比对,如果最后修改时间与该属性值一致时,服务端会返回一个304 Not Modified响应,该响应中不包括响应实体。浏览器收到304的响应后,会进行重定向,获取本地缓存资源。如果最后修改时间与该属性值不一致,则会从服务端重新获取资源,做出200响应。

优化点三:版本标记 ETag 和 If-None-Match

  • ETag其实与Last-Modified是差不多的方式,但是ETag并没有选择以时间作为标记,而是对所请求文件进行某些算法来生成一串唯一的字符串,作为对某一文件的标记。当收到客户端对某一资源的请求时,服务端在响应时,添加ETag首部,如下:
1
ETag:W/"a627ff1c9e65d2dede2efe0dd25efb8c"
  • 当客户端发现ETag头部时,同样会对资源进行缓存,并在下次请求时,在请求头部添加If-None-Match,如:

    1
    If-None-Match:W/"a627ff1c9e65d2dede2efe0dd25efb8c"
  • 当服务端收到请求中含有该头部时,会使用同样的ETag生成算法对文件ETag进行计算,并与If-None-Match属性值进行比对,如果一致,则返回一个304 Not Modified响应,基本与上一种方式是一致的。

优化点四:缓存时间 Expires 和 Cache-Control

  • 上述两种方式中,每次请求资源时,虽然在有缓存的情况下,选择缓存进行渲染绘制,但是在这之前还是发起了一次HTTP请求,虽然并没有真实的响应实体,但是依然会造成一些资源消耗。而Expires与上述两种方式使用了不同的思路。

  • 当服务端希望客户端浏览器对某一资源进行缓存时,为了免去客户端每次都要询问自己:我上次的缓存现在还能用吗?所以,服务端选择了放权。只去告诉浏览器,我这次给你的资源你可以用多长时间,在这个时间段内,你可以一直使用它,无需每次咨询我。而服务端就是通过Expires属性来告诉客户端浏览器可以多长时间内不需要询问服务端。如下:

    1
    Expires:Thu, 19 Nov 2015 15:00:00 GMT
  • 当客户端在响应首部中发现该属性值时,便会将该资源缓存起来,而缓存的过期时间即是Expires中的时间。在这个时间段内,浏览器完全自主。

  • 但是,Expires有一个不足的地方是,如果服务端时间与客户端本地时间不统一时,可能服务端让客户端可以对该资源缓存一个小时,而客户端本地时间比服务端时间快了两个小时,那就意味着,所有缓存都将不会生效。
  • 于是有了弥补该不足的一个属性,即:Cache-Control。如果服务端在响应首部添加该属性时,客户端将直接使用该属性值来生成本地时间的缓存过期时间,这样便解决了这个问题,如下:

    1
    Cache-Control:max-age=3600
  • 如果客户端在2015年10月01日13时00分00秒收到该响应时,便会加上3600秒也就是2015年10月01日14时00分00秒作为缓存过期时间。如果响应头部既有Expires和Cache-Control,浏览器会首选Cache-Control。

计算机网络五层结构功能汇总

发表于 2015-09-12 | 分类于 computer |

引言

  • 网上有很多博客介绍过计算机网络五层结构,但专门汇总五层结构功能的博文非常少;
  • 本文从纵向的角度,清晰概述计算机网络五层结构的功能和特点;
  • 本文总结了计算机网络五层结构的协议名和专用术语,并对一些结构性问题加以解释;
  • 本文没有介绍五层结构的实现细节;

五层结构的作用汇总

层级名称 主要作用和功能
物理层 发送和接收比特流;
负责将比特流调制到载波上面(调幅、调频、调相);
可以用信道复用等技术提升发送速率;
数据链路层 负责相邻设备间通信,这两个设备间没有交换节点,具体表现为点对点通信(PPP协议)和广播通信(CSMA/CD协议);
用MAC地址识别参与通信的两个节点(源节点、目的节点),MAC地址是本层及以下层专用;
上层网络层根据目的IP确定下一跳IP地址,数据链路层(交换机)根据下一跳IP地址就能确定下一跳的MAC地址,并将网络层数据报再次封装,加上目的MAC地址,交由物理层传送;
负责帧的差错检测,丢弃错误的帧(只检错不纠错),错误可能是帧丢失、帧重复、帧失序;
网络层 负责不相邻的两个节点间的通信,比如我的电脑和网页服务器之间的通信;
用IP地址识别源地址、目的地址,IP地址是本层及以上层专用;
能够确定转发路线,使得效率最高;
运输层 识别并将数据报正确交付相应的应用程序,识别数据报属于哪个应用程序的方法为看端口;
TCP可靠传输,UDP不可靠传输;
应用层 使用得到的报文段,为用户提供各种服务(浏览器、邮件、远程通信);
向上面向用户(域名给用户看),向下面向运输层和网络层(域名解析给他们看);

五层结构传输单元名称

层级名称 传输单元名称
应用层 报文
运输层 TCP:报文段;
UDP:用户数据报;
网络层 分组或IP数据报(简称数据报)
数据链路层 帧
物理层 比特

各层协议和专用术语汇总

层级名称 传输单元名称
应用层 网际组管理协议IGMP:使得连接在本局域网上的多播路由器知道本局域网上是否有主机(指主机上的进程)参加或退出某个多播组;
DNS:域名系统;
简单文件传送协议TFTP;
文件传送协议FTP;
NFS:网络文件系统;
动态主机配置协议DHCP:用于自动分配IP地址;
简单网络管理协议SNMP;
简单邮件传送协议SMTP:用于发送邮件;
邮件读取协议POP和IMAP:用于读取邮件;
远程终端协议TELNET;
超文本传送协议HTTP;
socket:IP和端口统称套接字,java语言中称为java socket编程;
安全外壳SSH(Secure Shell):不是一种协议,是运用运输层SSL协议构建的一个类似telnet的应用,SSH = TELNET + SSL,和web开发框架中的SSH框架没有任何关系(SSH=Struts+Spring+Hibernate);
运输层 传输控制协议TCP:面向连接;
用户数据报协议UDP:无连接;
安全套接层SSL:是一种在运输层对网络数据进行加密的协议;
网络层 地址解析协议ARP:已知一台机器的IP地址,需要找出其相对应的硬件MAC地址;
网际控制报文协议ICMP:分为ICMP差错报文和ICMP询问报文,用于主机和路由器报告差错和异常情况,ping命令是在应用层直接使用网络层协议的例子;
内部网关协议IGP:一个自治系统内部使用的路由协议,包括RIP和OSPF协议;
外部网关协议EGP:自治系统间路由协议,包括BGP协议;
VPN:虚拟专用网技术,用到了网络地址转换NAT技术;
数据链路层 PPP:点对点信道所使用的协议;
CSMA/CD:广播信道使用的协议;
MAC地址;
物理层 传输介质:双绞线、同轴电缆;
信道复用技术:频分复用、时分复用、码分复用;
宽带接入技术:ADSL(非对称用户数字线);

各层典型设备

层级名称 设备名称
应用层 网关:连接两个或多个使用不同协议的网络,充当“协议转换器”,是两个局域网之间的通道;
运输层 暂无
网络层 路由器:连接不同网络的设备,自动选择设定路由,寻找最佳路径,在某些情况下,路由器也被称做网关;
数据链路层 网卡(网络适配器):当接收帧错误时就丢弃,正确时就交付网络层,内含MAC地址;
交换机:相当于多个网桥,将好多计算机连接成一个局域网,负责局域网内部的数据交换,相当于扩展的网桥,允许在还没把数据帧接受完全的情况下转发帧;
网桥/桥接器:把一个局域网隔离成许多网段,维护转发表,对收到的帧进行转发、过滤或丢弃(完全接收到一帧后才转发);
物理层 集线器/转发器:简单转发比特,不维护任何表;

几个结构性问题的解释

  • 为何不能只有MAC地址,IP地址没有行不行?
  • 这和”能不能只有数据链路层,没有网络层”的问题一样;
  • 他们处理的是不同层面的事务,前者专注于相邻节点传输、后者则专注于不相邻节点传输;
  • 这是”网络”这个事务由小到大、量变产生质变的必然选择;
  • 为何不将TCP可靠传输的任务交由网络层负责?
  • 其实这是”可靠”怎么理解的问题,如果网络层可靠了,但是在传输层不可靠,数据也无法正确及时的送达应用程序手中;
  • 如果网络层和运输层都保证可靠,协议未免太过复杂,效率降低;
  • 所以只在运输层保证可靠,是在效率、可靠性之间权衡做出的最佳选择;

CSS技巧汇总

发表于 2015-09-11 | 分类于 Web |

引言

  • 使用CSS渲染页面是当今网页常用的技术。
  • 本文汇总了CSS使用时的注意事项和技巧。

浏览器支持

  • webkit/safari/chrome:-webkit-;
  • firefox:-moz-;
  • IE:-ms-;
  • opera:-o-;
  • 一般来说,对于下面介绍的一些较新的css3特性,为了兼容旧版浏览器,需要添加-webkit-前缀;

技巧汇总

描述 方法
改变控件的四个角的弧度 border-radius属性,另有border-top-left-radius、border-top-right-radius、border-bottom-right-radius、border-bottom-left-radius等扩展属性
改变input标签中placeholder的样式 使用伪类 input::-webkit-input-placeholder, textarea::-webkit-input-placeholder
使元素自由浮动 使用float属性
使元素从他自己的位置偏移 使用position:relative属性,配合left、right等属性使用
使元素固定位置 使用position:fixed属性,并配合z-index属性设置元素的层叠顺序
设置元素绝对位置 使用position:absolute属性
设置文字的位置 使用text-align属性
设置元素是否显示 使用display属性
设置元素溢出后样式 使用overflow属性
设置鼠标悬停样式 使用cursor属性
设置文本阴影 使用text-shadow属性,此属性为CSS3增强特性
设置元素透明度 使用opacity属性
1…789…11
Eric Chang

Eric Chang

www.yinshuisiyuan.net

101 日志
15 分类
51 标签
github weibo linkedin mail
  • 阮一峰
  • Azure
  • Python
  • Nodejs
© 2015 - 2020 Eric Chang
由 Hexo 强力驱动
主题 - NexT.Pisces


知识共享许可协议本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。