JS回顾

重新介绍javaScript

javaScript是什么
  • 解析执行:轻量级解释型的
  • 语言特点:动态,头等函数 (First-class Function)
  • 又称函数是 JavaScript 中的一等公民
  • 执行环境:在宿主环境(host environment)下运行,浏览器是最常见的 JavaScript 宿主环境
  • 但是在很多非浏览器环境中也使用 JavaScript ,例如 node.js

MDN-JavaScript

javaScript的组成
  • ECMAScript - 语法规范
  • 变量、数据类型、类型转换、操作符
  • 流程控制语句:判断、循环语句
  • 数组、函数、作用域、预解析
  • 对象、属性、方法、简单类型和复杂类型的区别
  • 内置对象:Math、Date、Array,基本包装类型String、Number、Boolean
  • Web APIs
  • BOM
  • onload页面加载事件,window顶级对象
  • 定时器
  • location、history
  • DOM
  • 获取页面元素,注册事件
  • 属性操作,样式操作
  • 节点属性,节点层级
  • 动态创建元素
  • 事件:注册事件的方式、事件的三个阶段、事件对象
JavaScript 可以做什么

阿特伍德定律:

Any application that can be written in JavaScript, will eventually be written in JavaScript.

任何可以用JavaScript来写的应用,最终都将用JavaScript来写

阿特伍德 stackoverflow的创始人之一

浏览器是如何工作的

User Interface  用户界面,我们所看到的浏览器
Browser engine  浏览器引擎,用来查询和操作渲染引擎
*Rendering engine 用来显示请求的内容,负责解析HTML、CSS,并把解析的内容显示出来
Networking   网络,负责发送网络请求
*JavaScript Interpreter(解析者)   JavaScript解析器,负责执行JavaScript的代码
UI Backend   UI后端,用来绘制类似组合框和弹出窗口
Data Persistence(持久化)  数据持久化,数据存储  cookie、HTML5中的sessionStorage

JavaScript 执行过程

JavaScript 运行分为两个阶段:

  • 预解析
  • 全局预解析(所有变量和函数声明都会提前;同名的函数和变量函数的优先级高)
  • 函数内部预解析(所有的变量、函数和形参都会参与预解析)
  • 函数
  • 形参
  • 普通变量
  • 执行

先预解析全局作用域,然后执行全局作用域中的代码,
在执行全局代码的过程中遇到函数调用就会先进行函数预解析,然后再执行函数内代码。

面向对象编程

面向对象介绍

什么是对象

Everything is object (万物皆对象)

内存的一片区域, 包含数据和代码, 数据成为属性, 代码成为方法.

(1) 对象是单个事物的抽象。

(2) 对象是一个容器,封装了属性(property)和方法(method)。

ECMAScript-262 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数

提示:每个对象都是基于一个引用类型创建的,这些类型可以是系统内置的原生类型,也可以是开发人员自定义的类型。

什么是面向对象

面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维 护性。

面向对象编程 —— Object Oriented Programming,简称 OOP ,是一种编程开发思想。
它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。

在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。
因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目。

面向对象与面向过程的区别

面向过程的编程:面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。

封装:封装就是把处理数据的所有步骤封装到一个函数或其他结构中,方便代码的调用和管理,方便重用。

面向对象的编程: 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

面向对象和面向过程的主要区别就是数据是单独存储还是与操作存储在一起

对面向过程而言,数据是独立的。而在面向对象中,对象本身就提供了存储数据的空间(类的数据成员),这样就是函数的参数传递简单多了,而且提供了数据封装后,数据的访问也变可靠了。

创建对象

简单方式

我们可以直接通过 new Object() 创建:

var person = {
  name: 'Jack',
  age: 18,
  sayName: function () {
    console.log(this.name)
  }
}
工厂函数

我们可以写一个函数,解决代码重复问题:

