JavaScript-week3

197 阅读14分钟

正则表达式

什么是:定义字符串中字符出现规则的表达式
何时:切割 替换 验证

最简单的正则

关键字原文"xx"->/xx/后缀
后缀:g:global找全部
      i:忽略大小写

备选字符集

/^[备选字符集]$/;
强调:
        1、一个中括号,只管一个字符
        2、正则表达式默认只要满足就不管后续,我们希望从头到尾完全匹配,解决:前加^后加$:/^[备选字符集]$/;
特殊:如果备选字符集中的ascii码是连续的,那么我们可以用-省略中间的部分
比如:一位数字:[0-9];
    一位字母:[A-Za-z];
    一位字母,数字,下划线[0-9A-Za-z_];
    一位汉字:[\u4e00-\u9fa5];

预定义字符集

一位数字:\d
一位字母,数字,下划线:\w
一位空白字符:\s

量词

作用:规定一个字符集出现的次数
1、有明确数量:
        字符集{n,m}:前边相邻的字符集,至少n个,最多m个
        字符集{n,}:前边相邻的字符集,至少n个,多了不限
        字符集{n}:前边相邻的字符集,必须n个
2、没有明确数量
        ?:前边相邻的字符集,可有可无,最多1个
        *:前边相邻的字符集,可有可无,多了不限
        +:前边相邻的字符集,至少一个,多了不限

选择和分组

选择:在两个规则中选一个
       规则1|规则2
分组:将多个字符集临时组成一组子规则
       (规则1|规则2)

指定匹配位置

^:开头
$:结尾
特殊:两者同时使用:前加^后加$:表示从头到尾完全匹配
reg.test(user)//验证ture或false

密码强度

4位密码,数字和字母的组合,至少出现一位数字和一位大写字母 /^[0-9A-Za-z]{4}$/
预判公式:
        (?![0-9]+$) -> 不能全由数字组成
        (?![a-z]+$) -> 不能全由小写字母组成
        (?![0-9a-z]+$) -> 不能全由数字、不能全由小写字母、不能只有他俩的组合组成
        
/(?![0-9a-z]+$)(?![A-Za-z]+$)[0-9A-Za-z]{4}/;//4位密码,数字和字母的组合,至少出现一位数字和一位大写字母
/(?![0-9a-z]+$)(?![A-Za-z]+$)(?![A-Z0-9]+$)[0-9A-Za-z]{4}/;//4位密码,数字和字母的组合,三者必须都有

支持正则表达式字符串的API

切割

var arr = str.split(reg);

替换

基本替换法:缺陷:替换的东西都是固定的
var Newstr = str.replace(/正则表达式/后缀,"新内容");
//replace支持正则,并且搭配上后缀就可以找到全部

高级替换法:
var newStr=str.replace(/正则表达式/后缀,function(a){
//a代表正则匹配到的当前的关键字
return a.length==2?"**":"***";
	});

格式化

                var id="5001031xxx022xxx33";
		var reg=/\d{6}(\d{4})(\d{2})(\d{2})\d{4}/;
		id=id.replace(reg,function(a,b,c,d){
		//在replace的时候,正则出现了分组:我们会得到更多的形参
		//在形参a的后面就会出现n个形参,就看你有多少个分组
		//第一个分组获得的内容会保存在第二个形参中
		//第二个分组获得的内容会保存在第三个形参中
		//...
			return b+"年"+c+"月"+d+"日";
		})
			console.log(id);

正则对象

创建

直接量方式:var reg=/正则表达式/后缀;
构造函数方式:var reg=new RegExp("正则表达式","后缀");

API

var bool=reg.test(用户输入的内容);
true->验证成功 false->验证失败

Math对象

强调:不需要创建,直接使用
属性:Math.pi===3.1415926

API

上取整

var num = Math.ceil(num);//小数位数不能超过15位

下取整

var num = Math.floor(num);//无论超过多少都会省略小数部分

四舍五入取整

var num = Math.round(num);

以上三个都是垃圾,取整推荐:num.toFixed(d)

笔试题

不允许使用toFixed的情况下,我们自己封装一个函数,实现toFixed的功能,并且最后返回的是一个数字:
思路:看用户想保留的是几位小数,我们就拿着数字*10的几次方,使用Math.round四舍五入取整,最后/10的几次方

