JavaScript高级
1.面向对象编程
1.1.创建对象
-
通过Object()创建
var person = new Object() person.name = 'Jack' person.age = 18 person.sayName = function () { console.log(this.name) }
-
字面量来创建
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
1.2.构造函数
1.2.1.成员
实例成员:实例成员/对象成员(构造函数的this创建的属性) – 跟对象相关的成员,将来使用对象的方式来调用
静态成员:直接给构造函数添加的成员 – 静态成员不能使用对象的方式来调用(是构造函数私有的),静态成员使用构造函数来调用
1.2.2原型
-
创建构造函数就会有一个原型
-
访问原型
a. Student.prototype
可以用来设置属性和方法
注意:
Student.prototype = {}
(1).先让原型重新指向一个对象
(2).然后再创建实例对象
(3).设置新对象的属性 constructor.Student
b. 实例:
__proto__
(非标准属性,不要用它给原型设置属性或者方法)c. 属性:constructor(指向了创建p实例的构造函数)
注意:对象的
__proto__
等于 构造函数的Student.prototype -
原型链
- 实例p.
__proto__
指向Student的原型 - 实例p.
__proto__
.__proto__
指向Object的原型 - 实例p.
__proto__``__proto__``__proto__
指向null
- 实例p.
1.3.属性的查找规则
实例对象p.name
当调用对象的属性或者方法的时候,先去找对象本身的属性/方法 ,如果对象没有该属性或者方法。此时去调用原型中的属性/方法 ,原型中也没有该属性或者方法 ,去Object的原型里找,有就返回值,没有则找到null,返回underfined
1.4.属性的设置
直接在实例对象上设置,不会去原型里设置属性
2.继承
继承目的: 把子类型中共同的成员提取到父类型中,代码重用
2.1构造函数的原型方法继承:拷贝继承(for-in)
<script>
var wjl = {
name: '王健林',
money: 10000000,
cars: ['玛莎拉蒂', '特斯拉'],
houses: ['别墅', '大别墅'],
play: function () {
console.log('打高尔夫');
}
}
var wsc = {
name: '王思聪'
}
// // 复制对象的成员给另一个对象
// for (var key in wjl) {
// // 不给wsc复制同名的属性
// if (wsc[key]) {
// continue;
// }
// wsc[key] = wjl[key];
// }
// console.dir(wsc);
// 对象的拷贝
// 复制对象的成员给另一个对象
function extend(parent, child) {
for (var key in parent) {
// 不给wsc复制同名的属性
if (child[key]) {
continue;
}
child[key] = parent[key];
}
}
extend(wjl, wsc);
console.dir(wsc);
/* //对象浅拷贝
//Object.assign(新的对象,要复制的对象)
var obj = {
name: 'ls',
age: 20,
arr: [1, 2]
}
var newObj = Object.assign({}, obj);
console.log(newObj); */
</script>
深拷贝的方法
1.自己写for in 与递归
2.使用JSON.parse(JSON.stringify())
2.2另一种继承方式:原型继承
特点:能够让子类型创建的实例对象访问父原型里的方法
缺点:
- 创建子类实例时,无法向父类构造函数传参
- 来自原型对象的引用属性是所有实例共享的 ,一改变全改变
<script>
// 继承:类型和类型之间的关系
// 学生类型 老师类型 -> Person类型
// 继承目的: 把子类型中共同的成员提取到父类型中,代码重用
// 父类型
function Person() {
this.name = 'zs';
this.age = 18;
this.sex = '男';
}
// 子类型
function Student() {
this.score = 100;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
var s1 = new Student();
console.log(s1.constructor);
console.dir(s1);
function Teacher() {
this.salary = 3000;
}
// 原型继承: 无法设置构造函数的参数
</script>
2.3构造函数的属性继承:借用构造函数
特点:子类型可以借用父类型的属性,或者实例成员
缺点:无法继承子类型的方法
继承属性,不继承方法
<script>
// 借用构造函数
// 父类型
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
// this.sayHi
}
Person.prototype.sayHi = function () {
console.log(this.name);
}
// 子类型
function Student(name, age, sex, score) {
Person.call(this, name, age, sex);
this.score = score;
}
var s1 = new Student('zs', 18, '男', 100);
console.dir(s1);
</script>
2.4组合继承
组合继承:借用构造函数(属性) + 原型继承(方法)
<script>
// 组合继承:借用构造函数 + 原型继承
// 父类型
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.sayHi = function() {
console.log('大家好,我是' + this.name);
}
// 子类型
function Student(name, age, sex, score) {
// 借用构造函数
Person.call(this, name, age, sex);
this.score = score;
}
// 通过原型,让子类型,继承父类型中的方法
Student.prototype = new Person();
Student.prototype.constructor = Student;
// 学生特有的方法
Student.prototype.exam = function() {
console.log('考试');
}
// var s1 = new Student('zs', 18, '男', 100);
// console.dir(s1);
// var p1 = new Person('ls', 18, '男');
// console.dir(p1);
function Teacher(name, age, sex, salary) {
// 借用构造函数
Person.call(this, name, age, sex);
this.salary = salary;
}
// 通过原型让子类型继承父类型中的方法
Teacher.prototype = new Person();
Teacher.prototype.constructor = Teacher;
var t1 = new Teacher('ww', 30, '男', 100000);
console.dir(t1);
t1.sayHi();
</script>
3.函数进阶
1.函数声明
现代浏览器不会提升if语句中的函数声明 1.声明是函数 预解析会函数提升到最顶部function fn() {} 封装函数时用
2.函数表达式 预解析会把函数声明提升到最顶部 var fn = function() {} 注册事件时用
3.不要在条件语句中定义函数
4.函数也是对象 var fn = new Function(‘a’,‘b’,‘console.log(a+b)’); fn(1+2)
2.call、apply和bind
<script>
// call bind apply 改变函数中的this
// 函数是一个对象
// var fn = new Function();
// function fn() {
// }
// 证明fn是Function的实例(对象)
// console.log(fn.__proto__ === Function.prototype);
// console.dir(fn);
function fn(x, y) {
console.log(this);
console.log(x + y);
}
// fn(5, 6); // this->window
// call
// 1 调用函数,改变函数中的this(临时改变)
// 2 第一个参数 设置函数内部this的指向
// 其它参数,对应函数的参数
// 3 函数的返回值 call的返回值就是函数的返回值
// 4 测试
// var obj = {
// name: 'zs'
// }
// fn.call(obj, 5, 6);
// apply 只有两个参数
// 1 调用函数,改变函数中的this(临时改变)
// 2 第一个参数 设置函数内部this的指向
// 第二个参数 是数组
// 3 函数的返回值 的返回值就是函数的返回值
// 4 测试
// var obj = {
// name: 'ls'
// }
// fn.apply(obj, [1, 2]);
// bind
// 1 改变函数中的this(永久改变),不会调用函数,而是把函数复制一份
// 2 第一个参数 设置函数内部this的指向
// 其它参数,对应函数的参数
// 3 函数的返回值 call的返回值就是函数的返回值
// 4 测试
var obj = {
name: 'ww'
}
var f = fn.bind(obj, 5, 5);
f();
</script>
3.高阶函数
作为参数
function eat (callback) {
setTimeout(function () {
console.log('吃完了')
callback()
}, 1000)
}
eat(function () {
console.log('去唱歌')
})
作为返回值
<script>
// 求两个数的和
// 100 + m
// 1000 + m
// 10000 + m
function getFun(n) {
return function (m) {
return n + m;
}
}
// 求 100 + m
var fn100 = getFun(100);
// 求 1000 + m
var fn1000 = getFun(1000);
console.log(fn100(1));
console.log(fn1000(1));
</script>
4.函数闭包
浏览器的垃圾回收机制:在函数调用完成之后,浏览器会周期性清除函数作用域内的变量,节省内存
闭包产生的背景:就是浏览器在函数执行完后,不再清空函数作用域内的变量
***什么是闭包:***在嵌套的函数内,里层的函数如果访问了外层函数内的变量,就会产生闭包
特点:返回一个函数,引用外部的变量
优点:延展函数作用域
缺点:内存溢出
5.函数递归
递归:函数自己调用自己,一定要有一个结束条件
在递归的过程中会出错
内容溢出:超过了最大的堆栈大小
递归一般都要写一个结束的条件
<script>
// n的阶乘
// 1 * 2 * 3....* n
// n = 3, 3 * fn(3 - 1)
// n = 2, 2 * fn(2 - 1)
// n = 1, 1
function fn(n) {
if (n === 1) {
return 1;
}
return n * fn(n - 1);
}
console.log(fn(3));
console.log(fn(4));
</script>
<script>
// 斐波那契数列 1、1、2、3、5、8、13、21、34、.....
function fn(n) {
if (n === 1 || n === 2) {
return 1;
}
return fn(n - 1) + fn(n - 2);
}
console.log(fn(3));
console.log(fn(5));
</script>
深拷贝
利用递归
<script>
var obj1 = {
name: 'zs',
age: 18,
sex: '男',
dog: {
name: '金毛',
age: 2,
yellow: '黄色'
},
friends: ['lilei', 'hanmeimei']
}
var obj2 = {};
var arr = [];
console.log(arr instanceof Array);
console.log(arr instanceof Object);
function deepCopy(o1, o2) {
for (var key in o1) {
if (o1[key] instanceof Array) {
o2[key] = [];
deepCopy(o1[key], o2[key]);
} else if (o1[key] instanceof Object) {
o2[key] = {};
deepCopy(o1[key], o2[key]);
} else {
o2[key] = o1[key];
}
}
}
deepCopy(obj1, obj2);
obj1.name = 'xxxx';
obj1.dog.name = '大黄';
obj1.friends[0] = 'xxxx';
console.dir(obj1);
console.dir(obj2);
</script>
es6方法
<script>
var obj1 = {
name: 'zs',
age: 18,
sex: '男',
dog: {
name: '金毛',
age: 2,
yellow: '黄色'
},
friends: ['lilei', 'hanmeimei']
}
var newObj = JSON.parse(JSON.stringify(obj1));
</script>
4.正则表达式
正则表达式的作用
- 能对我们想要的字符串进行匹配
- 能对我们想要的字符串进行提取
- 能对我们想要的字符串进行替换
4.1元字符
常用元字符
元字符 | 说明 |
---|---|
\d | 匹配数字 |
\D | 匹配任意非数字的字符 |
\w | 匹配字母或数字或下划线 |
\W | 匹配任意不是字母,数字,下划线 |
\s | 匹配任意的空白符 |
\S | 匹配任意不是空白符的字符 |
. | 匹配除换行符以外的任意单个字符 |
^ | 表示匹配行首的文本(以谁开始) |
$ | 表示匹配行尾的文本(以谁结束) |
限定符
限定符 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
其他
[] 字符串用中括号括起来,表示匹配其中的任一字符,相当于或的意思
[^] 匹配除中括号以内的内容
\ 转义符
| 或者,选择两者中的一个。注意|将左右两边分为两部分,而不管左右两边有多长多乱
() 从两个直接量中选择一个,分组
eg:gr(a|e)y匹配gray和grey
[\u4e00-\u9fa5] 匹配汉字
/1{2,4}$/, ‘请输入2-4个汉字’
案例
验证手机号:
^\d{11}$
验证邮编:
^\d{6}$
验证日期:2019-6-8
^\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}$
4.2JavaScript 中使用正则表达式
创建正则对象
方式一
// 正则匹配
var str = 'abc';
var reg = new RegExp('\\w+'); //在这种方式写\d \w \s 加上转义符
console.log(reg.test(str)); //有一个返回值 是boolean
方式二
// 正则匹配 正则对象test方法
var str = 'abc';
var reg1 = /\w+/;
console.log(reg.test(str));//有一个返回值 是boolean
参数
标志 | 说明 |
---|---|
i | 忽略大小写 |
g | 全局匹配 |
gi或者ig | 全局匹配+忽略大小写 |
案列(表单验证)
<body>
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>
<script>
// 1 验证QQ号
var txtQQ = document.getElementById('txtQQ');
// 当光标离开文本框的时候
txtQQ.onblur = function () {
// 验证用户的输入是否是QQ号
var reg = /^\d{5,12}$/;
var span = this.nextElementSibling;
// 检测用户输入的文本是否匹配指定的模式(正则表达式)
if (!reg.test(this.value)) {
// 不匹配 在文本框后面的span中进行相应的提示
span.innerText = '请输入正确的QQ格式';
span.style.color = 'red';
} else {
// 匹配
span.innerText = '';
span.style.color = '';
}
}
// 2 验证邮箱
var txtEMail = document.getElementById('txtEMail');
txtEMail.onblur = function () {
// 验证用户的输入是否是邮箱地址 xxx@itcast.com.cn
var reg = /^\w+@\w+(\.\w+)+$/;
var span = this.nextElementSibling;
// 验证
if (!reg.test(this.value)) {
span.innerText = '请输入正确的电子邮箱格式';
span.style.color = 'red';
} else {
// 匹配
span.innerText = '';
span.style.color = '';
}
}
//表单验证封装
/* // 1 验证QQ号
addCheck('txtQQ', /^\d{5,12}$/, '请输入正确的QQ号格式');
// 2 验证电子邮箱
addCheck('txtEMail', /^\w+@\w+(\.\w+)+$/, '请输入正确的EMail号格式');
// 3 手机号
addCheck('txtPhone', /^[1-9]\d{10}$/, '请输入正确的手机号码格式');
// 4 验证日期
addCheck('txtBirthday', /^\d{4}-\d{1,2}-\d{1,2}$/, '请输入正确的日期格式')
// 5 验证姓名
addCheck('txtName', /^[\u4e00-\u9fa5]{2,4}$/, '请输入2-4个汉字')
// 文本框的验证封装成一个函数
// 第一个参数是元素的id
// 第二个参数 正则表达式对象 RegExp
// 第三个参数 是提示的文本
function addCheck(elementId, reg, tip) {
var element = document.getElementById(elementId);
element.onblur = function () {
var span = this.nextElementSibling;
// 验证
if (!reg.test(this.value)) {
span.innerText = tip;
span.style.color = 'red';
} else {
span.innerText = '';
span.style.color = '';
}
}
} */
</script>
</body>
4.3正则表达式的相关方法
1.RegExp对象
test() 匹配 返回值是是boolean,(true/false)
exec() 提取 提取一个内容;只返回一个匹配到的结果 如果没有匹配的内容返回null
2. String对象
match() 提取 可以提取多个内容
replace() 替换
split() 切割 变为数组
search() 搜索
4.4正则提取
<script>
// 1. 提取工资
var str = '张三:1000,李四:5000,王五:8000。';
var reg = /\d+/g;
// 提取多个内容
console.log(str.match(reg));
// 2. 提取email地址
var str = '123123@xx.com,fangfang@valuedopinions.cn 这是其它内容 286669312@qq.com 2、emailenglish@emailenglish.englishtown.com 286669312@qq.com...';
var reg = /\w+@\w+(\.\w+)+/g;
console.log(str.match(reg));
// 3. 分组提取
// 3. 提取日期中的年部分 2015-5-10
var dateStr = '2015-1-5';
var reg = /(\d{4})-(\d{1,2})-(\d{1,2})/;
// reg.test(dateStr);
// reg.exec(dateStr);
console.log(reg.exec(dateStr));
dateStr.match(reg);
console.log(RegExp.$1);
console.log(RegExp.$2);
console.log(RegExp.$3);
// 4. 提取邮件中的每一部分
var str = 'xxxx@itcast.com';
var reg = /(\w+)@(\w+)\.(\w+)/;
reg.test(str);
console.log(reg.test(str));
console.log(RegExp.$1);
console.log(RegExp.$2);
console.log(RegExp.$3);
</script>
split
<script>
// 1. 提取日期中的年部分 2015-5-10
var dateStr = '2015-1-5';
console.log(dateStr.split('-'));
// var dateStr = '2015/1-5';
// console.log(dateStr.split(/[/-]/));
// 2. 提取邮件中的每一部分
var str = 'xxxx@itcast.com';
console.log(str.split(/[@\.]/));
</script>
4.5正则替换
<script>
// 1. 替换所有空白
var str = " 123AD asadf asadfasf adf ";
// trim() 去除前后的空格
// console.log( str.trim());
// replace() 只能替换掉第一个查找到的内容
// console.log(str.replace(' ', 'x'));
// console.log(str.replace(/\s/g, ''));
// console.log(str.split(' ').join(''));
console.log(str.replace(/\s/g, ''));
console.log(str.split(' '));
console.log(str.split(' ').join(''));
// 2. 把所有,和,替换为.
var str = "abc,efg,123,abc,123,a";
console.log(str.replace(/,|,/g, '.'));
console.log(str.replace(/[,,]/g, '.'));
</script>
teStr.split(/[/-]/));
// 2. 提取邮件中的每一部分
var str = 'xxxx@itcast.com';
console.log(str.split(/[@\.]/));
</script>
4.5正则替换
<script>
// 1. 替换所有空白
var str = " 123AD asadf asadfasf adf ";
// trim() 去除前后的空格
// console.log( str.trim());
// replace() 只能替换掉第一个查找到的内容
// console.log(str.replace(' ', 'x'));
// console.log(str.replace(/\s/g, ''));
// console.log(str.split(' ').join(''));
console.log(str.replace(/\s/g, ''));
console.log(str.split(' '));
console.log(str.split(' ').join(''));
// 2. 把所有,和,替换为.
var str = "abc,efg,123,abc,123,a";
console.log(str.replace(/,|,/g, '.'));
console.log(str.replace(/[,,]/g, '.'));
</script>
\u4e00-\u9fa5 ↩︎