function createPerson (name, age) {
  return {
    name: name,
    age: age,
    sayName: function () {
      console.log(this.name)
    }
  }
}

然后生成实例对象:

var p1 = createPerson('Jack', 18)
var p2 = createPerson('Mike', 18)

这样封装确实爽多了,通过工厂模式我们解决了创建多个相似对象代码冗余的问题,
但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

静态成员和实例成员
静态成员 : 属于构造函数对象的属性或方法
	构造函数.属性;  // 
实例成员 : 属于实例对象的属性或方法
	对象.属性;  //

构造函数

更优雅的工厂函数:构造函数

一种更优雅的工厂函数就是下面这样,构造函数:

function Person (name, age) {
  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }
}

var p1 = new Person('Jack', 18)
p1.sayName() // => Jack

var p2 = new Person('Mike', 23)
p2.sayName() // => Mike
构造函数的执行

要创建 Person 实例,则必须使用 new操作符。
以这种方式调用构造函数会经历以下 4 个步骤:

  1. 在内存中创建一个新对象
  2. 设置构造函数的this, 让this只想刚刚创建的对象
  3. 执行构造函数中的代码
  4. 返回新对象
下面是具体的伪代码:
function Person (name, age) {
  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }
}
构造函数和实例对象的关系

在每一个实例对象中同时有一个 constructor 属性,该属性指向创建该实例的构造函数:

console.log(p1.constructor === Person) // => true
console.log(p2.constructor === Person) // => true
console.log(p1.constructor === p2.constructor) // => true

对象的 constructor 属性最初是用来标识对象类型的,
如果要检测对象的类型,还是使用 instanceof

console.log(p1 instanceof Person) // => true
console.log(p2 instanceof Person) // => true
构造函数的问题

使用构造函数带来的最大的好处就是创建对象更方便了,但是其本身也存在一个浪费内存的问题:

function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = function () {
    console.log('hello ' + this.name)
  }
}

var p1 = new Person('Tom', 18)
var p2 = new Person('Jack', 16)

在该示例中,从表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。
那就是对于每一个实例对象,typesayHello 都是一模一样的内容,
每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。

console.log(p1.sayHello === p2.sayHello) // => false 两个sayHello是不一样的

对于这种问题我们可以把需要共享的函数定义到构造函数外部

你肯定想到了可以把多个函数放到一个对象中用来避免全局命名空间冲突的问题:

var fns = {
  sayHello: function () {
    console.log('hello ' + this.name)
  },
  sayAge: function () {
    console.log(this.age)
  }
}

function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = fns.sayHello
  this.sayAge = fns.sayAge
}

var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)

console.log(p1.sayHello === p2.sayHello) // => true
console.log(p1.sayAge === p2.sayAge) // => true

至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。
但是代码看起来还是那么的格格不入,那有没有更好的方式呢?

原型对象

prototype
目的 : 

	为了减少代码冗余(占用内存), 可以把共同的代码保存到一个构造函数创建出来的所有对象都可以访问的空间, 这个空间称之为原型对象.

具体的步骤:

	1. js解释器, 在碰到构造函数时, 会自动开辟一块空间, 并给构造函数对象添加一个属性 prototype, 可以吧所有的对象共用的代码存出来这片空间中, 这个空间就称之为原型对象.
	
	2. 在使用new关键字创建对象时, js解释器会给新创建出来的对象默认添加一个隐含的属性__proto__, 这个属性中存储的就是构造函数中原型对象的地址, 通过这个地址, 每个实例对象都可以访问构造函数的原型对象中的属性和方法.
	
	3. 当我们使用实例对象.方法名或属性名访问对象的属性或方法时, 会先在实例对象本身的空间进行查找, 如果找不到, 回到原型对象中进行查找.

原型对象语法

function Person (name, age) {
  this.name = name;
  this.age = age;
}

// 给Person原型对象增加一个属性
Person.prototype.text = '随便写的文本内容';