function sswr(num,b){
    num*=Math.pow(10,d);
    num=Math.round(num);
    num/=Math.pow(10.d);
    return nm.toString();

}

乘方

Math.pow(底数,幂);

开方

Math.sqrt(num);//只能开平方

最大值和最小值

var 最大/最小的=Math.max/min(a,b,c,d);
问题:本身不支持数组参数
解决:固定用法:
        var 最大/最小的 = Math.max/min.apply(Math,arr);
        apply其中的一个作用是可以将数组打散为单个元素
        

绝对值

将负数转为正数
Math.abs(-1);//1

随机数

Math.random();在0-1之间取出一个随机小数,可能去到0,但是不可能去到1,意味着取不到最大值
公式:praseInt(Math.random()*(max-min+1)+min);

Date日期对象

创建

创建当前日期时间

var now = new Date();

创建一个自定义时间

var birth = new Date("yyyy/MM/dd hh:mm:ss")

创建一个自定义时间

var birth = new Date(yyyy,MM-1,dd,hh,mm,ss)//修改月份,从0开始的

复制一个日期

为什么:日期的所有的API都是直接修改原日期的,无法获得修改之前的日期,所以,在执行API之前进行赋值,然后再操作复制后的日期。
var end = new Date(start);

操作

两个日期对象之间可以相减,得到一个毫秒差,换算出自己想要的某一部分
创建的最后一个方式: var date = new Date(毫秒数);

API

分量:时间的单位
年月日星期:FullYeat Month Date Day
时分秒毫秒:Hours Minutes Seconds Milliseconds
每一个分量都有一堆getxxx/setxxx
其中getxxx负责获取某一个分量的值
其中setxxx负责设置某一个分量的值
特殊:
        1、取值范围:
                FullYear:当前年份的数字
                Month0-11
                Day:0-6
                Hours:0-23
                Minutes,Seconds0-59
        
        2Day 没有set方法

如果希望对某个分量进行加减计算

date.setxxx(date.getxxx()+/-n)

格式化为字符串

date.toLocaleString();//本地日期格式,有兼容问题,解决:自定义format方法

Error对象

1、语法错误:SyntaxError -多半时你的语法符号写错了
2、引用错误:ReferenceError -没有创建就直接使用了
3、类型错误:TypeError -不是你的方法,你却使用了
4、范围错误:RangeError -只有一个API会碰到:num.toFixed(d)//d的取值范围:0-100之间

只要发生错误就会报错,会导致后续代码终止。

错误处理

就算报错,我们也不希望后续代码终止,而是给一个错误提示,后续代码可以继续执行
语法:
        try{
            放入你可能出错的代码
        }catch(err){
            发生错误后才执行
            congsole.log(err);//err就是我们的错误提示,英文
            console.log("中文错误提示");
        }
try...catch...的性能非常差,几乎里面的代码效率都会降到最低
可以用一个技术代替:if...else

抛出自定义错误

throw new Error("自定义错误信息")-只要是报错都会卡住后续代码

Function对象

创建

1、声明方式: function 函数名(形参,...){函数体;return 返回值;}
2、直接量方式:var 函数名 = function(形参,...){函数体;return 返回值;}
3、构造函数: var 函数名 = new Function("形参1","形参2",...,"函数体;return 返回值";)
    何时:如果你的函数体不是固定的而是动态拼接的字符串
          比如:以下案例,我们让用户来参与到底是顺序还是降序排列
                  var arr = [321,5,43,65,8];
                  var user = prompt("排序数组,如果您输入a-b则为升序排列,如果您输入b-a则为降序排列")
                  var compare = new Function("a","b","return"+user);
                  arr.sort(compare);
                  console.log(arr);

调用时,如果有return,记得接住
    var result = 函数名(实参,...)

重载

相同的函数名,传入不同的实参,可以自动选择对应的函数执行操作
问题:JS的语法不支持重载
      JS不允许有多个同名函数同时存在,如果同时存在,最后的会覆盖之前的所有
