《Javascript语言精粹》读书笔记

本书指出了JavaScript这门语言的精华与糟粕,作者也是JSON的创立者,更是莎士比亚迷,每一章前面都会引用莎士比亚的一句名言~

Chap-1 精华

好的部分

函数,弱类型,动态对象,对象字面量表示法。

弱类型

强类型允许编译器在编译时检测错误。虽然强类型为主流要求,但是弱类型更自由,无须建立复杂的类层次,不用疲于应付系统以得到想要的行为。

字面量表示法

简单直观,列出对象的组成部分就是创建过程,JSON的灵感来源。

原型继承

(class-free)无类别对象系统,对象直接从其他对象继承属性。

坏的部分

基于全局变量的编程模型

Chap-2 语法

数字

  • 单一数字类型,表示为64位的浮点数,没有分离出单独的整数类型。1和1.0是相同的值。避免了短整数的溢出问题。
  • 数字字面量可以有指数部分:

    1
    2
    var a = new Number(1e2);
    console.log(a); //[Number: 100]
  • 数字有方法。Math对象拥有作用于数字的一套方法。如Math.floor

字符串

  • JavaScript被创建时,Unicode是16位的字符集,所以JavaScript所有字符都是16位的。
  • \u只懂用数字表示的字符码位。"A" === "\u0041";
  • 字符串有length属性:'seven'.length === 5;
  • 字符串不可变,'+'运算符连接后的字符串是新字符串
  • 'c' + 'a' + 't' === 'cat';
  • 字符串有方法。如toUpperCase()

语句

  • 花括号创建的代码块,并没有创建新的作用域。
  • if为假的值:false, null, undefined, ' ', 0, NaN

    1
    2
    3
    4
    5
    for(myvar in obj){
    if(obj.hasOwnProperty(myvar)){ //是该对象成员还是原型链里找到的
    ...
    }
    }
  • try...catch:在try语句中throw一个异常对象,有namemessage属性。

  • return语句:若没有指定返回表达式则返回值为undefinedreturn关键字和表达式之间不能换行,break关键字和标签之间也不能换行。

表达式

  • 运算符优先级
  • typeof返回值:number, string, boolean, undefined, function, object。对于数组和null也返回object。这样不好。
  • /运算符可能产生一个非整数结果,即使两个运算数都是整数。

Chap-3 对象

  • 简单类型:数字,字符串,布尔值,null值和undefined值。其他值(包括正则表达式)均为对象。
  • 对象是键控集合(keyed collections),数字、字符串、布尔值貌似对象,因为它们拥有方法,但它们是不可变的,故而不是对象。
  • 属性名:包括空字符串在内的任意字符串;属性值:除undefined值以外的任何值。、
  • JS对象是无类别(class-free)的。对于新属性的名字和值没有约束。
  • 原型链特性:对象继承,减少对象初始化的时间和内存消耗。

对象字面量

对象字面量即“名/值”对,如果属性名合法且不是保留字,并不强制要求用引号括住属性名,如:"first-name"必须括住,而first_name可以不用引号括住。

  • 读取属性和给属性赋值:可以用方括号表示法和点表示法,优先后者。方括号方式的优点是:可以使用变量和表达式作为属性。赋值时,若属性已存在则更新属性值,不存在则创建属性值。
  • 检索属性值:用&&避免该属性不存在而报错
  • ||填充属性默认值
  • 对象通过引用传递,而不是拷贝。

原型

原型连接在属性值更新时不起作用,只有检索值的时候才用到。委托:层层向上寻找某属性,直到找到Object.prototype为止,若没有则返回undefined

创建一个使用原对象作为其原型的新对象:

1
2
3
4
5
6
7
8
9
10
11
12
if(typeof Object.beget !== 'function'){
Object.beget = function(o){
var F = funcgtion(){};
F.prototy
> Blockquote
pe = o;
return new F();
};
}
var another_stooge = Object.beget(stooge);

反射