// 将 Person.prototype 重置到一个新的对象
Person.prototype = {
    // 保持 constructor 的指向正确
  constructor: Person, // 手动将 constructor 指向正确的构造函数
  type: 'human',
  sayHello: function () {
    console.log('我叫' + this.name + ',我今年' + this.age + '岁了')
  }
}

// 在原型链中查找或添加/修改
	1. 查找属性或方法时, 自身有就找到自己的, 自己没有就找原型对象
	2. 添加属性或方法时, 自身有就在自身改变, 自己没有就在自身添加, 不改变原型对象
三角关系


原型链


作用:

		为了代码的重用。把相同的方法,只保存一份!

	A和B对象具有同样的方法,发现某些方法跟C和D对象的方法也具有同样的代码

继承

什么是继承

  • 现实生活中的继承
  • 程序中的继承

构造函数的属性继承:借用构造函数

function Person (name, age) {
  this.type = 'human'
  this.name = name
  this.age = age
}

function Student (name, age) {
  // 借用构造函数继承属性成员
  Person.call(this, name, age)
}

var s1 = Student('张三', 18)
console.log(s1.type, s1.name, s1.age) // => human 张三 18

构造函数的原型方法继承:拷贝继承(for-in)

function Person (name, age) {
  this.type = 'human'
  this.name = name
  this.age = age
}

Person.prototype.sayName = function () {
  console.log('hello ' + this.name)
}

function Student (name, age) {
  Person.call(this, name, age)
}

// 原型对象拷贝继承原型对象成员
for(var key in Person.prototype) {
  Student.prototype[key] = Person.prototype[key]
}

var s1 = Student('张三', 18)

s1.sayName() // => hello 张三

另一种继承方式:原型继承

function Person (name, age) {
  this.type = 'human'
  this.name = name
  this.age = age
}

Person.prototype.sayName = function () {
  console.log('hello ' + this.name)
}

function Student (name, age) {
  Person.call(this, name, age)
}

// 利用原型的特性实现继承
Student.prototype = new Person();
// 保证 constructor 指向正确
Student.prototype.constructor = Student;

var s1 = Student('张三', 18)

console.log(s1.type) // => human

s1.sayName() // => hello 张三

函数进阶

函数的定义方式

  • 函数声明
  • 函数表达式
  • new Function
函数声明
function foo () {

}
函数表达式
var foo = function () {

}
函数声明与函数表达式的区别
  • 函数声明必须有名字
  • 函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
  • 函数表达式类似于变量赋值
  • 函数表达式可以没有名字,例如匿名函数
  • 函数表达式没有变量提升,在执行阶段创建,必须在表达式执行之后才可以调用

下面是一个根据条件定义函数的例子:

if (true) {
  function f () {
    console.log(1)
  }
} else {
  function f () {
    console.log(2)
  }
}

以上代码执行结果在不同浏览器中结果不一致。

不过我们可以使用函数表达式解决上面的问题:

var f

if (true) {
  f = function () {
    console.log(1)
  }
} else {
  f = function () {
    console.log(2)
  }
}

函数的调用方式

  • 普通函数
  • 构造函数
  • 对象方法

函数内 this 指向的不同场景

函数的调用方式决定了 this 指向的不同:

调用方式

非严格模式

备注

普通函数调用

window

严格模式下是 undefined

构造函数调用

实例对象

原型方法中 this 也是实例对象

对象方法调用

该方法所属对象

紧挨着的对象

事件绑定方法

绑定事件对象

定时器函数

window

这就是对函数内部 this 指向的基本整理,写代码写多了自然而然就熟悉了。

函数也是对象

  • 所有函数都是 Function 的实例

call、apply、bind

