WebKong的网络日志

分享工作中的所用、所想、所感。

Javascript的变量是松散类型的,它可以存储Javascript支持的任何数据类型,其变量的类型可以在运行时被动态改变。

正因为Javascript中变量类型具有动态性,在程序实际执行的过程中就需要用到类型转换的概念。类型转换可以分为隐式转换和显式转换,所谓隐 式转换即程序在运行时进行的自动转换,显式转换则是人为的对类型进行强制转换。

Javascript提供了以下转型函数:
转换为数值类型:Number(mix)、parseInt(string,radix)、parseFloat(string)
转换为字符串类型:toString(radix)、String(mix)
转换为布尔类型:Boolean(mix)

  • Number(mix)函数,可以将任意类型的参数mix转换为数值类型。其规则为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Boolean->Number : true,false --> 1,0
    Number->Number : 返回其本身
    undefined->Number : undefined --> NaN
    String->Number :
    + 如果字符串中只包含数字,则将其转换为十进制(忽略前导0)
    + 如果字符串中包含有效的浮点格式,将其转换为浮点数值(忽略前导0)
    + 如果是空字符串,将其转换为0
    + 如果字符串中包含非以上格式,则将其转换为NaN
    + 如果是对象,则调用对象的valueOf()方法,然后依据前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,再次依照前面的规则转换返回的字符串值。

    下表列出了对象的valueOf()的返回值:

对象 返回值
Array 数组的元素被转换为字符串,这些字符串由逗号分隔,连接在一起。其操作与 Array.toString 和 Array.join 方法相同。
Boolean Boolean 值。
Date 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。
Function 函数本身。
Number 数字值。
Object 对象本身。这是默认情况。
String 字符串值。
  • parseInt(string, radix)函数,将字符串转换为整数类型的数值。它也有一定的规则:
    1
    2
    3
    4
    5
    忽略字符串前面的空格,直至找到第一个非空字符
    如果第一个字符不是数字符号或者负号,返回NaN
    如果第一个字符是数字,则继续解析直至字符串解析完毕或者遇到一个非数字符号为止
    如果上步解析的结果以0开头,则将其当作八进制来解析;如果以0x开头,则将其当作十六进制来解析
    如果指定radix参数,则以radix为基数进行解析
  • parseFloat(string)函数,将字符串转换为浮点数类型的数值。
    1
    2
    它的规则与parseInt基本相同,但也有点区别:
    字符串中第一个小数点符号是有效的,另外parseFloat会忽略所有前导0,如果字符串包含一个可解析为整数的数,则返回整数值而不是浮点数值。
  • toString(radix)方法。除undefined和null之外的所有类型的值都具有toString()方法,其作用是返回对象的字符串表示。
对象 操作
Array 将 Array 的元素转换为字符串。结果字符串由逗号分隔,且连接起来。
Boolean 如果 Boolean 值是 true,则返回 “true”。否则,返回 “false”。
Date 返回日期的文字表示法。
Error 返回一个包含相关错误信息的字符串。
Function 返回如下格式的字符串,其中 functionname 是被调用 toString 方法函数的名称:
function functionname( ) { [native code] }
Number 返回数字的文字表示。
String 返回 String 对象的值。

默认 返回 “[object objectname]”,其中 objectname 是对象类型的名称。

  • String(mix)函数,将任何类型的值转换为字符串,其规则为:
    1
    2
    3
    如果有toString()方法,则调用该方法(不传递radix参数)并返回结果
    如果是null,返回”null”
    如果是undefined,返回”undefined”
  • Boolean(mix)函数,将任何类型的值转换为布尔值。
1
以下值会被转换为false:fals* ”"、0、NaN、null、undefined,其余任何值都会被转换为true。

编码规则

