看完必会的正则表达式和递归

1,递归

递归函数:一个函数在内部可以调用其本身

function func() {
  func()
}
func()

image

递归容易发生栈溢出错误(stack overflow),以上是一个典型的栈溢出,所以我们必须加退出条件 return

var num = 1
function func() {
  console.log(`打印${num}次`)
  if (num === 10) {
    return
  }
  num++
  func()
}
func()

递归实例:

  • 1, 利用递归求 1 ~ n 的阶乘
function func(n) {
  // 1 * 2 * 3 * ...n
  if (n === 1) return 1;
  return n * func(n - 1)
}
func(5)  // 120
  • 2, // 斐波那契数列(兔子序列):前两项的和等于第三项的值 表现: 1、1、2、3、5、8、13、21、34...
// 用户输入一个数字 n, 求出这个数字对应的兔子序列值
function fb (n) {
  if (n === 1 || n === 2) return 1
  return fb(n - 1) + fb(n - 2)
}
fb(9)  // 34
  • 3, 根据 id 返回对应的数据对象
const arr = [
  {
    id: 1,
    name: 'Jerry',
    friends: [
      {
        id: 11,
        name: 'Lise'
      },
      {
        id: 12,
        name: 'Tom'
      }
    ]
  },
  {
    id: 2,
    name: 'xiaowang'
  }
];

const arr = [
  {
    id: 1,
    name: 'Jerry',
    friends: [
      {
        id: 11,
        name: 'Lise'
      },
      {
        id: 12,
        name: 'Tom'
      }
    ]
  },
  {
    id: 2,
    name: 'xiaowang'
  }
];
function getId(data, id) {
  var o = {}
  data.forEach(function (value) {
    if (value.id === id) {
      o = value
      return value
    } else if (value.friends?.length > 0) {
      o = getId(value.friends, id)
    }
  })
  return o
}
console.log(getId(arr, 12)) // {id: 12, name: 'Tom'}
或者
function getId(arr, id) {
  let queue = [];
  arr.forEach(item => {
    queue.push(item)
  })
  while(queue.length > 0) {
    const value = queue.shift();
    if(value.id === id) return value;
    if(value.friends && value.friends.length > 0) {
      value.friends.forEach(item => queue.push(item));
    }
  }
}
console.log(getId(arr, 12)) // {id: 12, name: 'Tom'}

2,深浅拷贝

  • 1,浅拷贝只是拷贝一层,更深层对象级别的只拷贝引用
  • 2,深拷贝拷贝多层,每一级别的数据都会拷贝

浅拷贝

const obj = {
  name: 'Jerry',
  age: 18,
  friends: {
    name: 'Lise'
  }
};
var o = {}

for (var i in obj) { // i 属性名,obj[i] 属性值
  o[i] = obj[i]
}
或
Object.assign(o, obj)  // ES6新增的方法可以实现浅拷贝

o.friends.name = 'Tom'
console.log(o) 
console.log(obj)
image

image

深拷贝

const obj = {
  name: 'Jerry',
  age: 18,
  friends: {
    name: 'Lise'
  },
  colors: ['red', 'green', 'blue']
};
var o = {}
// 封装函数
function deepCopy(newObj, oldObj) {
  for(var i in oldObj) {
    // 判断属性值是简单数据类型还是复杂数据类型
      // 1,获取属性值 oldObj[i]
      const item = oldObj[i]
      // 2, 判断这个值是否是数组
      if (item instanceof Array) {
        newObj[i] = []
        deepCopy(newObj[i], item)
      } else if (item instanceof Object) {
        // 3,判断这个值是否是对象
        newObj[i] = {}
        deepCopy(newObj[i], item)
      } else {
        // 4,属于基本数据类型
        newObj[i] = item
      }
  }
}
deepCopy(o, obj)
image

3,正则表达式

正则表达式:用于匹配字符串中字符组合的模式,在JS中,正则表达式也是对象。

通常用于检测、替换那些符合某个模式的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线。

还常用于过滤页面内容中的一些敏感词(替换),或者从字符串中获取我们想要的特定部分等

3.1,创建正则表达式

  • 创建正则表达式:
    • 1, 利用 RegExp 对象来创建
    const rg = new RegExp(/表达式/)
    
    • 2, 利用字面量创建正则表达式
    const rg = /表达式/
    
  • 测试正则表达式 test
    test()正则对象的方法,用于检测字符串是否符合该规范,该对象会返回 truefalse