检查对象有什么属性:typeof确定属性的类型,可以用来排除函数;hasOwnProperty确定是否是对象独有的属性,不检查原型链。

枚举

for...in语句:

  • 会列出包含原型链属性在内的所有属性:结合反射,过滤掉你不想要的值。
  • 出现的顺序不确定。

删除

delete运算符。移除对象中属性,不触及原型链中的任何对象。删除对象的属性可能会让来自原型链中的属性暴露出来。

减少全局变量污染

最小化使用全局变量:在应用中只创建唯一一个全局变量(命名空间):

1
var MYAPP = {};

把多个全局变量整理在一个命名空间下,避免与其他应用程序、组件或类库之间相互影响。

或:使用闭包。

Chap-4 函数

代码复用、信息隐藏、组合调用。

函数对象

每个对象都拥有一个连到原型对象的隐藏连接:对象字面量产生的对象连接到Object.prototype,函数对象连接到Function.prototype(该原型对象本身连接到Object.prototype)。

每个函数在创建时附有两个附加隐藏属性:函数的上下文,实现函数行为的代码。

每个函数创建时,该函数对象有一个prototype属性,该属性值也是一个对象,该对象的constructor属性值为该函数,即:

1
2
function A(){}
A.prototype.constructor == A; //true

函数字面量

通过函数字面量定义函数,如果没有给函数命名,会被认为是匿名函数:

1
var add = function(a, b){} //匿名函数

函数字面量可以出现在任何允许出现表达式的地方。

闭包:通过函数字面量创建的函数对象包含一个连到外部上下文的连接。闭包中能访问到它被嵌套在其中的那个函数的参数与变量。

函数调用

函数调用时,接收的参数除了声明时定义的形式参数之外(如果实际参数过多了,超出的参数值将被忽略;如果实际参数值过少,缺失的参数值为undefined),还会接收两个附加参数:thisargumentsthis的值取决于函数调用的模式:

  • 方法调用模式
    函数被保存为对象的一个属性:方法。
    超级迟绑定:方法被调用时,该方法所属对象的this绑定到方法上。高度复用this
    公共方法:通过this可以取得其所属对象的上下文的方法

  • 函数调用模式
    函数并非对象属性时,为函数调用。this指向全局对象。这是设计上的错误,没法通过内部函数的this访问到外部变量。解决方法:var that = this;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var myObj = {
    value: 0
    };
    myObj.double = function(){ ////该函数是对象myObj的属性,其内部的this就指向myObj对象的this,故可通过this.value访问到myObj对象的value属性值
    var that = this;
    var helper = function(){ //该函数不是对象myObj的属性,其内部的this不指向myObj对象的this,故无法通过this.value访问到myObj对象的value属性值
    that.value = that.value + that.value;
    };
    helper();
    }
  • 构造器调用模式
    new Constructor()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //constructor
    var Quo = function(str){
    this.status = str;
    };
    //公共方法,所有实例共有
    Quo.prototype.getStatus = function(){
    return this.status; //this指向该构造函数构造的对象
    }
    var myQuo = new Quo("confused");
    console.log(myQuo.getStatus()); //confused
  • apply调用模式
    func.apply(将被绑定给this的值, arguments array);
    若第一个参数为null,则直接调用函数本身。

参数

arguments类数组对象。不推荐的模式。

返回

如果函数以new调用,且返回值不是一个对象,则返回this(该新对象)

异常

throw语句中断函数执行,抛出一个exception对象,该对象包含可识别异常类型的name属性和message属性,也可添加其他属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var add = function(a, b){
if(typeof a !== 'number' || typeof b !== 'number'){
throw{
name: 'TypeError',
message: 'add needs numbers',
...
};
}
return a + b;
};
var try_it = function(){
try{
add('seven');
}catch(e){
console.log(e.name + ': ' + e.message);
}
}
try_it();

产生的exception对象会被传递到catch从句。

给类型增加方法