Base64编码的思想是是采用64个基本的ASCII码字符对数据进行重新编码。它将需要编码的数据拆分成字节数组。以3个字节为一组。按顺序排列24 位数据,再把这24位数据分成4组,即每组6位。再在每组的的最高位前补两个0凑足一个字节。这样就把一个3字节为一组的数据重新编码成了4个字节。当所要编码的数据的字节数不是3的整倍数,也就是说在分组时最后一组不够3个字节。这时在最后一组填充1到2个0字节。并在最后编码完成后在结尾添加1到2个 “=”。

代码实现

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  //下面是64个基本的编码
var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var base64DecodeChars = new Array(
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 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, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
//编码的方法
function base64encode(str) {
var out, i, len;
var c1, c2, c3;
len = str.length;
i = 0;
out = "";
while(i < len) {
c1 = str.charCodeAt(i++) & 0xff;
if(i == len)
{
out += base64EncodeChars.charAt(c1 >> 2);
out += base64EncodeChars.charAt((c1 & 0x3) << 4);
out += "==";
break;
}
c2 = str.charCodeAt(i++);
if(i == len)
{
out += base64EncodeChars.charAt(c1 >> 2);
out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
out += base64EncodeChars.charAt((c2 & 0xF) << 2);
out += "=";
break;
}
c3 = str.charCodeAt(i++);
out += base64EncodeChars.charAt(c1 >> 2);
out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6));
out += base64EncodeChars.charAt(c3 & 0x3F);
}
return out;
}
//解码的方法
function base64decode(str) {
var c1, c2, c3, c4;
var i, len, out;
len = str.length;
i = 0;
out = "";
while(i < len) {

do {
c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
} while(i < len && c1 == -1);
if(c1 == -1)
break;

do {
c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
} while(i < len && c2 == -1);
if(c2 == -1)
break;
out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));

do {
c3 = str.charCodeAt(i++) & 0xff;
if(c3 == 61)
return out;
c3 = base64DecodeChars[c3];
} while(i < len && c3 == -1);
if(c3 == -1)
break;
out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));

do {
c4 = str.charCodeAt(i++) & 0xff;
if(c4 == 61)
return out;
c4 = base64DecodeChars[c4];
} while(i < len && c4 == -1);
if(c4 == -1)
break;
out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
}
return out;
}
function utf16to8(str) {
var out, i, len, c;
out = "";
len = str.length;
for(i = 0; i < len; i++) {
c = str.charCodeAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
out += str.charAt(i);
} else if (c > 0x07FF) {
out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
} else {
out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
}
}
return out;
}
function utf8to16(str) {
var out, i, len, c;
var char2, char3;
out = "";
len = str.length;
i = 0;
while(i < len) {
c = str.charCodeAt(i++);
switch(c >> 4)
{
case case case case case case case case
// 0xxxxxxx
out += str.charAt(i-1);
break;
case case
// 110x xxxx 10xx xxxx
char2 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = str.charCodeAt(i++);
char3 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
}
}
return out;
}

调用

1
2
3
4
5
//编码
value = base64encode(utf16to8(src))

//解码
value = utf8to16(base64decode(src))

列举常用的JavaScript字符串操作的方法

concat

将两个或多个字符的文本组合起来,返回一个新的字符串。

1
2
3
4
5
var a = "hello";
var b = ",world";
var c = a.concat(b);
alert(c);
//c = "hello,world"

indexOf

返回字符串中一个子串第一处出现的索引(从左到右搜索)。如果没有匹配项,返回 -1 。

1
2
3
4
5
var index1 = a.indexOf("l");
//index1 = 2
var index2 = a.indexOf("l",3);
//index2 = 3

charAt

返回指定位置的字符。

1
2
var get_char = a.charAt(0);
//get_char = "h"

lastIndexOf

返回字符串中一个子串最后一处出现的索引(从右到左搜索),如果没有匹配项,返回 -1 。

1
2
3
4
var index1 = lastIndexOf('l');
//index1 = 3
var index2 = lastIndexOf('l',2)
//index2 = 2