解决:在函数中有一个对象 - arguments对象
      什么是arguments对象:只能在函数中使用,自动创建,是一个类数组对象
      作用:可以接收所有传入的参数
      arguments对象是一个类数组对象:长的像一个数组,但是,不是一个数组
      只有三点和数组相同:1、使用下标 2、使用length 3、遍历
      arguments可以做的事:
              1、实现重载:通过函数内部判断arguments,执行不同的操作
              2、以后有没有形参都无所谓
              3、正式开发中,有可能会将多个函数整合为一个函数

匿名函数

没有名字的函数
1、匿名函数自调:
    为什么:节约内存,因为匿名函数没有变量引用着,用完就会立刻释放变量
    语法:(function(){
        //以后可以代替全局代码写法,尽量不要在外部书写JS
    })();
2、匿名函数回调:将函数作为实参,传递给其他函数调用
    1、学习回调的目的:只要不是自调,就是回调
    arr.sort(function(){})
    str.replace(reg,function(){})
    btn.onclick = function(){}

函数的执行原理

	1、程序加载时
	   创建执行环境栈(ECS):保存函数调用顺序的数组
	   首先压入全局执行环境(全局EC)
	   全局EC引用着全局对象window
	   window中保存着全局变量

	2、定义函数时
	   创建函数对象:封装代码段
	   在函数对象中有一个scope(作用域)属性:记录着函数来自的作用域是哪里
	   全局函数的scope都是window

	3、调用前
	   在执行环境栈(ECS)压入新的EC(函数的EC)
	   创建活动对象(AO):保存着本次函数调用时用到的局部变量
	   在函数的EC中有一个scope chain(作用域链)属性引用AO
	   AO有一个parent属性是函数的scope引用着的对象

	4、调用时:正是因为前面三步,我们才有了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错

	5、调用完:函数的EC会出栈,AO自动释放,局部变量也就自动释放了

	***两链一包:
	   作用域链:以函数的EC的scope chain属性为起点,经过AO,逐级引用,形成的一个链式结构
		 作用:查找变量,带来变量的使用规则

闭包

希望保护一个可以反复使用的局部变量的一种词法结构
何时使用:希望保护一个可以反复使用的局部变量的时候
如何使用:
    1、两个函数进行嵌套
    2、外层函数创建出受保护的变量
    3、外层函数return出内层函数
    4、内层函数要去操作受保护的量
强调:
    1、判断是不是闭包,有没有两个函数嵌套,返回内层函数,内层函数在操作受保护的变量
    2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
    3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
缺点:受保护的变量,永远都不会被释放,使用过多,内存泄漏
问题:应该在哪里用?
    1、三个事件需要防抖节流
        1、elem.onmousemove - 鼠标移入事件
        2、input.oninput - 每次输入/改变就会触发
        3window.onresize - 每次窗口的大下发生变化就会触发
        
防抖节流公式:
        elem.on需要防抖节流的事件 = function(){
         fdjl();//内层函数只要一旦移动就触发
     }
        function f1(){
            var timer = null;//3
            return function(){
            if(timer){clearTimeout(timer);timer=null;}
            timer = setTimeout(function(){
                    操作;
                },1000)
            }
        }
        var fdjl = f1();

小案例

滚动轮播