本质:原型继承的动态性,新添加的方法立刻赋予到所有对象实例上,哪怕对象实例在方法被创建前就建立好了。

1
2
3
4
5
Function.prototype.method = function(...){...}
Number.method('integer', function(){});
String.method('trim', function(){
return this.replace(/^\s+|\s+$/g/, "");
});

注意:

  • 确定没有该方法时才添加它:

    1
    2
    3
    if(!this.prototype[name]){
    this.prototype[name] = func;
    }
  • 因为是在原型上添加,而for...in在原型上的表现很糟糕,使用注意判断筛选。

递归

汉诺塔问题

1
2
3
4
5
6
//disc: 圆盘数量,aux:辅助柱子
var hanoi = function(disc, src, aux, dst){
hanoi(disc - 1, src, dst, aux);
console.log('Move disc ' + disc + ' from ' + src + ' to ' + dst);
hanoi(disc - 1, aux, src, dst);
};

递归处理给定DOM树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var walk_the_DOM = function walk(node, func){
func(node);
node = node.firstChild;
while(node){
walk(node, func);
node = node.nextSibling;
}
};
//查找节点的att属性名为
var getElementsByAttribute = function(att, value){
var results = [];
walk_the_DOM (document.body, function(node){
var actual = node.nodeType === 1 && node.getAttribute(att);
if(typeof actual === 'string' &&
(actual === value || typeof value !== 'string') ){
//如果value不是string类型,说明调用函数时没有提供可选参数,则直接push就好
results.push(node);
}
});
return results;
}

尾递归优化

尾递归:如果函数返回自身递归调用的结果(如Fibonacci序列),调用过程会被替换为一个循环,提高速度。
尾递归优化:JavaScript没有提供尾递归优化。所以深度递归可能导致堆栈溢出。

作用域

控制变量和参数的可见性及生命周期。
JavaScript没有块级作用域:最好在函数体的顶部声明函数中可能用到的所有变量。

闭包

闭包:函数可以访问它被创建时所处的上下文环境。

example1
1
2
3
4
5
6
7
8
9
10
11
12
var myObj = function(){
var value = 0;
return {
increment: function(inc){
value += typeof inc === 'number' ? inc : 1;
},
getValue: function(){
return value;
}
};
}();

返回的对象中的两个闭包仍能访问value

example2

关于前述的构造器调用模式:用了一个getter函数去访问本可以直接访问的属性status。修改:将status属性设为私有属性,然后使用闭包去访问它:

1
2
3
4
5
6
7
8
9
10
var quo = function(status){
return {
get_status: function(status){
return status;
}
}
};
//创建一个quo实例
var myQuo = quo("amazed");
console.log(muQuo.get_status()); //"amazed"

即使quo已经返回了,但是闭包get_status仍然享有访问quo对象的status属性的特权。

example3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//设置一个DOM节点为黄色,然后把它渐变为白色。注意该脚本应写在body标签内,如果写在head标签内,则body还没渲染出来。
var fade = function(node){
var level = 1;
var step = function(){
var hex = level.toString(16);
node.style.backgroundColor = '#FFFF' + hex + hex;
if(level < 15){
level += 1;
setTimeout(step, 100);
}
};
setTimeout(step, 100);
};
fade(document.body);
example4

预期结果:单击每个节点时弹出节点序号

1
2
3
4
5
6
7
8
var add_the_handlers = function(nodes){
var i;
for(i = 0; i < nodes.length; i++){
nodes[i].onclick = function(e){
alert(i);
};
}
};

实际结果:单击每个节点都弹出节点总数,即循环结束时i最后的值。
修改:使用闭包

1
2
3
4
5
6
7
8
9
10
var add_the_handlers = function(nodes){
var i;
for(i = 0; i < nodes.length; i++){
nodes[i].onclick = function(i){
return function(e){
alert(i);//这里的i为事件处理函数被构造时的i的值
}
};
}
};

回调