那了解了函数 this 指向的不同场景之后,我们知道有些情况下我们为了使用某种特定环境的 this 引用,
这时候时候我们就需要采用一些特殊手段来处理了,例如我们经常在定时器外部备份 this 引用,然后在定时器函数内部使用外部 this 的引用。
然而实际上对于这种做法我们的 JavaScript 为我们专门提供了一些函数方法用来帮我们更优雅的处理函数内部 this 指向问题。
这就是接下来我们要学习的 call、apply、bind 三个函数方法。

call

call() 方法调用一个函数, 其具有一个指定的 this 值和分别地提供的参数(参数的列表)。

注意:该方法的作用和 `apply()` 方法类似,只有一个区别,就是 `call()` 方法接受的是若干个参数的列表,而 `apply()` 方法接受的是一个包含多个参数的数组。

语法:

fun.call(thisArg[, arg1[, arg2[, ...]]])

参数:

  • thisArg
  • 在 fun 函数运行时指定的 this 值
  • 如果指定了 null 或者 undefined 则内部 this 指向 window
  • arg1, arg2, ...
  • 指定的参数列表
apply

apply() 方法调用一个函数, 其具有一个指定的 this 值,以及作为一个数组(或类似数组的对象)提供的参数。

注意:该方法的作用和 `call()` 方法类似,只有一个区别,就是 `call()` 方法接受的是若干个参数的列表,而 `apply()` 方法接受的是一个包含多个参数的数组。

语法:

fun.apply(thisArg, [argsArray])

参数:

  • thisArg
  • argsArray

apply()call() 非常相似,不同之处在于提供参数的方式。
apply() 使用参数数组而不是一组参数列表。例如:

fun.apply(this, ['eat', 'bananas'])
bind

bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。
当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

语法:

fun.bind(thisArg[, arg1[, arg2[, ...]]])

参数:

  • thisArg
  • 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
  • arg1, arg2, …
  • 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

返回值:

返回由指定的this值和初始化参数改造的原函数拷贝。

示例1:

this.x = 9; 
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 返回 81

var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域

// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81

示例2:

function LateBloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
  window.setTimeout(this.declare.bind(this), 1000);
};

LateBloomer.prototype.declare = function() {
  console.log('I am a beautiful flower with ' +
    this.petalCount + ' petals!');
};

var flower = new LateBloomer();
flower.bloom();  // 一秒钟后, 调用'declare'方法
小结
  • call 和 apply 特性一样
  • 都是用来调用函数,而且是立即调用
  • 但是可以在调用函数的同时,通过第一个参数指定函数内部 this 的指向
  • call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
  • apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
  • 如果第一个参数指定了 null 或者 undefined 则内部 this 指向 window
  • bind
  • 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
  • 它和 call、apply 最大的区别是:bind 不会调用
  • bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
  1. 在 bind 的同时,以参数列表的形式进行传递
  2. 在调用的时候,以参数列表的形式进行传递
  • 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
  • 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部

函数的其它成员

  • arguments
  • 实参集合
  • caller
  • 函数的调用者
  • length
  • 形参的个数
  • name
  • 函数的名称
function fn(x, y, z) {
  console.log(fn.length) // => 形参的个数
  console.log(arguments) // 伪数组实参参数集合
  console.log(arguments.callee === fn) // 函数本身
  console.log(fn.caller) // 函数的调用者
  console.log(fn.name) // => 函数的名字
}

function f() {
  fn(10, 20, 30)
}

f()

高阶函数

  • 函数可以作为参数
  • 函数可以作为返回值
作为参数
function eat (callback) {
  setTimeout(function () {
    console.log('吃完了')
    callback()
  }, 1000)
}

eat(function () {
  console.log('去唱歌')
})
作为返回值
function genFun (type) {
  return function (obj) {
    return Object.prototype.toString.call(obj) === type
  }
}

var isArray = genFun('[object Array]')
var isObject = genFun('[object Object]')

console.log(isArray([])) // => true
console.log(isArray({})) // => true

函数闭包

function fn () {
  var count = 0
  return {
    getCount: function () {
      console.log(count)
    },
    setCount: function () {
      count++
    }
  }
}

