3、引用类型(3)(《JS高级》笔记)

四、RegExp类型

JS通过RegExp类型来支持正则表达式,语法如下:

var expression = / pattern / flags ;

其中的模式(pattern)部分可以是任何简单或复杂的正则表达式,可以包含字符类、限定符、分组、向前查找以及反向引用。每个正则表达式都可带有一或多个标志(flags),用以标明正则表达式的行为。可以有如下三个标志

  • g:表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
  • i:表示不区分大小写模式。即在确定匹配项时忽略模式与字符串的大小写;
  • m:表示多行模式。即在达到一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。

注意:在正则表达式中,模式中使用的所有元字符都必须转义(转义符号是\\。元字符如下:

( [ { \ ^ $ | ) ? * + . ] }

例如:

//匹配所有".at",不区分大小写
var pattern = /\.at/gi;

以上例子是以字面量形式来定义的正则表达式。另一种创建正则表达式的方式是使用RegExp构造函数,接收两个参数:一个是要匹配的字符串模式,另一个是可选的标志字符串。

//两种方式定义同一个正则表达式,匹配第一个"bat"或"cat",不区分大小写
var pattern1 = /[bc]at/i;
var pattern2 = new RegExp("[bc]at", "i");

说明:这里要注意,传递给RegExp构造函数的两个参数都是字符串(不能把正则表达式字面量传递给构造函数)。由于构造函数的模式参数是字符串,所以在某些情况下要对字符进行双重转义。如:

字面量模式 等价字符串
/\[bc\]at/ "\\\\[bc\\\\]at"
/\.at/ "\\\\.at"
/name\/age/ "name\\\\/age"
/\d.\d{1,2}/ "\\\\d.\\\\d{1,2}"
/\w\\\\hello\\\\123/ "\\\\w\\\\\\\\hello\\\\\\\\123"

使用正则表达式字面量和使用RegExp构造函数创建的正则表达式不一样。在ECMAScript 3中,正则表达式字面量始终会共享同一个RegExp实例,而使用构造函数创建的每一个新RegExp实例都是一个新实例:

var re = null, i;
for(i = 0; i < 10; i++){
  re = /cat/g;
  re.test("catastrophe");
}

for(i = 0; i < 10; i++){
  re = new RegExp("cat", "g");
  re.test("catastrophe");
}

说明:在第一个循环中,机试是循环体中指定的,但实际上只为/cat/创建了一个RegExp实例。由于实例属性(后面介绍)不会重置,所以在循环中再次调用test()方法会失败。因为第一次调用test()方法找到了"cat",但第二次调用是从索引为3的字符(上一次匹配的末尾)开始的,所以就找不到它了。由于测试到字符串末尾,所以再下一次调用test()方法就又从头开始了。而使用RegExp构造函数会每次都创建新实例,所以不会失败。

4.1 RegExp 实例属性

RegExp的每个实例都具有下列属性:

  • global:布尔值,表示是否设置了g标志
  • ignoreCase:布尔值,表示是否设置了i标志
  • lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从零算起
  • multiline:布尔值,表示是否设置了m标志
  • source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。
var pattern1 = /\[bc\]at/i;

alert(pattern1.global);     //false
alert(pattern1.ignoreCase); //true
alert(pattern1.multiline);  //false
alert(pattern1.lastIndex);  //0
alert(pattern1.source);     //"\[bc\]at"

4.2 RegExp 实例方法

  • exec()方法
    RegExp对象的主要方法是exec(),该方法是专门为捕获组而设计的。接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组,在没有匹配项的情况下返回null。返回的数组是一个Array实例,而且包含两个额外的属性:indexinput。其中index表示第一个匹配项在字符串中的位置,而input表示应用正则表达式的字符串。返回的数组中第一项是与整个模式匹配的字符串,其他项是与模式中的捕获组(就是打括号的)匹配的字符串(如果模式中没有捕获组,则数组只包含一项)。
var text = "mom and dad and baby";
//全局搜索,忽略大小写,问号表示任意多少个
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);

alert(matches.index);    //0
alert(matches.input);    //"mom and dad and baby"
alert(matches[0]);       //"mom and dad and baby"
alert(matches[1]);       //" and dad and baby"
alert(matches[2]);       //" and baby"

说明:上述代码中,最外层的捕获组是" and dad( and baby)?",里层的捕获组是"( and baby)"注意:对于exec()方法而言,即使在模式中设置了全局标志,它每次也只会返回一个来匹配项。在不设置全局标志的情况下,在同一个字符串上多次调用此方法将始终返回第一个匹配项的信息。而设置了全局标志时,则每次调用都会在字符串中继续查找新匹配项。

  • test()方法
    此方法接受一个字符串参数。在模式与该参数匹配的情况下返回true,否则返回false。在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用此方法是一个不错的选择。

  • RegExp实例继承的toLocaleString()toString()方法都会返回正则表达式的字面量。而其valueOf()方法返回正则表达式本身。