const rg =  /123/
rg.test(123) // true
rg.test(456) // false
  • 正则的组成
    一个正则可以由简单的字符组成,比如/abc/,也可以是简单和特殊字符的组合,比如/ab*c/,特殊字符有叫元字符,在正则中具有特殊意义的专用符号,比如: ^、$、+等

特殊符号可以参考:MDN正则表达式

正则测试工具:https://tool.oschina.net/regex

3.2,正则中的特殊字符

  • 3.2.1 边界符
    正则中的边界符(位置符)用来提示字符串所处的位置,主要有两个:^、$
    • ^ 匹配行首的文本
    • $ 匹配行尾的文本
const rg =  /abc/ // /abc/表示只要包含有abc这个字符串返回的都是true
console.log(rg.test('abc')) // true 
console.log(rg.test('abcd')) // true
console.log(rg.test('aabc')) // true

const reg = /^abc/ // 表示必须以abc开头的
console.log(reg.test('abc')) // true 
console.log(reg.test('abcd')) // true
console.log(reg.test('aabc')) // false
console.log(reg.test('abdc')) // false

const reg1 = /^abc$/ // 精确匹配
console.log(reg1.test('abc')) // true 
console.log(reg1.test('abcd')) // false
console.log(reg1.test('aabc')) // false
  • 3.2.2 字符类:[]
    表示有一系列字符可供选择,只要匹配其中一个就可以了
const reg = /[abc]/  // 表示只要包含有a 或 有b 或 有c,都返回true
console.log(reg.test('alia')) // true

const reg1 = /^[abc]$/  // 表示只有是a 或 b 或 c,才返回true
console.log(reg1.test('alia')) // false
console.log(reg1.test('a')) // true
console.log(reg1.test('b')) // true
console.log(reg1.test('bc')) // false
console.log(reg1.test('c')) // true
console.log(reg1.test('cc')) // false
console.log(reg1.test('abc')) // false
  • 3.3.3 [-]方括号内 范围符 -
const reg = /^[a-z]$/ // 26个英文字母任何一个字母返回true
console.log(reg.test('alia')) // false
console.log(reg.test('a')) // true
console.log(reg.test('A')) // false
  • 3.3.4 字符组合
// 字符组合
const reg1 = /^[a-zA-Z]$/ // // 26个英文字母(大小写都可以) 任何 一个字母返回true
console.log(reg1.test('alia')) // false
console.log(reg1.test('a')) // true
console.log(reg1.test('A')) // true

// 比如:reg = /^[a-zA-Z0-9_-]$/
  • 3.4.5 []方括号内 表示取反
const reg = /^[^abc]$/
console.log(reg.test('a'))  // false
  • 3.4.6 量词符

用来设定某个模式出现的次数

量词 说明
* 重复0次或更多次
+ 重复1次或更多次
? 重复0次或1次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
// *  >= 0
const reg = /^a*$/
console.log(reg.test('')) // true
console.log(reg.test('a')) // true
console.log(reg.test('b')) // false
console.log(reg.test('aaa')) // true

// + >= 1
const reg1 = /^a+$/
console.log(reg1.test('')) // false
console.log(reg1.test('a')) // true
console.log(reg1.test('b')) // false
console.log(reg1.test('aaa')) // true

// ? 1 || 0
const reg2 = /^a?$/
console.log(reg2.test('')) // true
console.log(reg2.test('a')) // true
console.log(reg2.test('b')) // false
console.log(reg2.test('aaa')) // false

// {3} 重复3次
const reg3 = /^a{3}$/
console.log(reg3.test('')) // false
console.log(reg3.test('a')) // false
console.log(reg3.test('b')) // false
console.log(reg3.test('aaa')) // true

// {3,} >= 3
const reg4 = /^a{3,}$/
console.log(reg4.test('')) // false
console.log(reg4.test('a')) // false
console.log(reg4.test('b')) // false
console.log(reg4.test('aaa')) // true
console.log(reg4.test('aaaaa')) // true
console.log(reg4.test('aaabc')) // false
console.log('-----------')
// {3,8}  >= 3 && <= 8
const reg5 = /^a{3,8}$/
console.log(reg5.test('')) // false
console.log(reg5.test('a')) // false
console.log(reg5.test('b')) // false
console.log(reg5.test('aaa')) // true
console.log(reg5.test('aaaaa')) // true
console.log(reg5.test('aaabc')) // false
console.log(reg5.test('aaaaaaaa')) // true
  • 3.4.7