match

检查一个字符串匹配一个正则表达式内容,如果么有匹配返回 null。

1
2
3
4
5
var re = new RegExp(/^\w+$/);
var is_alpha1 = a.match(re);
//is_alpha1 = "hello"
var is_alpha2 = b.match(re);
//is_alpha2 = null

substring

返回字符串的一个子串,传入参数是起始位置和结束位置。

1
2
3
4
var sub_string1 = a.substring(1);
//sub_string1 = "ello"
var sub_string2 = a.substring(1,4);
//sub_string2 = "ell"

substr

返回字符串的一个子串,传入参数是起始位置和长度

1
2
3
4
var sub_string1 = a.substr(1);
//sub_string1 = "ello"
var sub_string2 = a.substr(1,4);
//sub_string2 = "ello"

replace

用来查找匹配一个正则表达式的字符串,然后使用新字符串代替匹配的字符串。

1
2
3
4
var result1 = a.replace(re,"Hello");
//result1 = "Hello"
var result2 = b.replace(re,"Hello");
//result2 = ",world"

执行一个正则表达式匹配查找。如果查找成功,返回字符串中匹配的索引值。否则返回 -1 。

1
2
3
4
var index1 = a.search(re);
//index1 = 0
var index2 = b.search(re);
//index2 = -1

slice

提取字符串的一部分,并返回一个新字符串(与 substring 相同)。

1
2
3
4
var sub_string1 = a.slice(1);
//sub_string1 = "ello"
var sub_string2 = a.slice(1,4);
//sub_string2 = "ell"

split

通过将字符串划分成子串,将一个字符串做成一个字符串数组。

1
2
var arr1 = a.split("");
//arr1 = [h,e,l,l,o]

length

返回字符串的长度,所谓字符串的长度是指其包含的字符的个数。

1
2
var len = a.length();
//len = 5

toLowerCase
——

将整个字符串转成小写字母。

1
2
var lower_string = a.toLowerCase();
//lower_string = "hello"

toUpperCase
——

将整个字符串转成大写字母。

1
2
var upper_string = a.toUpperCase();
//upper_string = "HELLO"

package.json 的版本的匹配规则

用version来表示一个具体的版本

一般范围语法

  • version //必须严格匹配到 version版本
  • >version //必须大于 version的版本
  • >=version //大于等于 version的版本
  • <version //小于version的版本
  • <=version //小于等于
  • 带alpha的版本 // 例如 >1.2.3-alpha.3,允许匹配版本1.2.3-alpha.7,但它不匹配 3.4.5-alpha.9,即使3.4.5-alpha.9在技术上是“大于” 1.2.3-alpha.3的。SemVer排序规则

高级区域语法

  • version1 - version2 //连字符范围, 1.2.3 - 2.3.4 等价于 >=1.2.3 <=2.3.4

  • ~version //一般是补丁级别的修改 例如:~1.2.3 等价于 >=1.2.3 <1.(2+1).0 等价于 >=1.2.3 <1.3.0

  • ^version //“兼容的版本” 参考

  • 1.2.x //匹配 >=1.2.0 <1.3.0

  • * //匹配任意版本
    “” // ""(空字符串)等价于 * 等价于 >=0.0.0

  • range1 || range2 //range1 或者 range2 的任一版本.

更多的使用方法请访问参考链接

参考

The semantic versioner for npm
Semantic Versioning 2.0.0

通过 JavaScript 来获取 url 上携带的参数

假设 url=”http://xxx.com.cn?name=webkong&age=23&sex=男"

第一种,直接 js 函数,截取 url,拼装成对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function GET(){
var url = window.document.location.href.toString();
var u = url.split("?");
if(typeof(u[1]) == "string"){
u = u[1].split("&");
var get = {};
for(var i in u){
var j = u[i].split("=");
get[j[0]] = j[1];
}
return get;
}else {
return {};
}
};