var pattern = new RegExp("\\[bc\\]at", "gi");
alert(pattern.toString());    // /\[bc\]at/gi
alert(pattern.toLocaleString());    // /\[bc\]at/gi

4.3 RegExp构造函数属性

RegExp构造函数属性适用于作用域中的所有正则表达式,并且基于所执行的最近一次正则表达式操作而变化。可以通过两种方式访问它们。

长属性名 短属性名 说明
input $_ 最近一次要匹配的字符串
lastMatch $& 最近一次的匹配项
lastParen $+ 最近一次的捕获组
leftContext $`tab上方的符号) input字符串中lastMatch之前的文本
nultiline $* 布尔值,表示是否所有表达式都使用多行模式
rightContext $'(单引号) input字符串中lastMatch之后的文本

使用这些属性可以从exec()test()执行的操作中提取出更具体的信息。

var text = "this has been a short summer";
var pattern = /(.)hort/g;

/*
 * 注意: Opera 不支持 input, lastMatch, lastParen, 和 multiline属性
 * IE不支持multiline
 */        
if (pattern.test(text)){
    alert(RegExp.input);               //this has been a short summer
    alert(RegExp.leftContext);         //this has been a注意:没有后面的s
    alert(RegExp.rightContext);        // summer
    alert(RegExp.lastMatch);           //short
    alert(RegExp.lastParen);           //s
    alert(RegExp.multiline);           //false
}

说明:

  • input属性返回了原始的字符串
  • leftContext属性返回了单词short之前的字符串,而rightContext属性则返回了short之后的字符串;
  • lastMatch属性返回了最近一次与整个正则表达式匹配的字符串,即short
  • lastParen属性返回了最近一次匹配的捕获组,即例子中的s

例子中使用的长属性名都可以使用相应的短属性名来代替,只是这些短属性名大都不是有效的ECMAScript标识符,因此必须通过方括号语法来访问它们:

var text = "this has been a short summer";
var pattern = /(.)hort/g;

/*
 * 注意: Opera 不支持 input, lastMatch, lastParen, 和 multiline属性
 * IE不支持multiline
 */        
if (pattern.test(text)){
    alert(RegExp.$_);               //this has been a short summer
    alert(RegExp["$`"]);            //this has been a            
    alert(RegExp["$'"]);            // summer
    alert(RegExp["$&"]);            //short
    alert(RegExp["$+"]);            //s
    alert(RegExp["$*"]);            //false
}

说明:还有多达(只有)九个用于存储捕获组的构造函数属性。访问这些属性的语法是RegExp.$1、RegExp.$2、...、RegExp.$9,分别用于存储第一、第二、...、第九个匹配的捕获组。

var text = "this has been a short summer";
var pattern = /(..)or(.)/g;
      
if (pattern.test(text)){
    alert(RegExp.$1);       //sh
    alert(RegExp.$2);       //t
}

五、Function类型

函数实际上是对象,每个函数都是Function类型的实例,因为函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。定义如下:

//函数声明
function sum(num1, num2){
  return num1 + num2;
}

//函数表达式
var sum = function(num1, num2){
  return num1 + num2;
};//注意这里有个分号

//使用构造函数
var sum = new Function("num1", "num2", "return num1 + num2");//不推荐

说明:由于函数名是一个指针,所以,一个函数可能会有多个名字

function sum(num1, num2){
    return num1 + num2;
}        
alert(sum(10,10));    //20

var anotherSum = sum;        
alert(anotherSum(10,10));  //20

sum = null;        
alert(anotherSum(10,10));  //20

说明:在函数赋值中,没有使用圆括号,所以这是访问函数指针,而不是调用函数。anotherSumsum都指向同一个函数,因此即使将sum设置为null,让它与函数“断绝关系”,但仍然可以正常调用anotherSum()

5.1 函数声明与函数表达式

解析器在向执行环境中加载数据时,会率先读取函数声明,并使其在执行任何代码之前可用(可以访问),至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。

alert(sum(10,10));    //20
function sum(num1, num2){
    return num1 + num2;
}        

说明:以上代码完全可以正常执行,因为在代码开始执行之前,解析器就已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。如果上面代码中函数定义不是使用函数声明,而是使用函数表达式则会出现错误。

5.2 作为值的函数

函数本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。例如,假设有一个对象数组,我们想要根据某个对象属性进行排序。而传递给数组sort()方法的比较函数要接收两个参数,即要比较的值。这里,我们可以定义一个函数,它接收一个属性名,然后根据这个属性名来创建一个比较函数。

function createComparisonFunction(propertyName) {

    return function(object1, object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];

        if (value1 < value2){
            return -1;
        } else if (value1 > value2){
            return 1;
        } else {
            return 0;
        }
    };
}

var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}];

data.sort(createComparisonFunction("name"));
alert(data[0].name);  //Nicholas

data.sort(createComparisonFunction("age"));
alert(data[0].name);  //Zachary        