// 量词是设定某个模式出现的次数
var reg = /^[a-zA-Z0-9_-]$/ // 这个模式用户只能输入英文字母 数字 下划线 短横线但是有边界符和[] 这就限定了只能多选1
console.log(reg.test('a')) // true
console.log(reg.test('ab')) // false
console.log(reg.test(1)) // true
console.log(reg.test('11')) // false
console.log(reg.test('aa')) // false

4, 正则案例

1, 用户名验证,如果用户名输入合法,提示信息为:用户名合法,颜色改为绿色,如果不合法,则后面提示:用户名不符合规范,改色改为红色

const name = document.querySelector('.name');
const span = document.querySelector('span');
var reg = /^[a-zA-Z0-9_-]{6,16}$/
name.onblur = function () {
  if (reg.test(this.value)) {
    span.className = 'green'
    span.innerHTML = '用户名合法'
  } else {
    span.className = 'red'
    span.innerHTML = '用户名不符合规范'
  }
}

5, 括号总结

  • 1,量词符大括号,表示重复次数
  • 2,字符集合中括号,匹配方括号中的任意字符
  • 3,小括号,表示优先级
// 中括号[],字符集合,匹配方括号中的任意字符
const reg = /^[abc]$/ // a || b || c都可以

// 大括号{},量词符,表示重复次数
const reg1 = /^[abc]{3}$/
console.log(reg1.test('aaa')) // true
console.log(reg1.test('abcabcabc')) // false

const reg2 = /^abc{3}$/  // 它只是让c重复3次 abccc
console.log(reg2.test('aaa')) // false
console.log(reg2.test('abcabcabc')) // false
console.log(reg2.test('bbb')) // false
console.log(reg2.test('ccc')) // false
console.log(reg2.test('abccc')) // true
console.log(reg2.test('aabccc')) // false

// 小括号 表示优先级
const reg3 = /^(abc){3}$/ // 表示让 abc重复3次 
console.log(reg3.test('aaa')) // false
console.log(reg3.test('abcabcabc')) // true
console.log(reg3.test('abccc')) // false

6,可以在线测试自己写的正则对不对(https://c.runoob.com/front-end/854/)

也会有一些常用的正则:


image

7,预定义类

指的是某些常见模式的简写方式

预定义类 说明
\d 匹配0-9之间的任一数字, 相当于[0-9]
\D 匹配所有0-9以外的字符, 相当于[^0-9]
\w 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]
\W 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_]
\s 匹配空格(包括换行符、制表符、空格符),相当于[\t\r\n\v\f]
\S 匹配非空格的字符,相当于[^\t\r\n\v\f]

实例:

//  座机号码验证:全国座机号码,两种格式:010-12345678 或者 0530-1234567
// const reg = /^\d{3}-\d{8}|\d{4}-\d{7}$/  // 正则里的 或 用 | 表示
// 简写
const reg = /^\d{3,4}-\d{7,8}$/
console.log(reg.test('魑魅魍魉')) // false
console.log(reg.test('3829889')) // false
console.log(reg.test('3829-889')) // false
console.log(reg.test('022-31231321')) // true

8, replace 替换

replace()方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则

// 语法
str.replace(/str/, replacement)
// 第一个参数:被替换的字符串 或者 正则
// 第二个参数:替换为的字符串
// 返回值是一个替换完的新字符串
const str = 'JerryLise'
const newStr = str.replace('Jerry', 'xiaowang')
const newStr = str.replace(/Jerry/, 'xiaowang')
console.log(newStr)  // xiaowangLise

实例:

const text = document.querySelector('textarea')
const btn = document.querySelector('button')
const div = document.querySelector('div')
btn.onclick = function () {
    div.innerHTML = text.value.replace(/小猫咪/, '**')
}
image

以上代码replace只能替换第一个满足条件的字符串,后面的就不再替换了,那么我们可以用正则表达式参数

9,正则表达式参数

/表达式/[switch]
// switch也称修饰符,按照什么样的模式来匹配,有三种值:
// 1,g: 全局匹配
// 2,i:忽略大小写
// 3,gi:全局匹配 + 忽略大小写

以上实例:

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

推荐阅读更多精彩内容