发起异步请求,当服务器的响应到达时调用回调函数,避免客户端假死。

模块

模块是一个提供接口却隐藏状态与实现的函数或对象。

examle1

String添加方法,寻找字符串中的HTML字符实体并替换为它们对应的字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
String.method('deentityify', function(){
//字符实体表:定义在函数中,避免成为全局变量。
var entity = {
quot: '"',
lt: '<',
gt: '>'
};
return function(){
return this.replace(/&([^&;]+)/g, function(a, b){
var r = entity[b];
return typeof r === 'string' ? r : a;
});
};
}()); //返回的闭包才是真正的deentityify函数
console.log('&lt;&quot;&gt;'.deentityify()); //<">

只有deentityify方法有权访问entity对象

模块模式:一个定义了私有变量和函数(可访问私有变量的闭包)的函数。避免了全局变量的使用。

example2

用来产生安全的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//产生唯一字符序列
var serial_maker = function(){
var prefix = '';
var seq = 0;
return {
set_prefix: function(p){
prefix = String(p);
},
set_seq: function(s){
seq = s;
},
gensym: function(){
var result = prefix + seq;
seq += 1;
return result;
}
};
};
var seqer = serial_maker();
seqer.set_prefix('Q');
seqer.set_seq(1000);
var unique = seqer.gensym(); //"Q1000"

如果把unique作为一个值传给第三方函数,那个函数能使用它,但是不能通过它改变prefixseq的值。

级联

没有返回值的函数,如果让其返回this而不是undefined,就可以启用级联(链式调用)。在一条语句中依次调用同一个对象的很多方法。

套用:curry

套用:将函数与传递给它的参数结合产生一个新的函数。给函数添加curry方法:

1
2
3
4
5
6
7
8
Function.method('curry', function(){
var slice = Array.prototype.slice,
args = slice.apply(arguments), //原函数的参数
that = this; //原函数
return function(){
return that.apply(null, args.concat(slice.apply(arguments))); //将原函数的参数args和新函数的参数arguments结合
};
});

记忆

函数用对象记住先前的操作结果,避免无谓的运算。比如Fibonacci数列:

1
2
3
4
5
6
7
8
9
10
11
12
13
var fibonacci = function(){
var memo = [0, 1];
var fib = function(n){
var result = memo[n];
if(typeof result !== 'number'){
result = fib(n - 1) + fib(n - 2);
memo[n] = result;
}
return result;
};
return fib;
}();

=> 带记忆功能的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var memorizer = function(memo, fundamental){
var shell = function(n){
var result = memo[n];
if(typeof result !== 'number'){
result = fundamental(shell, n);
memo[n] = result;
}
return result;
};
return shell;
};
//使用memoizer来定义fibonacci函数
var fibonacci = memorizer([0, 1], function(shell, n){
return shell(n - 1) + shell(n - 2);
});
//使用memoizer来定义阶乘函数
var factorial = memorizer([1, 1], function(shell, n){
return n * shell(n - 1);
});

Chap-5 继承

对象可以直接从其他对象继承。

伪类 pseudoclassical

创建一个类,其原型为父类对象

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
var Mammal = function(name){
this.name = name;
};
Mammal.prototype.get_name = function(){
return this.name;
};
Mammal.prototype.says = function(){
return this.saying || '';
};
//构造伪类
var Cat = function(name){
this.name = name;
this.saying = 'meow';
};
//继承
Cat.prototype = new Mammal();
//扩充新原型对象
Cat.prototype.purr = function(n){
var i, s = '';
for(i = 0; i < n; i++){
if(s){
s += '-';
}
s += 'r';
}
return s;
};
Cat.prototype.purr = function(n){
var i, s = '';
for(i = 0; i < n; i++){
if(s){
s += '-';
}
s += 'r';
}
return s;
};
Cat.prototype.get_name = function(){
return this.says() + ' ' + this.name + ' ' + this.says();
};
var myCat = new Cat('Henrietta');
console.log(myCat.says()); //"meow"
console.log(myCat.purr(5)); //"r-r-r-r-r"
console.log(myCat.get_name()); //"meow Henrietta meow"