var fns = fn()

fns.getCount() // => 0
fns.setCount()
fns.getCount() // => 1
作用域、作用域链、预解析
  • 全局作用域
  • 函数作用域
  • 没有块级作用域
{
  var foo = 'bar'
}

console.log(foo)

if (true) {
  var a = 123
}
console.log(a)

作用域链示例代码:

var a = 10

function fn () {
  var b = 20

  function fn1 () {
    var c = 30
    console.log(a + b + c)
  }

  function fn2 () {
    var d = 40
    console.log(c + d)
  }

  fn1()
  fn2()
}
  • 内层作用域可以访问外层作用域,反之不行
什么是闭包
  • 函数嵌套函数
  • 函数内部使用外部的变量
  • 解决了命名污染
  • 内存泄漏
  • 解决内存泄漏? 手动赋值变量为空

闭包就是能够读取其他函数内部变量的函数,

由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,
因此可以把闭包简单理解成 “定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的用途:

  • 可以在函数外部读取函数内部成员
  • 让函数内成员始终存活在内存中
一些关于闭包的例子

示例1:

var arr = [10, 20, 30]
for(var i = 0; i < arr.length; i++) {
  arr[i] = function () {
    console.log(i)
  }
}

示例2:

console.log(111)

for(var i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i)
  }, 0)
}
console.log(222)

示例3:投票

示例4:判断类型

示例5:沙箱模式

闭包的思考题

思考题 1:

var name = "The Window";
var object = {
  name: "My Object",
  getNameFunc: function () {
    return function () {
      return this.name;
    };
  }
};

console.log(object.getNameFunc()())

思考题 2:

var name = "The Window";  
var object = {    
  name: "My Object",
  getNameFunc: function () {
    var that = this;
    return function () {
      return that.name;
    };
  }
};
console.log(object.getNameFunc()())
小结

函数递归

递归执行模型
function fn1 () {
  console.log(111)
  fn2()
  console.log('fn1')
}

function fn2 () {
  console.log(222)
  fn3()
  console.log('fn2')
}

function fn3 () {
  console.log(333)
  fn4()
  console.log('fn3')
}

function fn4 () {
  console.log(444)
  console.log('fn4')
}

fn1()
举个栗子:计算阶乘的递归函数
function factorial (num) {
  if (num <= 1) {
    return 1
  } else {
    return num * factorial(num - 1)
  }
}
递归应用场景
  • 深拷贝
  • 菜单树
  • 遍历 DOM 树

正则表达式

  • 了解正则表达式基本语法
  • 能够使用JavaScript的正则对象

正则表达式简介

什么是正则表达式

正则表达式:用于匹配规律规则的表达式,正则表达式最初是科学家对人类神经系统的工作原理的早期研究,现在在编程语言中有广泛的应用。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

正则表达式的作用
  1. 给定的字符串是否符合正则表达式的过滤逻辑(匹配)
  2. 可以通过正则表达式,从字符串中获取我们想要的特定部分(提取)
  3. 强大的字符串替换能力(替换)
正则表达式的特点
  1. 灵活性、逻辑性和功能性非常的强
  2. 可以迅速地用极简单的方式达到字符串的复杂控制
  3. 对于刚接触的人来说,比较晦涩难懂

正则表达式的测试

  • sublime/vscode/word
  • 演示替换所有的数字

正则表达式的组成

  • 普通字符
  • 特殊字符(元字符):正则表达式中有特殊意义的字符

示例演示:

  • \d 匹配数字
  • ab\d 匹配 ab1、ab2

元字符串

通过测试工具演示下面元字符的使用

常用元字符串

元字符

说明

\d

匹配数字

\D

匹配任意非数字的字符

\w

匹配字母或数字或下划线

\W

匹配任意不是字母,数字,下划线

\s

匹配任意的空白符

\S

匹配任意不是空白符的字符

.

