FRONT-END
GET
POST
COOKIE
LOCALE STORAGE
APPLICATION CACHE
BACK-END
DATABASE
SESSION
undefined 用于表示一个没有赋值的变量 用于区分改变量是否被赋过值,一个变量在声明的时候默认就是undefined 正常情况下我们不需要手动把一个变量赋值为undefined。
关于Ajax应用前进后退操作历史记录目前在很多主流框架例如YUI/Dojo/Prototype/JQuery都已经有比较完美的解决方案。
但想要实现历史记录的功能都是无法脱离框架的束缚,HashManager是一个脱离所有框架的一个工具类,它不依赖任何框架实现了Ajax应用前进后退功能,并支持AJAX应用的书签访问。
越来越习惯在代码里以注解的形式来阐述一些事情了,感觉这样会描述的更清晰一些。
// 代码没多少废话很多,看下去要有心理准备
// 原型继承的核心方法
function clone(obj) {
function F () {};
F.prototype = obj;
return new F();
}
// Person 类
var Person = {
name: 'default name',
getName: function() {
return this.name;
}
};
(function (){
/*
* 我们先不看继承,先看看
* 如何实例化一个Person
* 使用clone方法来实例化
*/
var per = clone(Person);
// 给这个人取一个名字
per.name = 'default name';
// 看看这个人的名字
console.log(per.getName()); // output: default name
})();
/* Author extends Person
* 继承很简单,直接使用clone方法传入
* 父类对象即可完成继承
* 现在可以对继承后的Author添加子类特
* 有的方法或者覆盖父类同名方法
*
* 现在一定有点困惑,为什么继承和实例化都是clone方法
* 在原型继承中继承和实例化其实是一回事,就像一块砖头
* 拿起来砸人就是凶器,用来盖房子就叫砖头
* 通常区分它们通过接收clone返回值的变量名称是否首字母
* 大写为界,大写为继承,小写为实例化ok~
*/
var Author = clone(Person);
Author.books = [];
Author.getBooks = function () {
return this.books;
};
(function () {
/*
* 好吧,我们再来尝试实例化一个Author对象
*/
var auth1 = clone(Author);
// 先来给这个作者一些信息
auth1.name = 'Xiaoming';
auth1.books = ['午夜的花儿为谁开', '夜半敲门声'];
// 上面一句话做一个记号,这里会有问题遗留到后面解决
// 试验1:检查继承情况是否良好
console.log(auth1.getName()); // output: Xiaoming
console.log(auth1.getBooks()); // output: ['午夜的花儿为谁开', '夜半敲门声']
// 验证ok
/* 试验2:
* 我们需要尝试一下现在改变
* 父类Person是否会影响到子类Author的实例
*/
Person.getSay = function () {
console.log('bingo!');
};
auth1.getSay(); // output: bingo!
// 事实说明是会的。这未必是坏事,这是一种灵活,但不要滥用
})();
/*
* 上面这个故事中有没有童鞋发现
* auth1.name = 'Xiaoming' 这行
* 的name到底是谁的name
* 由于原型继承机制的特殊性
* 父类也是一个对象
* 通常思维name属性是赋值给继承来的name属性
* 但这里不同,因为clone而来的auth1起初其实根本没有name属性
* auth1.name = 'Xiaoming'是在auth上增加一个name属性并赋值的
* 在prototype中的name仍然没有改变依旧是'default name'
* 导致这种“非对称读写”现象的原因是当读取对象的某个属性时,该对象本身如果不存在该属性
* Javascript就会自动搜索对象的prototype对象中是否有,如果有则读取,如果
* 没有则继续向上protoype寻找,直到Object为止
* JavaScript设计模式中称为“非对称读写”
* 为了更透彻的阐释,还是看代码说事
*/
(function() {
var authorClone = clone(Author);
console.log(authorClone.getName()); // output: default name
/*
* 很好理解,调用getName方法(不解释)
* JavaScript中去找authorClone的name属性
* 没找到,继续查找authorClone.prototype中是否存在
* 找到了,返回该值也就是Person.name的值default name
*/
authorClone.name = 'new name';
/*
* 这句话中的name需要澄清一下是新建的
* 而不是改变了Person.name的值
* 所以authorClone.prototype中的name还
* 好好的是 default name
* 只是authorClone对象中多了一个
* name 属性值为 new name而已
* 验证一下
*/
console.log(authorClone.getName()) // output: new name
/*
* 接下来尝试通过push方法
* 对authorClone的books增加内容
*/
authorClone.books.push('new book');
/*
* 看似没什么问题,其实问题很严重
* 理解上面那个故事的童鞋已经恍然大悟
* 事实就是new book被push到了Author.books中
* 所有Author的子类和实例都会读取到这个默认值
* 这里不再试验,现在想想上面那个故事中做记号的地方
* auth1.books = [xxx, xxx]; 的写法为什么没问题
* 原因就在这么写就相当于在auth1中增加了books属性的数组
* 而不会影响父类的值,正确的演示
*/
authorClone.books = [];
authorClone.books.push('new book');
// 这么做才是正确的 哈哈
})();
/*
* 还有一种情况需要注意的是当父类
* 属性中含有对象时应该如何处理
* 处理方法类似上面的数组,略有不同
* 父类属性中的属性
* 有点绕口,直接看代码
*/
(function() {
// 一个类(很没营养的例子,能说明白就行哈)
var CompoundObject = {
string1: 'default value',
childObject: {
bool: true,
num: 10
}
};
// 实例化
compoundObject = clone(CompoundObject);
// 想要改变该实例中childObject中的num属性
compoundObject.childObject.num = 5;
// oh no~ 不要这样!这样是能改,但会把CompoundObject中的也改了
// 这样改的坏处不再赘述,上文已经提到
// 同理,我们需要为这个对象创建一个对象
compoundObject.childObject = {
bool: true,
num: 5
};
// 虽然这样可以完美达成目的,遗憾的是我们需要关注该对象的其他默认值
// 这样悲剧就在于我们想改一个值,但要把其他值原样抄过来
console.log(compoundObject);
})();
// 这是JavaScript设计模式中推荐的写法
(function() {
// 更完美一些我们可以这样解决
// 看起来是不太优雅,但这是最接近完美的途径
var CompoundObject = {};
CompoundObject.string1 = 'default value';
CompoundObject.createChildObject = function () {
return {bool: true, num: 10}
};
CompoundObject.childObject = CompoundObject.createChildObject();
// 实例化
var compoundObjectClone = clone(CompoundObject);
compoundObjectClone.childObject = CompoundObject.createChildObject();
compoundObjectClone.childObject.num = 5;
console.log(compoundObjectClone);
// 完美达成!
})();
// 原型继承的内容在JavaScript设计模式中介绍的基本就这些,我是权当读书笔记了哈,希望能顺道帮助到别人
相关资料:http://javascript.crockford.com/prototypal.html (老道的原型继承启蒙)
其余不解释,直接看操作步骤:
第一步:下载tar包到指定目录
wget http://www.erikfantasia.com/download/godaddy-svn-1.5.6.tar.gz
第二步:解压tar包
tar zxvf godaddy-svn-1.5.6.tar.gz
第三部:编辑家目录下的.bashrc文件,追加以下两行,注意svn目录位置设置成刚刚解压后的svn目录
export PATH=$PATH:$HOME/svn/bin
export LD_LIBRARY_PATH=$HOME/svn/lib
大功告成!
参考:http://erikfantasia.wordpress.com/2009/03/18/subversion-on-godaddy-shared-hosting/
为啥jslint默认会将下划线的变量列入警告呢?
原因是道格拉斯不喜欢这么做,很多人都觉得这样有点过于严厉,事实上下划线开头的变量在很多JS类库中都很常见,也没有出过什么问题。
在ECMA中提到关于下划线和美元符号的用法:
from ECMA 262, section 7.6:
This standard specifies one departure from the grammar given in the Unicode standard: The dollar sign ($) and the underscore (_) are permitted anywhere in an identifier. The dollar sign is intended for use only in mechanically generated code.
美元符号($)与下划线(_)可以出现在标识符的任何位置。美元符号用于动态生成的代码。
公司的台式机已经装成UBUNTU做了私用的开发服务器,一直都不关机,想着能不能装个虚拟机XP然后在上面可以挂点东东,榨干这台工作机的剩余价值。哈哈
通过NAT创建了一台XP虚拟机,要将这台服务器的3389端口映射到虚拟机上,这样就可以远程桌面登录虚拟机啦。
VirtualBox下默认是没有设置端口映射的选项的,不像VM那么先进,但在UBUNTU下VB确实比VM速度上跟好一些。
VirtualBox 3以后都只要执行如下命令即可做端口映射:
VBoxManage modifyvm "VM name" --natpf1 "guestssh,tcp,,3389,,3389"
两个逗号没问题,因为这是空的参数,第一个3389为UBUNTU的端口,后者为虚拟机的端口。
把代码当作文章看吧,最后由解说继承实现的原理:
/**
* 水果类
* @param color 水果的颜色
*/
var Fruit = function (color) {
this._color = color;
};
/**
* 说出水果的颜色
*/
Fruit.prototype.tellColor = function () {
console.log('水果颜色:' + this._color);
};
/**
* 香蕉类
*
* @extends Fruit
* @param color 香蕉的颜色
*/
var Banana = function (color) {
Fruit.call(this, color);
this.superclass =Fruit.prototype;
};
Banana.prototype = new Fruit();
Banana.prototype.constructor = Banana;
/**
* 玩弄水果
*/
Banana.prototype.play = function (sth) {
console.log('一根' + this._color + '色的香蕉正在玩弄' + sth + ' - -!');
};
// 尝试一下 搞根香蕉玩玩
var banana = new Banana('BLUE');
banana.tellColor(); // output: 水果颜色:BLUE
banana.play('它自己的头发'); // output: 一根BLUE色的香蕉正在玩弄它自己的头发 - -!
// 香蕉的继承机制运作正常
// 再把香蕉细化看看还是否能正常运作
/**
* 杭州香蕉类
*
* @extends Banana
* @param color
*/
var HangzhouBanana = function (color) {
Banana.call(this, color);
this.superclass = Banana.prototype;
};
HangzhouBanana.prototype = new Banana();
HangzhouBanana.prototype.constructor = HangzhouBanana;
/**
* 吃东西
*/
HangzhouBanana.prototype.eat = function (sth) {
console.log('这只杭州' + this._color + '色的香蕉正在吃' + sth + ' = =!');
};
/**
* 玩
* @override
*/
HangzhouBanana.prototype.play = function (sth) {
this.superclass.play.call(this, sth);
console.log('一根' + this._color + '色的杭州香蕉正在玩弄' + sth + ' 三 三!!!');
};
// 看看杭州的香蕉是不是真的能吃东西
var hzBanana = new HangzhouBanana('GREEN');
hzBanana.tellColor(); // output: 水果颜色:GREEN
hzBanana.eat('桌子'); // output: 这只杭州GREEN色的香蕉正在吃桌子 = =!
// 杭州香蕉还有一个重写父类的方法play还没有试
hzBanana.play('另外一根香蕉');
// output: 一根GREEN色的香蕉正在玩弄另外一根香蕉 - -!
// 一根GREEN色的杭州香蕉正在玩弄另外一根香蕉 三 三!!!
// 成功!
// 现在试试instanceof是否能正常运作
// 关于typeof大家都懂的,比较悲剧都是object暂时没有有效的办法。
console.log(banana instanceof Banana, banana instanceof Fruit); // output: true true
console.log(hzBanana instanceof HangzhouBanana, hzBanana instanceof Banana, hzBanana instanceof Fruit); // output: true true true
为了更好的理解继承实现的原理,当用new关键字来实例化香蕉的同时,发生了一系列的操作:
-首先会在原型链的最前端创建一个空的Object实例;
-执行构造,将this的引用都指向这个空的对象;
-通过call将空对象的引用成为父级类的上下文,
从而可以使这个空对象(this所指的对象)可以粘附父类的属性,
比如Fruit中的color属性就在此时在空对象中了(这步需要手动来完成)。
Fruit.call(this, <color>);
-接下来便是将子类Banana的prototype属性赋值为父类的实例,
此举意义在于将父类实力对象中的方法属性赋值到子类Banana的prototype中。
因为整个prototype都被改成了父类,导致prototype的constructor属性为Fruit,而不是子类本身的Banana。
需要将prototype.constructor改回来。
解释一下为什么会出现
this.superclass = Fruit.prototype;
与
this.superclass = Banana.prototype;
两者道理相同,只解释this.superclass = Banana.prototype;
目的在于保留一份父类方法,以备在子类override父类方法后还可以调用父类方法调用。
这里出现在HangzhouBanana的override父类的play同名方法中:
this.superclass.play.call(this, sth);
参考资料:JavaScript设计模式 继承章节、YUI3中对继承的superclass的用法
模拟实现是从“JavaScript设计模式”一书中摘抄下来的,个人认为是个相对比较完美的模拟实现,详细见注释:
/**
* 定义接口类
* @param name 接口名称
* @param methods 字符串方法名为元素的数组对象
*/
function Interface (name, methods) {
if (arguments.length != 2) {
throw new Error("Interface constructor called with " +
arguments.length + " arguments, but expected exactly 2.");
}
this.name = name;
this.methods = [];
for (var i = 0, len = methods.length; i < len; i++) {
if (typeof methods[i] !== 'string') {
throw new Error("Interface constructor expects method names to be \
passed in a string.");
}
this.methods.push(methods[i]);
}
};
/**
* 接口类静态方法
* @param object 任何对象
* @param ... 任意多个接口对象
*/
Interface.ensureImplements = function (object) {
if (arguments.length < 2) {
throw new Error("Function Interface.ensureImplements called with " +
arguements.length + "arguemnts, but expected at least 2.");
}
for (var i = 1, len = arguments.length; i < len; i++) {
var interface = arguments[i];
if (interface.constructor !== Interface) {
throw new Error ("Function Interface.ensureImplements expects arguments \
two and above to be instance of Interface.");
}
for (var j = 0, methodLen = interface.methods.length; j < methodLen; j++) {
var method = interface.methods[j];
if (!object[method] || typeof object[method] !== 'function') {
throw new Error ('dd');
}
}
}
};
// 定义一个Item接口
var interface = {};
interface.Item = new Interface('Item', ['paint', 'destroy']);
var implements = {};
/**
* 定义一个Item接口的实现类
* 实现类中不需要显示说明此类实现了Item接口
* 检查将在paintItem方法中进行
*
* @param name
*/
implements.Item = function (name) {
this.init.apply(this, arguments);
};
implements.Item.prototype.init = function (name) {
this._name = name;
};
implements.Item.prototype.__defineSetter__('name', function (name) {
this._name = name;
});
implements.Item.prototype.paint = function () {
console.log(this._name + ' paint');
};
// 实例化两个item对象
var item1 = new implements.Item('item1');
var item2 = new implements.Item('item2');
/**
* 定义绘制item方法
* @param item 实现了Item接口的item对象
*/
function paintItem(item) {
// 此处检查是否实现了Item接口
Interface.ensureImplements(item, interface.Item);
item.paint();
}
// 调用绘制方法
paintItem(item1); // 输出 "item1 paint"