面向对象的编程介绍
面向过程:分析出解决问题的步骤,然后利用函数把这个步骤一步一步实现,使用的时候一个一个依次调用。
举例:得到大象-> 打开冰箱 ->大象放进去 ->冰箱关上
面向对象:把事务分解成一个个对象,由对象之间分工合作。
举例:一个大象的对象,有个方法叫进去。
一个冰箱的对象,有2个方法叫:打开和关闭。
然后使用大象和冰箱的功能。
ES6中的类和对象
用class来创建对象。
语法:
class name{
}
创建实例:
var xx=new name();
构造函数
constructor():用于传递参数,返回实例对象。通过new生成对象实例时,自动调用该方法。如果没显示定义,类内部会自动创一个constructor()。
class lkq{
constructor(uname){
this.uname=uname;
}
study(){
}
}
类的继承:
class father{
constructor(x,y){
this.x=x;
this.y=y;
}
sum(){
console.log(this.x+this.y);
}
}
class son extends father{
constructor(x,y){
super(x,y);//访问和调用父类的函数
}
}
son.sum(1,2);//不加super()会报错,因为1,2传给son的x,y,而没有传给父亲的x,y。
super要写在this的前面。constructor()的this指向的是创建的实例对象。方法里的this指的是方法的调用者。
在ES6之前,通过构造函数创建对象。
构造函数
静态成员:在构造函数本身上添加的成员。静态成员只能通过构造函数来访问。Star.sex
function Star(uname,age){
.....
}
Star.sex='男';//静态成员
实例成员:构造函数内部通过this添加的成员,比如以下的这个uname和age和sing就是实例成员.实例成员只能通过实例化对象来访问。比如var ldh=new Star('ldh',18); ldh.uname;
function Star(uname,age){
this.uname=uname;
this.age=age;
this.sing=function(){
console.log('唱歌');
}
}
构造函数的问题:内存浪费。比如定义了两个实例化对象,它俩使用同一种方法,但是开辟不同的内存地址空间。
原型
原型是一个对象,是每个构造函数里都会存在的对象。作用:共享方法;只要把方法定义在原型上,所有实例都能用这个方法,不用开辟额外的内存空间。
function Star(uname,age){
this.uname=uname;
this.age=age;
}
Star.prototype.sing=function(){
console.log('唱歌');
}
一般情况下,公共属性定义在构造函数里面,公共方法放到原型上。
对象都会有一个属性__proto__,指向构造函数prototype原型对象
ldh.__proto__===Star.prototype
方法的查找规则:先找ldh对象身上是否有sing方法,有就直接执行对象上的sing,没有就去原型对象prototype上找sing这个方法。
对象原型(__proto__)和构造函数(prototype)原型对象里面都有一个属性constructor属性,指回构造函数本身。
constructor主要用于记录该对象引用哪个构造函数,很多情况下,我们需要手动利用constructor这个属性指回原来的构造函数。
当方法比较多可以这么写:
Star.prototype={//这种写法会覆盖constructor方法,就相当于没有constructor了,需要指回
constructor:Star,
sing:function(){
console.log('唱歌');
},
movie:function(){
console.log('演电影');
}
}
总结:如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,就必须手动利用constructor指回原来的构造函数
构造函数、实例、原型对象三者之间的关系:
Star构造函数 ------------》通过构造函数.prototype指向 Star原型对象prototype(它的__proto__ 指向Object的prototype)
↑ \ 原型对象的属性constructor指回构造函数 《-------------- ↑
\ \ /
\ \new构造函数,产生实例对象 /实例里有个原型:ldh.__proto__
\ \构造函数指向实例对象 / 指向原型对象
\ 》 ldh对象的实例 /
(实例对象可以通过ldh.__proto__.constructor指向构造函数)
js中成员查找规则(ldh实例对象找,找不到就找Star原型对象,找不到再找Object原型对象,再没有就返回undefined)
1.当访问一个对象的属性(方法)时,首先查这个对象自身有没有该属性
2.没有就找它原型(prototype原型对象)
3.没有就找Object的原型对象
4.再找不到就null
__proto__对象原型的意义在于为对象成员查找机制提供一个方向,或者一条路线。
构造函数的this指向
1.在构造函数中this指向的是实例对象ldh
2.原型对象里的this指向实例对象ldh
扩展内置对象
通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。
在数组和字符串内置对象不能给原型对象覆盖操作:Array.prototype={}
Array.prototype.sum=function(){
var sum=0;
for(var i=0;i<this.length;i++){
sum+=this[i];
}
return sum;
}
继承
call():
作用:1.调用某个函数 2.修改函数运行时this的指向
语法:函数.call(thisArg,arg1,arg2,...);
thisArg:让this指向谁
arg1,arg2:可以传递的普通参数
作用1:
function fn(){
console.log('混蛋');
}
fn.call(); //调用成功
作用2:
function fn(){
console.log('混蛋');
}
var o={
name:'andy';
};
fn.call(o);//让fn的this指向o
借用构造函数继承父类属性,原型对象继承父类方法——组合继承
怎么用构造函数继承父类属性?
核心原理:通过call()把父类型的this指向子类型的this
function Father(uname,age){
this.uname=uname;
this.age=age;
}
function Son(uname,age){
Father.call(this,uname,age);
}
怎么继承父类方法?
function Father(uname,age){
this.uname=uname;
this.age=age;
}
Father.prototype.money=function(){
console.log('30000000');
}
function Son(uname,age){
Father.call(this,uname,age);
};
//Son.prototype=Father.prototype;这样赋值有问题,修改子类原型对象,父类原型对象也会跟着一起变化
Son.prototype=new Father();//son的原型对象指向father实例对象,father实例对象指向father原型对象
Son.prototype.constructor=Son;
ES5新增的方法
数组方法
遍历方法:forEach()、map()、fliter()、some()、every()
array.forEach(function(currentValue,index,arr));
currentValue:数组当前的这个值
index:当前值的索引号
arr:数组本身
var arr[1,2,3];
arr.forEach(function(value,index,arr){
});
array.fliter(function(currentValue,index,arr));
fliter()是创建一个新数组,新数组中的元素是原数组筛选以后的符合条件的所有元素,直接返回一个新数组。
var arr=[12,66,4];
var newArr=arr.fliter(function(value,index){
return value>=20;
});
array.some(function(currentValue,index,arr));
some()用来查找数组中是否有满足条件的元素,返回的是布尔值,找到就返回true。找到以后终止循环,不再继续查找。
var arr=[12,66,4];
var flag=arr.some(function(value){
return value>=20;
});
字符串方法
str.trim():会从一个字符串两端删除空白字符,不影响原字符串本身,返回一个新字符串
对象方法
Object.keys(obj):获取对象自身的所有属性名,返回的是由属性名组成的数组
Object.defineProperty(obj,prop,descriptor):定义新属性或修改原有的属性
这三个参数都是必填的。
obj:目标对象
prop:需要定义或修改的属性的名字
descriptor:目标属性所拥有的特性,这个参数以对象的形式书写,有四个选项:value(设置属性的值,默认undefined)、writable(值是否可以重写true | false)、enumerable(目标属性是否可以被枚举 true | false)、configurable(目标属性是否可以被删除或是否可以再次修改特性true | false)
比如:
var obj={
id:1,
pname:'小米',
price:1999
};
//添加:
Object.defineProperty(obj,'num',{
value:1000;
});//等价于obj.num=1000;有num属性就修改,没有就算添加
函数的定义和调用
定义:
1.利用function关键字(命名函数)function fn(){}
2.函数表达式(匿名函数)var fun=function(){}
3.new Function('参数1','参数2','函数体') :这个Function是个构造函数。 var f=new Function();
var f=new Function('console.log(123)');
注意:Function里面都必须是字符串格式,方法3的执行效率低,可读性差
所有函数都是Function的实例对象,都是通过new Funtion()得到的。
函数也属于对象
调用:
1.普通函数
function fn(){}
调用:fn();或fn.call();
2.对象的方法
var o={
sayHi:function(){}
}
调用:o.sayHi();
3.构造函数
function Star(){}
调用:new Star();
4.绑定事件的函数
btn.οnclick=function(){}
调用:点击按钮
5.定时器调用
setInterval(function(){},1000);//这个函数是定时器自动一秒钟调用一次
6.立即执行函数
(function(){
})();
自动调用。
this的指向
调用方式 | this指向 |
普通函数调用 | window |
对象的方法 | 指向对象o |
构造函数 | 实例对象 |
事件绑定方法 | 绑定事件的对象 |
定时器函数 | window |
立即执行函数 | window |
改变函数内部this指向
1.fun.call(thisArg,arg1,arg2,...)
2.fun.bind(thisArg,arg1,arg2,....)---------------用的最多
bind()方法不会调用函数。返回的是原函数改变this之后产生的新函数
var f=fn.bind(o);
f();
如果有的函数我们不需要立即调用,但又想改变这个函数内部的this指向,此时用bind()最合适。
比如:有个按钮,点击后禁用这个按钮,3秒后再开启这个按钮。
var btn=document.querySelector('button');
btn.onclick()=function(){
this.disabled=true;//这个this指向btn
setTimeout(function(){
this.disabled=false;//这个this指向window
}.bind(this),3000)
}
3.fun.apply(thisArg,[argsArray])
thisArg:在fun函数运行时指定的this值
argsArray:传递的值,必须包含在数组里面
apply的应用:求数组最大值
Math.max.apply(null,arr);
call apply bind的总结
相同点:
都可以改变函数内部this的指向
不同点:
1.call和apply会调用函数,且改变函数内部this的指向.bind不会调用函数
2.call和apply传递的参数不一样,call传递的参数是arg1,arg2...这样的形式。apply传递的是数组形式[arg]
主要应用场景:
1.call经常做继承
2.apply经常跟数组有关系,比如借助它实现求数组最大值,最小值
3.bind适合不调用函数,但还想改变this指向的。比如改变定时器内部的this指向。
严格模式
严格模式对Js做了一些修改,
1.消除了js语法的一些不合理、不严谨的地方,比如变量可以不声明就直接赋值
2.消除了代码的一些不安全地方
3.提高编译器效率,增加运行速度
4.禁用了在ECMAScript的未来版本中可能会定义的一些语法,比如让保留字:class、enum等不能做变量名。
开启严格模式:
严格模式可以应用到整个脚本或个别函数中。所以在使用时,可以将严格模式分为脚本开启严格模式和为函数开启严格模式两种情况。
为脚本开启严格模式
需要在所有语句前写一句话:"use strict";
为函数开启严格模式
function fn(){
"use strict";
}
严格模式的变化
1.变量规定:
(1)在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。
严格模式中,禁止这种写法,变量必须先声明再使用。
(2)严禁删除已经声明过的变量,delete x;这种语法是错误的。
2.严格模式下this指向问题
(1)以前在全局作用域函数中this指向window对象。
严格模式下全局作用域中函数中的this指向undefined
(2)以前构造函数不加new也可以调用,this指向全局对象
function Star(){
this.sex='男';
}
Star();
严格模式下,不加new调用构造函数,会报错,因为this指向undefined
(3)加了new实例化构造函数指向创建的实例对象。
(4)定时器的this指向的还是window
(5)事件、对象的this还是指向调用者
3.函数变化
(1)函数不能有重名的参数
function fn(a,a){
}
(2)函数必须声明在顶层
比如这样就会报错:
if(....){
function fn(){
}
}
高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。
比如:第一种:
function fn(callback){
callback&&callback();
}
fn(function(){alert('hi')}
第二种:
function fn(){
return function(){}
}
fn();
闭包
在函数内的是局部变量,函数外的是全局变量。
1.函数内部可以使用全局变量
2.函数外面不能使用局部变量
3.函数执行完后,局部变量会被销毁
什么是闭包?
有权访问另一个函数作用域中变量的函数。简单理解就是,一个作用域能访问另一个函数内部的局部变量。变量所在的函数就是闭包。
function fn(){//fn就是闭包
var num=10;
return function(){
console.log(num);
}
}
var f=fn();
f();
闭包的作用?
延伸了变量的作用范围。
案例:点击li输出对应的序号
<body>
<ul class="nav">
<li>榴莲</li>
<li>臭豆腐</li>
<li>鲱鱼罐头</li>
</ul>
<script>
var lis=document.querySelector('.nav').querySelectorAll('li');
for(var i=0;i<lis.length;i++){
(function(i){ //可能内存泄露,只要一直不点击这个i,就一直声明着
lis[i].οnclick=function(){
console.log(i);
}
})(i);
}
</script>
</body>
案例2: 3秒后,打印所有li元素的内容
<script>
var lis=document.querySelector('.nav').querySelectorAll('li');
for(var i=0;i<lis.length;i++){
(function(i){
setTimeout(function(){
console.log(lis[i].innerHTML);
},3000)
})(i);
</script>
浅拷贝和深拷贝
浅拷贝只拷贝一层,更深层次的对象级别的只拷贝引用。
深拷贝拷贝多层,每一级别的数据都会拷贝。
浅拷贝:o拷贝的是obj的地址,o里的msg和obj的msg指向同一个数据,如果修改了o里的msg会影响obj的msg。
var obj={
id:1.
name:'andy',
msg:{
age:18;
}
};
var o={};
for(var k in obj){
o[k]=obj[k];
}
ES6中新增Object.assign(target,...sources)来浅拷贝
深拷贝
var obj={
id:1.
name:'andy',
msg:{
age:18;
}
};
var o={};
function deepCopy(newobj,oldobj){
for(var k in oldobj){
//拿到属性值
var item=oldobj[k];
//判断这个值什么类型
if(item instanceof Array){
newobj[k]=[];
deepCopy(newobj,item);
}else if(item instanceof Object){
newobj[k]={};
deepCopy(newobj,item);
}else{
newobj[k]=item;
}
}
}
deepCopy(o,obj);
正则表达式
正则表达式是用于匹配字符串中字符组合的模式。
应用:匹配——比如用户名只能输入英文、数字或下划线
替换——比如过滤网页敏感词
提取——比如提取想要内容
一.创建正则表达式:
(1)调用RegExp对象的构造函数创建
var 变量名=new RegExp(/表达式/);
(2)用字面量创建
var 变量名=/表达式/;
二.测试正则表达式:
test():检测字符串是否符合规则,参数是测试的字符串,返回true | false。
regexpobj.test(str)
regexpobj是写的正则表达式
str是要测试的文本
三.正则表达式的组成
特殊字符称为元字符,是在正则表达式中有特殊意义的符号:比如^、$、+等。
1.边界符
^:匹配行首的文本(以谁开始)
$:匹配行尾的文本(以谁结束)
var rg=/abc/; //它的意思是只要包含abc字符串,返回都为true
console.log(rg.test('abc'));---------true
console.log(rg.test('abcd'));---------true
console.log(rg.test('aabcd'));-------true
var rg=/^abc/;
console.log(rg.test('abc'));----------true
console.log(rg.test('abcd'));--------true
console.log(rg.test('aabcd'));-------false
var rg=/^abc$/;
console.log(rg.test('abc'));----------true
console.log(rg.test('abcd'));--------false
console.log(rg.test('abcabc'));-------false
2.字符类
[]:表示有一系列字符可以选择,只要匹配其中一个就可以了
[-]:方括号内部范围符-
字符组合,见例3
如果[]里有^,表示取反的意思
var rg=/[abc]/; //只要包含有a,或b,或c就返回true
console.log(rg.test('marry'));-----true
var rg=/^[abc]$/; //只有a,b,c三者其中一个才返回true
console.log(rg.test('aa'));-------false
var rg=/^[a-z]$/; //26个英文字母,任何一个都为true
var rg=/^[a-zA-Z0-9_]$/; //可以输入英文(大小写都可)或数字或下划线
var rg=/^[^a-zA-Z0-9_]$/; //把^写到[]里面,表示取反,表示不能包含英文字母、数字、下划线
3.量词符:用来设定某个模式出现的次数
*:重复0次或更多次
+:重复1次或更多次
?:重复0次或1次
{n}:重复n次
{n,}:重复n次或更多次
{n,m}:重复[n]-[m]次
var rg=/^a*$/;
console.log(rg.test(''));------true
console.log(rg.test('aaaaa'));-----true
var reg=/^(abc){3}$/; //让"abc"重复3次,()表示优先级
限定输入用户名:
var rg=/^[a-zA-Z0-9_]{6,16}$/;
注意:量词中间不能有空格
案例:用户名验证,输入合法,提示用户名合法,颜色绿色
输入非法,提示不符合规范,颜色为红色
.right{
color:green;
}
.wrong{
color:red;
}
<body>
<input type="text" class="name"><span>请输入用户名</span>
<script>
var reg=/^[a-zA-Z0-9_-]{6,16}$/;
var uname=document.querySelector('.uname');
uname.οnblur=function(){
if(reg.test(this.value)){
span.className='right';
span.innerHTML='用户格式输入正确';
}
else{
span.className='wrong';
span.innerHTML='请输入6-16位由数字字母下划线组成的用户名';
}
</script>
</body>
4.预定义类(常见模式的简写方式)
\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]
| :正则里的或符号
比如:座机号码验证,全国座机号码有2种格式:如010-12345678或0530-1234567
var reg=/^\d{3}-\d{8} | \d{4}-\d{7} $/;
手机号码验证:
写法一:
var reg=/^(13[0-9] | 14[5 | 7] |15[ 0 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 ] | 18[ 0 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 ])\d{8}$/;
13几开头的 或 14几开头的 或15几开头的 或18几开头的 ,这是前3位,手机号一个11位,剩下8位。
写法二:
var reg=/^1[3 | 4 | 5 | 7 | 8 ]\d{9}$/;
5.正则表达式的参数
/表达式/[switch]
switch叫修饰符
g:全局匹配
i:忽略大小写
gi:全局匹配和忽略大小写
四.正则表达式的替换
replace方法可以替换字符串,替换的参数可以是字符串或正则表达式
stringObject.replace(regexp/substr,replacement);
第一个参数:被替换的字符串或正则表达式
第二个参数:替换为的字符串
返回值是一个替换完的新字符串
案例:替换敏感词
<body>
<textarea name="" id="message"></textarea>
<button>提交</button>
<div></div>
<script>
var text=document.querySelector('textarea');
var btn=document.querySelector('button');
var div=document.querySelector('div');
btn.οnclick=function(){
div.innerHTML=text.value.replace(/草|妈的/g,'**');
}
</script>
</body>