var getParam = GET(); //通过变量保存参数对象
//getParam:{
name:"webkong",
age:"23",
sex:"男"
}

和第一种逻辑过程一样,只是结果是对象本身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var $_GET = (function(){
var url = window.document.location.href.toString();
var u = url.split("?");
if(typeof(u[1]) == "string"){
u = u[1].split("&");
var get = {};
for(var i in u){
var j = u[i].split("=");
get[j[0]] = j[1];
}
return get;
} else {
return {};
}
})();

// 使用时, 可以直接 $_GET['name'], 就直接获得name值

第二种,通过正则来匹配

1
2
3
4
5
6
7
8
//通过传递参数来获取url上的某一个参数值
function GET(name){
var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
var r = window.location.search.substr(1).match(reg); //匹配目标参数
if (r!=null) return unescape(r[2]);
return null; //返回参数值
}
//调用 GET('name');

或者扩展成 jQuery

1
2
3
4
5
6
7
8
9
(function($){
$.getUrlParam= function(name){
var reg = new RegExp("(^|&)"+name +"=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r!=null) return unescape(r[2]);
return null;
}
})(jQuery);

大概有这 4 种封装方式,可以任意搭配封装方式和逻辑代码

记录一下 git 的删除回滚类操作

本地库操作

场景: 已经 commit 但是没有 push

1
2
3
4
5
git log
//查看上次提交的commit-id (hash值)

git reset --soft commit-id
//撤销commit操作,保留commit时候改的的代码。

强制回滚

1
2
git reset --hard commit-id
//回退到某个版本,并且删除修改的文件

场景: 没有 commit,而且是新建的文件或者文件夹,抛弃所有这些修改

1
2
git clean
//clean 用来 抛弃所有untracked 状态的文件

tips: git 文件的三种状态

  1. untracked 未被 git 跟踪的文件 (新增文件,未加入到暂存区)
  2. new files(Changes to be committed) 文件被 commit 到了暂存区
  3. modified (Changes not staged for commit) 已经提交过的文件被修改,还未提交到暂存区

场景: 抛弃所有 modified 状态的 文件

1
2
3
4
5
6
7
git checkout .  //一定要带点(.)

git checkout -- ''*.js'
//如果是删除某类文件可以用

git checkout -- 1.js
//删除某个文件

如果要删除工作区的修改可以连用

git checkout . && git clean -xdf

场景: 提交信息写错,或者遗漏提交

1
2
3
4
5
//如果有遗漏,先add
git add

//然后再次提交
git commit -m "some message" --amend

作者:阮一峰
原文地址

在面向对象编程中,类(class)是对象(object)的模板,定义了同一组对象(又称”实例”)共有的属性和方法。
Javascript语言不支持”类”,但是可以用一些变通的方法,模拟出”类”。
一、构造函数法


这是经典方法,也是教科书必教的方法。它用构造函数模拟”类”,在其内部用this关键字指代实例对象。

1
2
3
function Cat(){
this.name = '大毛'
}

生成实例的时候用new关键字

1
2
var cat1 = new Cat();
alert(cat1.name); //大毛

类的属性和方法,还可以定义在构造函数的prototype对象之上。

1
2
3
Cat.prototype.makeSound = function(){
alert('喵喵');
}

关于这种方法的详细介绍,请看我写的系列文章《Javascript 面向对象编程》,这里就不多说了。它的主要缺点是,比较复杂,用到了this和prototype,编写和阅读都很费力。

二、Object.create()法

为了解决”构造函数法”的缺点,更方便地生成对象,Javascript的国际标准ECMAScript第五版(目前通行的是第三版),提出了一个新的方法Object.create()。
用这个方法,”类”就是一个对象,不是函数。

1
2
3
4
5
6
var Cat = {
name:"大毛",
makeSound:function(){
alert("喵喵喵");
}
}

然后,直接用Object.create()生成实例,不需要用到new。

1
2
3
var cat1 = Object.creat(Cat);
alert(cat1.name); //大毛
cat1.makeSound(); //喵喵喵

目前,各大浏览器的最新版本(包括IE9)都部署了这个方法。如果遇到老式浏览器,可以用下面的代码自行部署。

1
2
3
4
5
6
7
if (!Object.create) {
    Object.create = function (o) {
       function F() {}
      F.prototype = o;
      return new F();
    };
  }

这种方法比”构造函数法”简单,但是不能实现私有属性和私有方法,实例对象之间也不能共享数据,对”类”的模拟不够全面。

三、极简主义法

荷兰程序员Gabor de Mooij提出了一种比Object.create()更好的新方法,他称这种方法为”极简主义法”(minimalist approach)。这也是我推荐的方法。

1.封装
这种方法不使用this和prototype,代码部署起来非常简单,这大概也是它被叫做”极简主义法”的原因。
首先,它也是用一个对象模拟”类”。在这个类里面,定义一个构造函数createNew(),用来生成实例。

1
2
3
4
5
var Cat = {
createNew: function(){
// some code here
}
}

然后,在createNew()里面,定义一个实例对象,把这个实例对象作为返回值。

1
2
3
4
5
6
7
8
var Cat = {
createNew: function(){
var cat = {};
cat.name = "大毛";
cat.makeSound = function(){alert('miaomiaomiao')};
return cat;
}
}

使用的时候,调用createNew()方法,就可以得到实例对象。

1
2
var cat1 = Cat.createNew();
  cat1.makeSound(); // 喵喵喵

这种方法的好处是,容易理解,结构清晰优雅,符合传统的”面向对象编程”的构造,因此可以方便地部署下面的特性。

2.继承
让一个类继承另一个类,实现起来很方便。只要在前者的createNew()方法中,调用后者的createNew()方法即可。
先定义一个Animal类。

1
2
3
4
5
6
7
var Animal = {
createNew: function(){
var animal = {};
animal.sleep = function(){alert("睡懒觉")};
return animal;
}
};

然后,在Cat的createNew()方法中,调用Animal的createNew()方法。

1
2
3
4
5
6
7
8
var Cat = {
createNew:function(){
var cat = Animal.createNew();
cat.name = "damao";
cat.makeSound = function (){alert('miaomiaomiao')}
return cat;
};
};

这样得到的Cat实例,就会同时继承Cat类和Animal类。

1
2
var cat1 = Cat.createNew();
 cat1.sleep(); // 睡懒觉

3.私有属性和私有方法
在createNew()方法中,只要不是定义在cat对象上的方法和属性,都是私有的。

1
2
3
4
5
6
7
8
var Cat = {
    createNew: function(){
      var cat = {};
      var sound = "喵喵喵";
      cat.makeSound = function(){ alert(sound); };
      return cat;
    }
  };

上例的内部变量sound,外部无法读取,只有通过cat的公有方法makeSound()来读取。

1
2
var cat1 = Cat.createNew();
 alert(cat1.sound); // undefined

4.数据共享
有时候,我们需要所有实例对象,能够读写同一项内部数据。这个时候,只要把这个内部数据,封装在类对象的里面、createNew()方法的外面即可。

1
2
3
4
5
6
7
8
9
var Cat = {
    sound : "喵喵喵",
    createNew: function(){
      var cat = {};
      cat.makeSound = function(){ alert(Cat.sound); };
      cat.changeSound = function(x){ Cat.sound = x; };
      return cat;
    }
  };

然后,生成两个实例对象:

1
2
3
var cat1 = Cat.createNew();
var cat2 = Cat.createNew();
cat1.makeSound(); // 喵喵喵

这时,如果有一个实例对象,修改了共享的数据,另一个实例对象也会受到影响。

1
2
cat2.changeSound("啦啦啦");
cat1.makeSound(); // 啦啦啦