说明:这里返回的函数会应用到数组中的每一项,实现按照相关属性对数组的排序。

5.3 函数内部属性(arguments、this、callee、caller)

在函数内部,有两个特殊的对象:argumentsthisarguments是一个类数组对象,包含着传入函数中的所有参数。arguments对象还有一个callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。

fucntion factorial(num){
    if(num <= 1){
        return 1;
    }else{
        return num * factorial(num - 1);
    }
}

说明:这个函数是一个递归函数。这样定义没有问题,但问题是这个函数的执行与函数名factorial紧密耦合在一起,在实际调用的时候会发现会出现问题,比如使用别的函数名就不能调用。这里可以使用arguments.callee解决。

fucntion factorial(num){
    if(num <= 1){
        return 1;
    }else{
        return num * arguments.callee(num - 1);
    }
}

说明:此时,在调用函数时可以使用任意的函数名。如:

var trueFactorial = factorial;
factorial = function(){
  return 0;
};
alert(trueFactorial(5));
alert(factorial (5));

函数内部另一个特殊对象是this,其行为和Java中的this大致类似。也就是this引用的是函数据以执行的环境——或者也可以说是this值(当在网页的全局作用域中调用函数时,this对象引用的就是window)。

window.color = "red";
var o = { color: "blue" };

function sayColor(){
    alert(this.color);
}

sayColor();     //red

o.sayColor = sayColor;
o.sayColor();   //blue

说明:上面定义的函数sayColor()是在全局作用域中定义的,引用了this对象。由于在调用函数之前,this的值并不确定,因此this可能会在代码执行过程中引用不同的对象。从两次调用函数返回不同的值也可以看出来。

ECMAScript 5规范了另一个函数对戏那个的属性:caller。这个属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null


function outer(){
    inner();
}

function inner(){
    alert(inner.caller);
    //alert(arguments.callee.caller);//为了实现跟松散的耦合
}

outer();

说明:以上代码会导致警告框中显示outer()函数的源代码。严格模式下,arguments.calleecaller都会导致错误,同时不能为函数的caller属性赋值,否则会导致错误。

5.4 函数属性和方法(length、prototype、applay()、call()、bind())

每个函数都包含两个属性:lengthprototype。其中,length属性表示函数希望接收的命名参数的个数。对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。也就是说,如toString()valueOf()等方法实际上都保存在prototype名下,只不过是通过各自对象的实例访问罢了。prototype属性是不可枚举的,因此使用for-in无法发现。

每个函数都包含两个非继承而来的方法:applay()、call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array实例,也可以使用arguments对象。

function sum(num1, num2){
    return num1 + num2;
}

function callSum1(num1, num2){
    return sum.apply(this, arguments);
}

function callSum2(num1, num2){
    return sum.apply(this, [num1, num2]);
}

alert(callSum1(10,10));   //20
alert(callSum2(10,10));   //20

说明:可以看到不管是传递arguments还是传递数组都可以正常运行。callSum1方法中传递了this作为this值(因为是在全局作用域中调用的,所以传入的是window对象)。而call()方法和apply()方法没有什么本质上的不同,只是调用的时候传入参数的时候,必须明确传入每一个参数。

function sum(num1, num2){
    return num1 + num2;
}

function callSum(num1, num2){
    return sum.call(this, num1, num2);
}

alert(callSum(10,10));   //20

说明:实际上,传递参数并非这两个函数的用武之地,真正强大的地方是能够扩充函数赖以运行的作用域。

window.color = "red";
var o = { color: "blue" };

function sayColor(){
    alert(this.color);
}

sayColor();            //red

sayColor.call(this);   //red
sayColor.call(window); //red
sayColor.call(o);      //blue

说明:可以看到,我们传入什么作用域,则函数就在哪个作用域上执行。

函数还有一个方法:bind()。这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。

window.color = "red";
var o = { color: "blue" };
                   
function sayColor(){
    alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor();   //blue

说明:起始功能很简单,就是设置一个函数的this值。

最后,每个函数继承的toLocaleString()、toString()、valueOf()方法始终都返回函数的代码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容

  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,231评论 0 4
  • 本章内容 使用对象 创建并操作数组 理解基本的 JavaScript 类型 使用基本类型和基本包装类型 引用类型的...
    闷油瓶小张阅读 680评论 0 0
  • 引用类型的值(对象)是引用类型的一个实例。创建一个引用类型实例需要用new操作符后跟一个构造函数来创建。构造函数本...
    惶惶不安的青年阅读 543评论 13 2
  • 有些是你无从选择的,有些是你可以选择的。 逃脱桎梏的方法就是让自己强大。 当心慢慢变凉,所有的一切都无法挽回。
    曼曼冰冰阅读 123评论 0 1
  • 昨晚凌晨肚子饿,看来晚上吃一个橙子顶不住! 之前没有理解企业财务记账的深刻作用,通过对每一笔业务往来进行记账,在月...
    奔啵儿霸阅读 619评论 0 51