匹配除换行符以外的任意单个字符

^

表示匹配行首的文本(以谁开始)

$

表示匹配行尾的文本(以谁结束)

限定符

限定符

说明

*

重复零次或更多次

+

重复一次或更多次

?

重复零次或一次

{n}

重复n次

{n,}

重复n次或更多次

{n,m}

重复n到m次

其它
[] 字符串用中括号括起来,表示匹配其中的任一字符,相当于或的意思
[^]  匹配除中括号以内的内容
\ 转义符
| 或者,选择两者中的一个。注意|将左右两边分为两部分,而不管左右两边有多长多乱
() 从两个直接量中选择一个,分组
   eg:gr(a|e)y匹配gray和grey
[\u4e00-\u9fa5]  匹配汉字

案例

验证手机号:

^\d{11}$

验证邮编:

^\d{6}$

验证日期 2012-5-01

^\d{4}-\d{1,2}-\d{1,2}$

验证邮箱 xxx@itcast.cn:

^\w+@\w+\.\w+$

验证IP地址 192.168.1.10

^\d{1,3}\(.\d{1,3}){3}$

JavaScript 中使用正则表达式

创建正则对象

方式1:

var reg = new Regex('\d', 'i');
var reg = new Regex('\d', 'gi');

方式2:

var reg = /\d/i;
var reg = /\d/gi;
参数

标志

说明

i

忽略大小写

g

全局匹配

gi

全局匹配+忽略大小写

正则匹配

// 匹配日期
var dateStr = '2015-10-10';
var reg = /^\d{4}-\d{1,2}-\d{1,2}$/
console.log(reg.test(dateStr));

正则提取

// 1. 提取工资
var str = "张三:1000,李四:5000,王五:8000。";
var array = str.match(/\d+/g);
console.log(array);

// 2. 提取email地址
var str = "123123@xx.com,fangfang@valuedopinions.cn 286669312@qq.com 2、emailenglish@emailenglish.englishtown.com 286669312@qq.com...";
var array = str.match(/\w+@\w+\.\w+(\.\w+)?/g);
console.log(array);

// 3. 分组提取  
// 3. 提取日期中的年部分  2015-5-10
var dateStr = '2016-1-5';
// 正则表达式中的()作为分组来使用,获取分组匹配到的结果用Regex.$1 $2 $3....来获取
var reg = /(\d{4})-\d{1,2}-\d{1,2}/;
if (reg.test(dateStr)) {
  console.log(RegExp.$1);
}

// 4. 提取邮件中的每一部分
var reg = /(\w+)@(\w+)\.(\w+)(\.\w+)?/;
var str = "123123@xx.com";
if (reg.test(str)) {
  console.log(RegExp.$1);
  console.log(RegExp.$2);
  console.log(RegExp.$3);
}

正则替换

// 1. 替换所有空白
var str = "   123AD  asadf   asadfasf  adf ";
str = str.replace(/\s/g,"xx");
console.log(str);

// 2. 替换所有,|,
var str = "abc,efg,123,abc,123,a";
str = str.replace(/,|,/g, ".");
console.log(str);

案例:表单验证

QQ号:<input type="text" id="txtQQ"><span></span><br>
邮箱:<input type="text" id="txtEMail"><span></span><br>
手机:<input type="text" id="txtPhone"><span></span><br>
生日:<input type="text" id="txtBirthday"><span></span><br>
姓名:<input type="text" id="txtName"><span></span><br>
//获取文本框
var txtQQ = document.getElementById("txtQQ");
var txtEMail = document.getElementById("txtEMail");
var txtPhone = document.getElementById("txtPhone");
var txtBirthday = document.getElementById("txtBirthday");
var txtName = document.getElementById("txtName");