对象说明符

传参时,若有多个参数,考虑将其写在一个参数对象里,将参数对象传入函数。

1
2
3
4
5
6
7
8
var myObj = maker(f, l, m, c, s);
//=>
var myObj = maker({
first: f,
last: l,
state: s,
city: c
});

原型

用前面给对象添加的beget方法继承:差异化继承

1
2
3
4
5
var myMammal = new Mammal();
var myCat = Object.beget(myMammal);
myCat.name = 'Henrietta';
myCat.saying = 'meow';
//...

函数化:模块模式

函数化构造器的伪代码模板:

1
2
3
4
5
6
7
8
var constructor = function(spec, my){
var that, 其他私有实例变量;
my = my || {};
把共享变量和函数添加到my中
that = 一个新对象
添加给that的特权方法
return this;
};

相比较与伪类模式,函数话模式具有更好的封装和信息隐藏,以及访问父类方法的能力。

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
var mammal = function(spec){
var that = {};
that.get_name = function(){
return spec.name;
};
that.says = function(){
return spec.saying || '';
};
return that;
};
//mammal构造器的作用:返回一个对象,只能通过该对象访问spec对象的属性和方法。
var cat = function(spec){
spec.saying = spec.saying || 'meow';
//只能通过that对象访问spec对象的属性和方法
var that = mammal(spec); //inherit from mammal
//为that方法添加私有方法
that.purr = function(n){
return that.get_name() + ' purr';
};
that.get_name = function(){
return that.says() + ' ' + spec.name;
};
return that;
};
//cat构造器的作用:返回一个对象,只能通过该对象访问父类和子类方法属性。
var myCat = cat({name: 'Henrietta'});

函数化模式提供了处理父类方法的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Object.method('superior', function(name){
var that = this,
method = that[name];
return function(){
return method.apply(that, arguments);
};
});
var coolcat = function(spec){
var that = cat(spec),
super_get_name = that.superior('get_name');
that.get_name = function(){
return 'like ' + super_get_name() + ' baby';
};
return that;
};
var myCoolCat = coolcat({name: 'Bix'});
var name = myCoolCat.get_name(); //'like meow Bix baby'

持久性:使用函数化模式创建一个对象,且对象的所有方法都不使用thisthat。该对象所有状态都是私有的。

部件

构造一个能添加事件处理函数到任何对象上的函数:

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
var eventuality = function(that){
var registry = {};
that.fire = function(event){
var array, func, handler, i,
type = typeof event === 'string' ? event : event.type; //string or array
if(registry.hasOwnProperty(type)){ //如果这个事件存在一组事件处理程序,则遍历它们依次执行
array = registry[type];
for(i = 0; i < array,.length; i++){
handler = array[i]; //array的每个元素,都是个对象,有method属性和parameters属性
func = handler.method;
if(typeof func === 'string'){
func = this[func];
}
func.apply(this, handler.parameters || [event]);
}
}
return this;
};
that.on = function(type, method, parameters){
//注册一个事件
var handler = {
method: method,
prarmeters: parameters
};
if(registry.hasOwnProperty(type)){
registry[type].push(handler);
}else{
registry[type] = [handler];
}
return this;
};
return that;
};
//调用:可在任何对象上调用,授予该对象事件处理方法。
eventuality(that);

Chap-6 数组

数组字面量

数组字面量:var arr = []; 继承自Array.rpototype
array-like 类数组:var arr = { '0': 'a', '1': 'b' }继承自Object.prototype
JavaScript允许数组包含任意混合类型的值。

长度

length属性值是这个数组的最大整数属性名+1,它不一定等于数组之际属性个数。
[]后缀下标运算符将它的表达式转换成字符串(一般调用该表达式的toString方法)。
设置更大的length无须给数组分配更多空间,设置更小的length导致下标小于新length的属性被删除。

