typeof
在ES5中,JavaScript共有6种数据类型,其中前5种为基本数据类型,Object为引用数据类型:
String Number Boolean Null Undefined Object
需要注意的是, NaN的typeof值是number; Null的typeof值是object(注意结果的大小写):
//typeof 运行后对应的结果:
string number boolean object undefined object
数据类型转换
其他类型转String
注意:null和undefined调用toString方法会报错,但是调用String()方法则直接转换为相应的字面量字符串:
var a = null;
a.toString();//error:Cannot read property 'toString' of null
var b = undefined;
b.toString(); //error: Cannot read property 'toString' of undefined
//如果是undefined类型调用String()方法:
var result = undefined
result = String(result)
console.log(result);// undefined (这个undefined是字符串类型的)
console.log(typeof result);//string
result += 'abc'
console.log(result); //undefinedabc
//如果是undefined类型调用String()方法:
var result = null
result = String(result)
console.log(result);// null (这个null是字符串类型的)
console.log(typeof result);//string
result += 'abc'
console.log(result); //nullabc
对于Boolean和Number类型的,调用toString()和String()的结果都是一样的,true会转为“true”,123转为“123”。
其他类型转Number
通过Number()函数转换为数字的,要么转化为数字,要么转化为NaN。需要注意的是:undefined会转化为NaN,而null则转化为0。并且,如果是空字符串或者全是空格的字符串,转化结果也都是0。针对布尔值,如果为true,则转化为1,false则转化为0.如果是Object类型的,则转化为NaN。
var a = '123'
a = Number(a)
console.log(a); //123
var b = 'abc'
b = Number(b)
console.log(b);//NaN
var c = undefined
c = Number(c)
console.log(c);//NaN
var d = null
d = Number(d)
console.log(d);//0
var e = ''
e = Number(e)
console.log(e);//0
var f = ' '
f = Number(f)
console.log(f);//0
var g = true
g = Number(g)
console.log(g);//1
var h = false
h = Number(h)
console.log(h);//0
var i = {}
i = Number(i)
console.log(i);//NaN
通过parseInt来转为数字的,如果是以数字开头的字符串,那么就可以转化为数字,否则便会转化为NaN,在parseInt中,’123.456px’会转化为123,而在parseFloat中,’123.456px’则会转化为123.456;如果是非字符串如Boolean类型的true,则会先转化为字符串的”true”,再通过parseInt来转,由于不是已数字开头的字符串,自然会转为NaN:
var a = '123'
a = parseInt(a)
console.log(a); //123
var a2 = '123px'
a2 = parseInt(a2)
console.log(a2); //123
var a3 = 'age:123'
a3 = parseInt(a3)
console.log(a3); //NaN
var a4 = 'abc123px456'
a4 = parseInt(a4)
console.log(a4); //NaN
var a5 = '123.456px'
a5 = parseInt(a5)
console.log(a5); //123
var a6 = '123.456px'
a6 = parseFloat(a6)
console.log(a6); //123.456
var a7 = '123px456'
a7 = parseInt(a7)
console.log(a7); //123
var b = 'abc'
b = parseInt(b)
console.log(b);//NaN
var c = undefined
c = parseInt(c)
console.log(c);//NaN
var d = null
d = parseInt(d)
console.log(d);//NaN
var e = ''
e = parseInt(e)
console.log(e);//NaN
var f = ' '
f = parseInt(f)
console.log(f);//NaN
var g = true
g = parseInt(g)
console.log(g);//NaN
var h = false
h = parseInt(h)
console.log(h);//NaN
var i = {}
i = parseInt(i)
console.log(i);//NaN
其他类型转Boolean
var a = '123'
a = Boolean(a)
console.log(a); //true
var a2 = 123
a2 = Boolean(a2)
console.log(a2); //true
var a3 = -123
a3 = Boolean(a3)
console.log(a3); //true
var a4 = []
a4 = Boolean(a4)
console.log(a4); //true
var a5 = ''
a5 = Boolean(a5)
console.log(a5); //false
var a6 = Infinity
a6 = Boolean(a6)
console.log(a6); //true
var a7 = ' '
a7 = Boolean(a7)
console.log(a7); //true
var b = {}
b = Boolean(b)
console.log(b);//true
var c = undefined
c = Boolean(c)
console.log(c);//false
var d = null
d = Boolean(d)
console.log(d);//false
var e = 0
e = Boolean(e)
console.log(e);//false
var f = NaN
f = Boolean(f)
console.log(f);//false
由上面的结果可以知道:空数组和空对象转化为布尔值的结果为true,而空字符串、null、undefined、NaN、0转化的结果为false。
运算符
当对非Number类型的值进行运算时,会先转换为Number类型再进行运算,加法运算符比较特殊,除了做加法操作外,还能做拼接字符串的操作(任何值和字符串想加,都需要先将其转化为字符串再进行拼接)。所以,数字型字符串在不同运算符的表现是不一定相同的:
var a = true + 1
console.log(a); //2
var a2 = false + 1
console.log(a2); //1
var a3 = 10 + null
console.log(a3); //10
var a4 = ['123','456'] + 10
console.log(a4); //123,45610
console.log('-----',typeof a4); //string
var A4 = ['123','456'] - 10
console.log(A4); //NaN
console.log('-----',typeof A4); //number
var AA4 = [] - 10
console.log(AA4); //-10
console.log('-----',typeof AA4); //number
var a5 = '' + 10
console.log(a5); //10
console.log(typeof a5); //string
var A5 = '' - 10
console.log(A5); //-10
console.log(typeof A5); //number
var AA5 = '110' - 10
console.log(AA5); //100
console.log('+++++',typeof AA5); //number
var a6 = Infinity + 1
console.log(a6); //Infinity
var a7 = ' ' + 1
console.log(a7); //' 1'
console.log(typeof a7); //string
var A7 = ' ' - 1
console.log(A7); //-1
console.log('____',typeof A7); //number
var AA7 = 'abc' - 1
console.log(AA7); //NaN
console.log('__+++__',typeof AA7); //number
var b = {} + 1
console.log(b);//[object Object]1
var B = {} - 1
console.log(B);//NaN
var c = undefined + 1
console.log(c);//NaN
var d = NaN + 1
console.log(d);//NaN
var e = '123' + 123
console.log(e);//123123
var f = '123' + '123'
console.log(f);//123123
相等运算符
console.log('true' == 1);//false
console.log(true == 1);//true
console.log('1' == 1);//true
console.log(null == 0);//false
console.log(undefined == 0);//false
console.log(undefined == null);//true
console.log(NaN == NaN);//false
注意NaN不和任何数据相等,包括和自己相比较。
基本数据类型与引用数据类型的区别
String/ Number/ Boolean/Undefined/Null是基本数据类型,Object是引用数据类型。
var a = 123;
var b = a;
a++;
console.log(a);//124
console.log(b);//123
var c = 110;
var d = c;
d++;
console.log(c);//110
console.log(d);//111
//*******************虽然很基础,但是很重要************************
var user = {};
user.age = 18;
user.name = "LEG";
var myAge = user.age;
var myName = user.name;
console.log(myAge);//18
console.log(myName);//LEG
//更改值后不影响user对象。
myAge = 456;
myName = "LXX";
console.log(user.name);//LEG
console.log(user.age);//18
//更改user对象值后也不影响myAge myName。
user.name = "DLL";
user.age = 666;
console.log(myName);//LXX
console.log(myAge);//456
//*******************虽然很基础,但是很重要************************
var user = {};
user.age = 18;
user.name = "LEG";
//引用数据类型被引用
var myUser = user;
console.log(myUser.name);//LEG
console.log(myUser.age);//18
//更改user对象值后会影响myUser。
user.name = "DLL";
user.age = 666;
console.log(myUser.name);//DLL
console.log(myUser.age);//666
//更改myUser值后也会影响user。
myUser.name = "LXX";
myUser.age = 333;
console.log(user.name);//LXX
console.log(user.age);//333
//*******************虽然很基础,但是很重要************************
var user = {
name: 'lxx',
age: 18,
edu: ['A', 'B', 'C'],
female: true,
other: {
address: 'henan',
count: 4,
marry: false,
family: ['father', 'mather','sister']
}
};
console.log(user);
//将user的属性值为基本数据类型的取出后更改,不影响user
var name = user.name;
var age = user.age;
var edu = user.edu;
var female = user.female;
name = 'DLL';
age = 19;
edu = ['E', 'F', 'G'];
female = false;
console.log(user);
//但是, 如果取出的值不是基本数据类型,列如user.other,是一个Object,那么更改值后会影响user
var other = user.other
other.address = 'hebei';
other.count = 9;
other.marry = true;
other.family = ['G', 'GM'];
//新增一个属性
other.data = 'shzyh';
console.log(user);
//此时我直接拿一个新的值来替换other,是不影响上面的对user.other的设置的,除非再执行一步user.other = other
other = {
test: '999'
}
// user.other = other
console.log(user);
基本数据类型之间的赋值,例如a=b,仅仅是将b的值赋值给了a,a和b的值仅仅是相同,一方更改值不会影响另一方。对象的属性值如果是基本数据类型,例如 a=user.name,那么赋值给a后,a和user.name也仅仅是值相同,一方更改值后也不会影响另一方。但是如果是other = user.other,通过更改other的值是会影响user.other的值的。虽然很基础,但是很重要。
Function
声明函数的几种方式:
//使用构造函数来创建函数
var test = new Function("console.log('LEG___');");
test();
console.log(typeof test); //function
//使用函数声明来创建函数。注意:因为函数也是对象,所以可以给test2添加属性。
function test2() {
console.log('LEG+++');
}
test2.age = 18;
console.log(typeof test2); //function
console.log(test2.age); //function
//使用函数表达式来创建函数
var test3 = function () {
console.log('LEG;;;;;;;;');
}
test3();
立即执行函数:
(function (name) {
console.log(`我是${name}`);
})('LEG');
函数在对象中使用:
var user = {
name: 'LEG',
age: 18,
print: function (content) {
console.log(content)
}
}
user.print('77777');
在函数中,不使用var声明的变量都会成为全局变量:
var name = 'LEG';
function test() {
//未使用var声明变量,age是全局变量
age = 123;
//更改全局变量name的值
name = 'DLL';
}
test();
console.log(name);//DLL
console.log(age);//123
//name和age都没有使用var声明,他们都是全局变量
name = 'LEG';
function test() {
name = 'DLL';
}
console.log(name);//LEG
test();
console.log(name);//DLL
age = 666;
console.log(age);//666
在函数中,通过形参传递的值是局部变量:
var name = 'LEG';
function test(name) {
//name通过形参传递过来的,相当于在函数中声明了局部变量 var name = 'DLL'
//如果迷糊name的传值,那么把形参换成abc是不是立即就秒懂了???
name = 'DLL';
}
test(name);
console.log(name);//LEG
构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.print = function () {
console.log(this.name);
console.log(this.age);
}
}
var per = new Person('DLL', 29)
per.print()
console.log(per instanceof Person);
上面这种构造函数中的print函数是写在构造函数中的,如果创建多个person,那么print的函数就会创建多次。但是如果通过以下的方式,将print函数给抽出来,又会造成以下问题:1.抽取出来的函数会污染全局作用域的命名空间;2.定义在全局作用域中也会很不安全,会被不小心给覆盖掉。
function Person(name, age) {
this.name = name;
this.age = age;
this.print = myPrint;
}
function myPrint() {
console.log(this.name);
console.log(this.age);
}
var per = new Person('DLL', 29)
per.print()
console.log(per instanceof Person);
原型对象
我们创建的每一个函数,解析器都会向函数中添加一个属性叫prototype,prototype属性对应着一个对象,这个对象就叫原型对象。
如果函数作为普通的函数去调用prototype没有什么作用。但是如果是以构造函数的方式调用时,构造函数创建的每一个对象都会含有一个隐含属性__proto__,指向该构造函数的原型对象。
function Person(name, age) {
this.name = name;
this.age = age;
this.print = myPrint;
}
function myPrint() {
console.log(this.name);
console.log(this.age);
}
var per = new Person('DLL', 29)
console.log(Person.prototype);
console.log(per.__proto__);
//构造函数创建的对象的__proto__和构造函数的prototype是相同的
console.log(per.__proto__ === Person.prototype);//true
原型对象就相当于一个公共区域,同一个类的所有实例都可以访问到这个原型对象。因此,我们可以将对象中的共有内容,统一设置到原型对象中。当我们访问对象的属性或方法时,他会先在自身中寻找,如果没有找到,会再从原型对象中查找使用。所以,以后我们创建构造函数时,可以将这些对象共有的属性和方法放到原型对象中。
function Person(name, age) {
this.name = name;
this.age = age;
}
//向原型对象中添加属性和函数
Person.prototype.test = 88888;
Person.prototype.print = function() {
console.log(this.name);
console.log(this.age);
}
var per = new Person('DLL', 29)
//我们可以直接使用通过原型对象添加的函数和属性
per.print()
console.log(per.test);
使用in和hasOwnProperty检查对象是否含有某个属性的区别
function Person(name, age) {
this.name = name;
this.age = age;
}
//向原型对象中添加属性和函数
Person.prototype.test = 88888;
Person.prototype.print = function() {
console.log(this.name);
console.log(this.age);
}
var per = new Person('DLL', 29)
//我们可以直接使用通过原型对象添加的函数和属性
per.print()
console.log(per.test);
//使用in来检查对象是否含有某个属性时,如果对象没有,但是原型对象中有,那么返回为true
console.log('test'in per);
const result = per.hasOwnProperty('test')
//使用hasOwnProperty检查对象属性时,必须是自己含有该属性时才会返回为true
console.log(result);//false
Object的原型对象是null
console.log(per.hasOwnProperty('hasOwnProperty'));//false
console.log(per.__proto__.hasOwnProperty('hasOwnProperty'));//false
//per.__proto__.__proto__对应的原型对象就是Object
console.log(per.__proto__.__proto__.hasOwnProperty('hasOwnProperty'));//true
//Object的原型对象是null,也就是说Object没有原型对象
console.log(per.__proto__.__proto__.__proto__);//null
toString()
function Person(name, age) {
this.name = name;
this.age = age;
}
//如果我们希望在打印对象时不输出[object object],我们可以为对象添加一个toString方法。
Person.prototype.toString = function () {
return `我的姓名是:${this.name},我的年龄是${this.age}岁`;
}
var per = new Person('DLL', 29);
//当我们直接在页面中打印一个对象时,输出的是对象的toString()方法的返回值
console.log(per);
document.write(per)
Array
array的类型是object,并且如果数组发生越界,不会报错error,而是取出的值为undefined:
let arr = []
console.log(typeof arr) //object
console.log(arr[100]) //undefined
修改length的值,如果length长度大于arr的原长度,那么多余的长度会在数组中空出来;如果小于原数组的长度,那么原数组会被截取掉:
let arr = [1,2,3,4,5]
arr.length = 10
console.log(arr)//[1,2,3,4,5,,,,,,]
console.log(arr[6])//undefined
let arr2 = [1,2,3,4,5]
arr2.length = 3
console.log(arr2) //[1,2,3]
array作为函数的参数进行传递,直接操作array(比如push,pop等操作)会影响arr;但是如果通过赋值的形式,则不会影响arr。
let arr = [1,2,3,4,5]
function test(array) {
array.push(1)
}
test(arr)
console.log(arr) //[1, 2, 3, 4, 5, 1]
slice与splice的区别:
//slice(start, end) 截取数组,不影响原数组
var array = ['夏','商','周','齐','楚','燕','韩','赵','魏','秦']
var result = array.slice(1,4)
console.log(result);//["商", "周", "齐"]
console.log(array);//["夏", "商", "周", "齐", "楚", "燕", "韩", "赵", "魏", "秦"]
//如果传负数-4,表示截取到最后,倒数后4个数不算在内
var result1 = array.slice(1, -4)
console.log(result1);//["商", "周", "齐", "楚", "燕"]var result2 = array.slice(-1, 4)
console.log(result2);//["商", "周", "齐", "楚", "燕"]
//splice(start,deleteCount,items),返回值是被删除掉的数组(计数包括从start开始)
var array = ['夏','商','周','齐','楚','燕','韩','赵','魏','秦']
//返回的是删除后的数组
var result = array.splice(1,4)
console.log(result);//["商", "周", "齐", "楚"]
console.log(array);//["夏", "燕", "韩", "赵", "魏", "秦"]
var array2 = ['夏','商','周','齐','楚','燕','韩','赵','魏','秦']
//还可以传入第三个参数,来替换值
var result2 = array2.splice(3, 7, '春秋', '战国')
console.log(result2);//["齐", "楚", "燕", "韩", "赵", "魏", "秦"]
console.log(array2);//["夏", "商", "周", "春秋", "战国"]
//如果第二个参数deleteCount值为0,splice则可以实现插入的功能
var array3 = ['夏','商','周','齐','楚','燕','韩','赵','魏','秦']
//还可以传入第2个参数为0
var result3 = array3.splice(3, 0, '大周', '小周', '996')
console.log(result3);//[]
console.log(array3);// ["夏", "商", "周", "大周", "小周", "996", "齐", "楚", "燕", "韩", "赵", "魏", "秦"]
js实现边遍历边删除的操作:
//错误示范(array执行pop后会影响array.length的值)
var array = ['夏','商','周','齐','楚','燕','韩','赵','魏','秦']
for (var i = 0; i < array.length; i++) {
array.pop()
}
console.log(array)//["夏", "商", "周", "齐", "楚"]
var array = ['夏','商','周','齐','楚','燕','韩','赵','魏','秦']
for (var i = 0; i < array.length; i++) {
array.pop()
i--;
}
console.log(array)//[]
//那么同理可以实现遍历删除重复数组元素的操作:
var array = [1,1,1,2,3,3,3,3,4,4,5,6,7,7]
for (var i = 0; i< array.length; i++) {
for (var j = i+1; j < array.length; j++) {
if (array[i] === array[j]) {
array.splice(j,1)
j--
}
}
}
console.log(array) //[1, 2, 3, 4, 5, 6, 7]
数组使用sort排序(会影响原数组):
var array = [11,21,1,52]
//a - b 如果大于0,则交换位置,小于等于0,则不交换位置(例如:11-22)
let result = array.sort(function (a, b) {
return a - b;
})
console.log(array) //[1, 11, 21, 52]
console.log(result) //[1, 11, 21, 52]
call和apply的区别
call和apply方法都是函数对象的方法,需要函数对象来调用:
function test1() {
console.log('我被调用了。。。');
}
test1()
test1.call()
test1.apply()
在调用call和apply方法时,可以将一个对象指定为函数的调用者:
var age = 110;
var user = {
age: 120
}
function test() {
console.log(this.age);
}
//直接调用test函数,则使用的age属性为全局的age属性。
test() // 110
//指定user,则使用user对象中的age属性
test.apply(user) // 120
test.call(user) //120
不同对象之间也可以调用call和apply:
var user = {
age: 120,
print: function () {
console.log(this.age);
}
}
var user2 = {
age: 119,
print: function () {
console.log(this.age);
}
}
user2.print() // 119
user2.print.call(user) //120
user2.print.apply(user) //120
传递参数时,call和apply的方式会有不同:
var desc = '我还是个汪'
var user = {
desc: '我不是单身',
}
function print(age, name) {
console.log(`我叫${name},今年${age}岁了!${this.desc}`);
}
//传递的是一个个的属性
print.call(user, 18, '周杰伦') //我叫周杰伦,今年18岁了!我不是单身
//传递的是数组
print.apply(user, [18, '周杰伦'])//我叫周杰伦,今年18岁了!我不是单身
print(19,'程序员')//我叫程序员,今年19岁了!我还是个汪
arguments
在调用函数时,浏览器每次都会传递进来2个隐含的参数,一个是this,另一个就是arguments。
function test() {
console.log(arguments);
}
test()// [object Arguments]
arguments是类数组对象。它同数组类似,它也可以通过索引获取数据,它也可以获取length长度。在调用函数时,如果函数有参数,那么我们传递的参数都会被保存在arguments中。我们可以通过arguments.length来获取传递参数的长度。所以,我们即使不定义函数的形参,也可以获取传递给函数的实参。
function test() {
console.log(arguments.length);
}
test()// 0
test('lxx')// 1
test('lxx', 123, false)// 3
我们可以通过arguments.callee来获取当前正在调用的函数对象:
function test(name) {
console.log(arguments.length);//3
console.log(arguments.callee === test);//true
console.log(arguments.callee);
// function test(name) {
// console.log(arguments.length);
// console.log(arguments.callee === test);
// console.log(arguments.callee);
// }
}
test('lxx', 123, false)
正则表达式
基本使用:
let reg = new RegExp('a')
let str = 'abcdefg'
let result = reg.test(str)
console.log(result); //true
忽略大小写:
let reg = new RegExp('a', 'i')
let str = 'Abcdefg'
let result = reg.test(str)
console.log(result); //true
使用字面量来创建正则表达式:
//语法: var 变量 = /正则表达式/匹配模式
let reg = /a/i
let str = 'Abcdefg'
let result = reg.test(str)
console.log(result); //true
正则表达式中 |表示或:
//匹配a或者b,忽略大小写
let reg = /a|b/i
let str = 'efgda'
let result = reg.test(str)
console.log(result); //true
使用[ab]也可以表示或:
//是否包含字母
/[a-z]/i
/[A-z]/ (注意:如果使用这种方式来忽略大小写,必须式大写A,小写z)
let reg = /[a-z]/i
let str = '123A'
let result = reg.test(str)
console.log(result); //true
//是否是字符串abc adc aec中的一个
/a[bde]c/
//除了a和b,是否包含别的
let reg = /[^ab]/
let str = 'ac'
let result = reg.test(str)
console.log(result); //true
//除了数字外,是否包含别的
/[^0-9]/
在字符串中使用正则表达式:
let str = 'abcd1e3fgh77ij'
let result = str.split(/[0-9]/)
console.log(result); //["abcd", "e", "fgh", "", "ij"]
//搜索字符串中是否含有指定字符
let result2 = str.search(/[e-g]/)
console.log(result2); //5
//提取所有的字母,默认找到一个符合条件的就停止,所以要设置全局匹配
let result3 = str.match(/[A-z]/g)
console.log(result3); //["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
let result4 = str.replace(/[0-9]/g, '-->')
console.log(result4); //abcd-->e-->fgh-->-->ij
设置次数:
//a出现3次
/a{3}/
//ab出现3次
/(ab){3}/
//b出现1-3次
/ab{1,3}c/
//b至少出现3次
/ab{3,}c/
//b至少出现1次 相当于 /ab{1,}c/
/ab{+}c/
//b出现0次或多次 相当于 /ab{0,}c/
/ab{*}c/
//b出现0次或者1次 相当于 /ab{0,1}c/
/ab{?}c/
设置位置:
//以a开头:
/^a/
//以a结尾:
/a$/
//以a开头或者以a结尾:
/^a|a$/
//验证手机号:
/^1[3-9][0-9]{9}$/
特殊字符:
//在正则表达式中, “.”表示任意字符,如果要检查字符串中是否含有“.”,则需要使用"\.":
let str = 'abc.efg'
let regExp = /\./
console.log(regExp.test(str)) //true
//注意,在使用正则表达式中的构造方法来创建时,由于参数是字符串,而在字符串中“\”又是转义字符
//所以,在构造方法中,需要使用“\\”,例如,我们查询字符串是否以“.”结尾:
let str = '.abc.efg.'
let regExp = /\.$/
console.log(regExp.test(str)) //true
let regExp2 = new RegExp('\\.$')
console.log(regExp2.test(str)) //true
//如果我们要查询是否已“\\”开头,则分别需要这样写:
let str = '\\\\abc.efg.'
let regExp = /^\\/
console.log(regExp.test(str)) //true
let regExp2 = new RegExp('^\\\\')
console.log(regExp2.test(str)) //true
//在正则表达式中,\w代表任意字母,数字,“_”下划线这三种,相当于[A-z0-9_]:
let str = '_1abc.efg.'
let regExp = /^\w/
console.log(regExp.test(str)) //true
let regExp2 = new RegExp('^\\w')
console.log(regExp2.test(str)) //true
//在正则表达式中,\W代表不是字母,数字,“_”下划线这三种的任意字符,相当于[^A-z0-9_]:
let str = '_1abc.efg.'
let regExp = /\W$/
console.log(regExp.test(str)) //true
let regExp2 = new RegExp('\\W$')
console.log(regExp2.test(str)) //true
\d 任意数字
\D 除了数字
\s 空格
\S 除了空格
\b 单词边界
\B 除了单词边界
let str = 'grandfather'
let regExp = /father/
console.log(regExp.test(str)) //true
let regExp2 = /\bfather\b/
console.log(regExp2.test(str)) //false
//去除字符串中前后的空格
let str = ' hello! grandfather '
let regExp = /^\s*|\s*$/g
let result = str.replace(regExp, '')
//电子邮件
// lxxwork0827@163.com
// sz.phealth@company.com.cn
// 任意字母下划线 .任意字母下划线 @ 任意字母数字 .任意字母{2-5} .任意字母{2-5}
/^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/