//
txtQQ.onblur = function () {
  //获取当前文本框对应的span
  var span = this.nextElementSibling;
  var reg = /^\d{5,12}$/;
  //判断验证是否成功
  if(!reg.test(this.value) ){
    //验证不成功
    span.innerText = "请输入正确的QQ号";
    span.style.color = "red";
  }else{
    //验证成功
    span.innerText = "";
    span.style.color = "";
  }
};

//txtEMail
txtEMail.onblur = function () {
  //获取当前文本框对应的span
  var span = this.nextElementSibling;
  var reg = /^\w+@\w+\.\w+(\.\w+)?$/;
  //判断验证是否成功
  if(!reg.test(this.value) ){
    //验证不成功
    span.innerText = "请输入正确的EMail地址";
    span.style.color = "red";
  }else{
    //验证成功
    span.innerText = "";
    span.style.color = "";
  }
};

表单验证部分,封装成函数:

var regBirthday = /^\d{4}-\d{1,2}-\d{1,2}$/;
addCheck(txtBirthday, regBirthday, "请输入正确的出生日期");
//给文本框添加验证
function addCheck(element, reg, tip) {
  element.onblur = function () {
    //获取当前文本框对应的span
    var span = this.nextElementSibling;
    //判断验证是否成功
    if(!reg.test(this.value) ){
      //验证不成功
      span.innerText = tip;
      span.style.color = "red";
    }else{
      //验证成功
      span.innerText = "";
      span.style.color = "";
    }
  };
}

通过给元素增加自定义验证属性对表单进行验证:

<form id="frm">
  QQ号:<input type="text" name="txtQQ" data-rule="qq"><span></span><br>
  邮箱:<input type="text" name="txtEMail" data-rule="email"><span></span><br>
  手机:<input type="text" name="txtPhone" data-rule="phone"><span></span><br>
  生日:<input type="text" name="txtBirthday" data-rule="date"><span></span><br>
  姓名:<input type="text" name="txtName" data-rule="cn"><span></span><br>
</form>
// 所有的验证规则
var rules = [
  {
    name: 'qq',
    reg: /^\d{5,12}$/,
    tip: "请输入正确的QQ"
  },
  {
    name: 'email',
    reg: /^\w+@\w+\.\w+(\.\w+)?$/,
    tip: "请输入正确的邮箱地址"
  },
  {
    name: 'phone',
    reg: /^\d{11}$/,
    tip: "请输入正确的手机号码"
  },
  {
    name: 'date',
    reg: /^\d{4}-\d{1,2}-\d{1,2}$/,
    tip: "请输入正确的出生日期"
  },
  {
    name: 'cn',
    reg: /^[\u4e00-\u9fa5]{2,4}$/,
    tip: "请输入正确的姓名"
  }];

addCheck('frm');


//给文本框添加验证
function addCheck(formId) {
  var i = 0,
      len = 0,
      frm =document.getElementById(formId);
  len = frm.children.length;
  for (; i < len; i++) {
    var element = frm.children[i];
    // 表单元素中有name属性的元素添加验证
    if (element.name) {
      element.onblur = function () {
        // 使用dataset获取data-自定义属性的值
        var ruleName = this.dataset.rule;
        var rule =getRuleByRuleName(rules, ruleName);

        var span = this.nextElementSibling;
        //判断验证是否成功
        if(!rule.reg.test(this.value) ){
          //验证不成功
          span.innerText = rule.tip;
          span.style.color = "red";
        }else{
          //验证成功
          span.innerText = "";
          span.style.color = "";
        }
      }
    }
  }
}

// 根据规则的名称获取规则对象
function getRuleByRuleName(rules, ruleName) {
  var i = 0,
      len = rules.length;
  var rule = null;
  for (; i < len; i++) {
    if (rules[i].name == ruleName) {
      rule = rules[i];
      break;
    }
  }
  return rule;
}

附录

A 代码规范

代码风格
校验工具

B Chrome 开发者工具

C 文档相关工具

  • 电子文档制作工具: docute
  • 流程图工具:[DiagramDesigner](