删除

  • delete运算符:删除的元素下标会保留,对应值变为undefined
  • 更好的方法:splicearr.splice(index, delLength, replace elements); 但是每删除一次后面的元素就要移动位置,对大型数组可能效率不高。

枚举

虽然可以用for...in,但是最好不要,因为:

  1. 可能遍历到原型链中的属性
  2. 无法保证属性顺序
    最好用for(var ... )循环

混淆的地方

  • typeof对数组、对象和null的判断结果均为'object'
  • 下面方法对不同'window''frame'里构造的数组会失效:

    1
    2
    3
    a &&
    typeof a === 'object' &&
    a.constructor === Array;
  • 最可靠的写法:

    1
    2
    3
    4
    5
    a &&
    typeof a === 'object' &&
    a.length === 'number' &&
    typeof a.splice === 'function' &&
    !(a.propertyIsEnumerable('length')); //length属性是否会被for...in是否会枚举出来

方法

可以通过Array.prototype扩充对所有数组有效的方法,或通过arr.func给某个特定数组对象添加方法。

维度

JavaScript没有多维数组,但是它支持元素为数组的数组。

Chap-7 正则表达式 Regular Expression

相关方法:
regexp.exex
regexp.test
string.match
string.replace
string.search
string.split
正则表达式必须写在一行中

例子

example1