<!DOCTYPE html>
<html>

	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			* {
				margin: 0;
				padding: 0;
				list-style: none;
			}
			
			img {
				width: 100%;
				vertical-align: top;
			}
			
			.carousel {
				position: relative;
				overflow: hidden;
			}
			
			.carousel_img{
				width: 300%;
				overflow: hidden;
				position: absolute;
				left: 0;
				top: 0;
				transition: 1s;
			}
			.carousel_img>img{
				width: 33.33%;
				float:left;
			}
			.carousel>button {
				width: 50px;
				height: 50px;
				border-radius: 50%;
				border: 1px solid #666;
				background: rgba(0, 0, 0, .5);
				color: #fff;
				font-size: 35px;
				cursor: pointer;
				position: absolute;
				top: 50%;
				margin-top: -25px;
			}
			
			.carousel>button:nth-of-type(1) {
				left: 50px;
			}
			
			.carousel>button:nth-of-type(2) {
				right: 50px;
			}
			
			.carousel>button:hover {
				background: rgba(0, 0, 0, .6);
			}
			
			.carousel>button:active {
				background: rgba(0, 0, 0, 1);
			}
			
			.carousel ul {
				width: 100px;
				text-align: center;
				position: absolute;
				left: 50%;
				margin-left: -50px;
				bottom: 50px;
			}
			
			.carousel li {
				width: 10px;
				height: 10px;
				border-radius: 50%;
				background: #fff;
				cursor: pointer;
				display: inline-block;
			}
			
			
			li.active {
				background: red;
			}
		</style>
	</head>

	<body>
		<div id="swiper" class="carousel">
			<div class="carousel_img">
				<img class="active" src="img/1.jpg" />
				<img src="img/2.jpg" />
				<img src="img/3.jpg" />
			</div>
			<img src="img/3.jpg" />
			<button><</button>
			<button>></button>
			<ul>
				<li dy="0" class="active"></li>
				<li dy="1"></li>
				<li dy="2"></li>
			</ul>
			var btns=document.getElementsByTagName("button"),
			car_img=document.getElementsByClassName("carousel_img")[0],
			lis=document.getElementsByTagName("li"),
			j=0;//计数
			for(var i in btns){
				btns[i].onclick=function(){
					if(this.innerText==">"){
						animate(1);
					
					}else{
						animate(-1);
					
					}
				}
			}
			for(var i in lis){
				lis[i].onclick=function(){
					animate(0,this);
				}
			}
			timer=setInterval(function(){
				animate(1);
			},3000)
			swiper.onmouseenter=function(){
				clearInterval(timer);
			}
			swiper.onmouseleave=function(){
				timer=setInterval(function(){
					animate(1);
				},3000)
			}

			function animate(){
				if(arguments[0]){
					j+=arguments[0];
					if(j==lis.length){j=0}else if(j==-1){j=lis.length-1}
				}else{
					j=parseInt(arguments[1].getAttribute("dy"));
				}
				
				for(var i=0;i<lis.length;i++){
					lis[i].className="";
				}
				car_img.style.left=j*-100+"%";
				lis[j].className="active";
			}
		</script>

防抖节流-定时器

	d1.onmousemove=function(){
				fdjl();//内层函数只要一旦移动就会触发
			}

			function f1(){
				var timer=null;//
				return function(){//
					if(timer){clearTimeout(timer);timer=null;}
					timer=setTimeout(function(){
						d1.innerHTML=parseInt(d1.innerHTML)+1
					},1000)
				}
			}
			
			var fdjl=f1();

防抖节流-input

			inp.oninput=function(){
				fdjl();//内层函数只要一旦移动就会触发
			}

			function f1(){
				var timer=null;//3
				return function(){//
					if(timer){clearTimeout(timer);timer=null;}
					timer=setTimeout(function(){
						console.log(inp.value);
					},1000)
				}
			}
			
			var fdjl=f1();

防抖节流-窗口改变大小

	window.onresize=function(){
				fdjl();//内层函数只要一旦移动就会触发
			}

			function f1(){
				var timer=null;//3
				return function(){//
					if(timer){clearTimeout(timer);timer=null;}
					timer=setTimeout(function(){
						if(innerWidth>=1200){
							d1.style.background="pink";
						}else{
							d1.style.background="purple";
						}
					},1000)
				}
			}
			
			var fdjl=f1();

Object对象

面向对象:三大特点:封装、继承、多态

开发方式

面向对象和面向过程?
面向过程:经过-开始-结束
面向对象:对象(属性和方法),JS有一句话,万物皆对象
为什么要使用面向对象:现实中所有的数据都必须包含在一个事物中才有意义
何时使用面向对象:以后做任何操作都要封装在一个对象中

封装

创建自定义对象:3种方法

直接量方式

var obj = {
            "属性名":属性值,
            ....
            "方法名":function(){},
            ....
}
强调:1、其实属性名和方法名的""可以不加
     2、访问对象的属性和方法
         obj.属性名
         obj.方法名()
         JS种一切都是对象,一切对象的底层都是hash数组
     3、访问到不存在的属性,返回undefined
     4、可以随时随地的添加新属性和新方法
     5、如果我希望遍历出对象所有的东西,使用for in , obj[i]才能拿到 -不要用,会出问题
     6、如果你希望在对象的方法里使用对象自己的属性:写为this.属性名

this的指向

1、单个元素绑定事件:this->单个元素
2、多个元素绑定事件:this->当前触发的元素
3、函数中使用thisthis->谁在调用此方法,this指的就是谁
4、定时器中this->window

预定义构造函数方式

var obj = new Object();//空对象,需要自己后续慢慢追加属性和方法
    obj.属性名 = 属性值
    obj.方法名 = function(){}

以上方法都有一个缺陷:一次只能创建一个对象,适合创建单个对象的时候使用

自定义构造函数方式

创建自定义构造函数

function 类名(name,age,salary){
        this.name=name;
        this.age = age;
        this.salary = salary;
}
//千万不要在里面创建方法,每个对象都会创建一个方法,浪费内存

调用构造函数创建出对象

var obj = new 类名(实参1,...)

继承

父对象的成员(属和方法),子对象可以直接使用
为什么:代码重用,节约内存空间,提升网站性能
何时继承:只要多个子对象公用的属性和方法,都要集中定义在父对象中

继承具有非常多的笔试题

1、判断自有还是共有
  判断自有:obj.hasOwnPropety("属性名");
     如结果为true,说明是自有属性,如果结果为false,有两种可能,共有或者没有
  判断共有:
      if(obj.hasOwnPropety("属性名")==fasle && "属性名"in obj){// in 会在obj的原型上进行查找
          共有
      }else{
          没有
      }
公式:
        if(obj.hasOwnPropety("属性名")){
            console.log("自有")
        }else{
            if("属性名"in obj){
                console.log("共有")
            }else{
                console.log("没有")
            }
        }

2、删除自有和共有
    自有:修改:obj.属性名 = 新值;
          删除:delete obj.属性名;
    共有:修改:obj.共有属性名 = 新值; - 只是在本地添加了一个同名属性
          删除:delete obj.属性名; - 无效,本地本来就没有此自有属性
          找到原型,修改/删除原型

3、如何为老IE的数组添加indexOf
   原理:
       if(Array.prototype.indexOf===undefined){//老IE
		Array.prototype.indexOf=function(key,starti){
			starti==undefined&&(starti=0);
			for(var i=starti;i<this.length;i++){
				if(this[i]==key){
					return i;
				}
			}
					return -1;
		}
	}

4、如何判断x是不是一个正则:4种方法,千万别用typeof,只能检查原始类型,不能检查引用类型
    1、判断x是否继承Array.prototype
        Array.prototype.isPrototypeOf(x);//
    2、判断x是不是由Array这个构造函数创建
        x instanceof Array;
    3Array.isArray(x); - ES5提供的,只是ES5+,老IE不支持,此方法只有数组
    4、输出【对象的字符串】形式
        在Object的原型上保存着最原始的toString方法
        原始的toString输出形式:[object 构造函数名]
	多态:子对象觉得父对象的成员不好,在本地定义了同名成员,覆盖了父对象的成员
	固定套路:借用:Object.prototype.toString.apply(x);
	if(Object.prototype.toString.apply(arr)=="[object Array]"){
			console.log("是数组")
		}else{
			console.log("不是数组")
		}

5、实现自定义继承:
	1、两个对象之间设置继承:
		子对象.__proto__=父对象
			
	2、多个对象之间设置继承:
		构造函数名.prototype=父对象
		时机:应该在开始创建对象之前设置好继承关系

如何找到父对象(原型对象):保存一类子对象共有属性和共有方法的父对象

1、__proto__;//必须要先有一个对象
2、构造函数名.prototype;//构造函数名,几乎人人都有,除了Math

面试题

两链一包:作用域链、原型链、闭包
每个对象都有一个属性:__proto__,可以一层一层的找到每个人的父亲,形参的一条链式结构,就称之为原型链,可以找到所有父对象的成员(属性和方法),作用:找共有属性和共有方法的
最顶层是Object的原型,上面放着toString,怪不得人人都可以用到toString();
JS万物皆对象

原型对象,设置共有的属性和方法

1、原型对象.属性名 = 属性值;//添加了一个共有属性
2、原型对象.方法名 = function(){};//添加了一个共有方法