var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]&*))?(?:\?([^#]*))?(?:#(.*))?$/;
ex1
匹配结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var regexp_url = new RegExp(/^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/);
var url = 'http://www.ora.com:80/goodparts?q#fragment';
var result = regexp_url.exec(url);
console.log(typeof result); //object
var names = ['url', 'scheme', 'slash', 'host', 'port', 'path', 'query', 'hash'];
var blanks = ' '; //这个字符串时去了最长单词scheme的长度+1,和后面的blank.substring(names[i].length)结合使用,以便排版。
var i;
for(i = 0; i < names.length; i++){
console.log(names[i] + ":" + blanks.substring(names[i].length) + result[i]);
}
/*
url: http://www.ora.com:80/goodparts?q#fragment
scheme: http
slash: //
host: www.ora.com
port: 80
path: goodparts
query: q
hash: fragment
*/

解释:
(?:([A-Za-z]+):)? 匹配协议名
?:表示一个非捕获型分组(noncapturing group),后缀?表示该分组重复0次或1次。括号中的内容表示一个捕获型分组,有编号,对应result数组的下标。[]表示字符类。+表示一次或多次。

example2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var regexp_number = /^-?\d+(?:\.\d*)?(?:e[+\-]?\d+)?$/i;
var test = function(num){
console.log(regexp_number.test(num));
};
test('1');
test('-112');
test('number'); //false
test('98.6');
test('132.21.86'); //false
test('12.45E-67'); //true
test('12.45e-67'); //true
test('12.45D-67'); //false

大小写问题: /i 表示忽略大小写。也可以不写/i,把e写成[Ee](?:[E|e])

结构

优先使用正则表达式字面量构造正则表达式。
使用字面量创建的正则表达式对象共享一个实例。
如果使用RegExp构造器,需要双写反斜杠并对引号进行转义,且第二个参数为标志g,i,m
RegExp构造器适用于运行时动态生成的情形。

元素

/in|int/如果匹配了in就不会匹配int
当指定了/m标志时,$也能匹配行结束符。

Col1 Col2
\f 换页符
\n 换行符
\r 回车
\t tab
\d [0-9]
\D [^0-9]
\s 空白字符:空格,tab,回车,换行,换页
\S 非空白字符
\w 单词字符 [0-9A-Z_a-z]
\W 非单词字符
\1 指向分组1所捕获到的文本的引用

分组

捕获型

是一个被包在圆括号中的正则表达式。

非捕获型

有前缀(?:。仅做简单匹配,并不捕获所匹配文本。即它只进行匹配,并不保存结果供以后引用。“微弱的性能优势”。veda文章

向前正向匹配

有前缀(?=)。类似于非捕获型分组,匹配后,文本将倒回到它开始的地方,实际上并不匹配任何东西。是个好特性。
veda文章

向前负向匹配

有前缀(?!)。类似于向前正向匹配分组。只有当它匹配失败时它才进行匹配。

量词

?等同于 {0, 1}
*等同于 {0, }
+等同于 {1, }
如果只有一个量词,则趋向于进行贪婪性匹配:匹配尽可能多的重复直至到达上限。

Chap-8 方法

Array

array.concat(item...)array.push()
都可以接收多个参数,不同之处在于:前者返回新数组,后者改变原数组。
array.join(seperator)返回字符串
array.pop()array.shift():分别移除并返回最后一个和第一个
array.slice(start, end)浅复制
array.sort(comparefn):
不带参数默认视作字符串排序。自定义比较函数,将数字按照从小到大排序:

1
2
3
array.sort(function(a, b){
return a - b;
});

构造一个给对象数组排序的比较函数(面试有被问到过):

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
//by函数接受一个成员名字符串作为参数,返回一个根据该成员名进行排序的比较函数
var by = function(name){
return function(o, p){
var a, b;
if(typeof o === 'object' && typeof p === 'object' && o && p){
a = o[name];
b = p[name];
if(a == b){
return 0; //即按对象原本顺序
}
if(typeof a === typeof b){
return a < b ? -1 : 1;
}
return typeof a < typeof b ? -1 : 1;
}else{
throw{
name: 'Error',
message: 'Expected an object when sorting by ' + name
};
}
};
};
var s = [
{first: 'Joe', last: 'Besser'},
{first: 'Moe', last: 'Howard'},
{first: 'Joe', last: 'DeRita'},
{first: 'Shemp', last: 'Howard'},
{first: 'Larry', last: 'Fine'},
{first: 'Curly', last: 'Howard'}
];
s.sort(by('first'));
console.log(s);
/*
[ { first: 'Curly', last: 'Howard' },
{ first: 'Joe', last: 'Besser' },
{ first: 'Joe', last: 'DeRita' },
{ first: 'Larry', last: 'Fine' },
{ first: 'Moe', last: 'Howard' },
{ first: 'Shemp', last: 'Howard' } ]
*/
//可以连续调用(不稳定)
s.sort(by('first')).sort(by('last'));
/*
[ { first: 'Joe', last: 'Besser' },
{ first: 'Joe', last: 'DeRita' },
{ first: 'Larry', last: 'Fine' },
{ first: 'Curly', last: 'Howard' },
{ first: 'Moe', last: 'Howard' },
{ first: 'Shemp', last: 'Howard' } ]
*/

修改参数,让其可接收第二个参数:当主要键值相同时,使用次要参数比较。

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
var by = function(name, minor){
return function(o, p){
var a, b;
if(o && p && typeof o === 'object' && typeof p === 'object'){
a = o[name];
b = p[name];
if(a == b){
return typeof minor === 'function' ? minor(o, p) : 0;
}
if(typeof a === typeof b){
return a < b ? -1 : 1;
}
return typeof a < typeof b ? -1 : 1;
}else{
throw{
name: 'Error',
message: 'Expected an object when sorting by ' + name
};
}
};
};
s.sort(by('last', by('first')));
/*
[ { first: 'Joe', last: 'Besser' },
{ first: 'Joe', last: 'DeRita' },
{ first: 'Larry', last: 'Fine' },
{ first: 'Curly', last: 'Howard' },
{ first: 'Moe', last: 'Howard' },
{ first: 'Shemp', last: 'Howard' } ]
*/

array.splice(start, deleteCount, item) 返回被移除元素的数组
array.unshift(item...)在数组首部插入元素,返回新数组的长度。

Function

Function.apply(thisArg, thisArray);

Number

number.toExponential(fragtionDigits)将数字转换成指数形式的字符串,参数为小数点后的数字位数(0~20)。
number.toFixed(fractionDigits)将数字转换成十进制形式的字符串,参数为小数点后的数字位数(0~20)。
number.toPrecision(precision)将数字转换成十进制形式的字符串,参数为有效位数(0~21)。
number.toString(radix)将数字转换成字符串,参数为基数。不指定基数也可以写成String(number)

Object

object.hasOwnProperty()

RegExp

regexp.exec(string) 使用正则表达式最强大和最慢的方法。
匹配成功返回数组:[子字符串, 分组1捕获的文本, 分组2捕获的文本, ...]
匹配失败返回null
如果设置了全局标志/g,查找会从regexp.lastIndex开始。如果匹配成功,lastIndex为匹配字符的末字符,如果匹配失败,regexp.lastIndex为0。
如果在一个循环中调用exec去查询一个匹配模式在一个字符串中发生几次,注意如果提前退出了循环,再次进入该循环前必须把regexp.lastIndex置0.

regexp.test(string) 使用正则表达式最简单和最快的方法。不要对这个方法使用g标志。返回true/false

String

string.charAt(pos) 返回类型为字符串
string.charCodeAt(pos)返回字符的ASCII码
string.concat(string...)可接收多个参数
string.indexOf(searchString, pos)searchString开始找
string.lastIndexOf(searchString, pos)
string.match(regexp)若有g标志,则返回一个数组,包含除捕获分组之外的所有匹配分组。(?)
string.replace(searchValue, replaceValue):如果searchValue是个字符串或是个没有g标志的正则表达式,则只会在第一次出现的地方被替换。

  • 如果replaceValue字符串中若含$,表示特殊含义:
特殊符号 替换对象
$$ $
$& 整个匹配的文本
$number 分组捕获的文本
$` 匹配之前的文本
$’ 匹配之后的文本
1
2
3
4
5
6
var reg = /\((\d{3})\)/g;
var p = '333(555)666-1212'.replace(reg, '!!!$`~~~');
console.log(p); //333!!!333~~~666-1212
var p = '333(555)666-1212'.replace(reg, '!!!$\'~~~'); //333!!!666-1212~~~666-1212
var p = '333(555)666-1212'.replace(reg, '!!!$&~~~'); //333!!!(555)~~~666-1212
var p = '333(555)666-1212'.replace(reg, '!!!$1~~~'); //333!!!555~~~666-1212
  • replaceValue也可以是个函数:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    String.method = function(name, func){
    this.prototype[name] = func;
    return this;
    };
    console.log(String.method); //[Function]
    String.method('entityify', function(){
    var character = {
    '<': '&lt;',
    '>': '&gt;',
    '&': '&amp;',
    '"': 'quot;'
    };
    return function(){
    return this.replace(/[<>&"]/g, function(c){
    return character[c];
    });
    };
    }());
    console.log("<&>".entityify()); //&lt;&amp;&gt;

string.search(regexp):只接收正则表达式作为参数,不接受字符串。会忽略g标志。返回第一个匹配的首字符位置,没找到返回-1。
string.slice(start, end) 若参数为负,自动加上字符串长度。最好不用sutstring
string.split(seperator, limit):将字符串分割为字符串数组。seperator是字符串或正则表达式,若为空字符串,则返回单字符数组。忽略g标志。limit可选。
如果正则表达式中有捕获分组,则捕获分组文本会被包含在分割后的数组中:

1
2
3
4
var text = 'last, first, middle';
var d = text.split(/\s*,\s*/);
console.log(d); //[ 'last', 'first', 'middle' ]
var d = text.split(/\s*(,)\s*/); //[ 'last', ',', 'first', ',', 'middle' ]

IE会省略输出数组中的空字符串。

string.toLowerCase(), string.toUpperCase()
string.fromCharCode(char...)从一串数字字符码中返回一个字符串

分享
0%