『前端学习笔记』 JavaScript 函数进阶与正则表达式

本文深入探讨JavaScript函数的高级特性,包括this的call()、apply()、bind()用法,严格模式,高阶函数,闭包及其应用场景。同时,介绍了正则表达式的创建、语法、组成字符及预定义类,结合实例讲解正则验证和字符串替换操作。

参考视频:2019全新javaScript进阶面向对象ES6

参考文档:ES6入门教程





函数进阶


定义和调用

  • 函数定义:
	//自定义函数:命名函数
	function f1(){
		console.log("f1");
	}
	f1();
	f1.call();
	
	//函数表达式:匿名函数
	var f2 = function(){
		console.log("f2");
	}
	f2();

	//利用new————'参数'(列表) + '函数体'
	var f31 = new Function("console.log('f31')");
	f31();
	var f32 = new Function("a", "b", "console.log(a + b)");
	f32(1, 2);

第三种效率低,慎用。


  • 函数实例、函数、对象:
	//所有函数实例,都是Function的实例对象
	console.dir(f1);
	
	//Function属于Object
	console.log(f1 instanceof Object); //true
	console.log(Function instanceof Object); //true

  • 函数调用方式:
	//普通函数
	function f1(){console.log("f1");}
	f1();
	f1.call();
	
	//对象的方法
	var o2 = {
		f2: function(){console.log("f2");}
	}
	o2.f2();
	
	//构造函数
	function f3(){};
	new f3();
	
	//绑定事件函数
	var btn1 = document.getElementById("b1");
	btn1.onclick = function(){};
	
	//定时器函数
	setInterval(function(){}, 3000); //每3秒调用一次
	setTimeOut(function(){}, 3000); //3秒后执行
	
	//立即执行函数
	(function(){})();


this:指向调用者

  1. 结论:
    在这里插入图片描述

  2. 测试:

	//普通函数:指向window
	function f1(){console.log('普通函数 ' + this);}
	window.f1();	
	
	//对象的方法:指向Object
	var o2 = {
		f2: function(){console.log('对象方法 ' + this);}
	}
	o2.f2();
	
	//构造函数:指向Object
	function f3(){console.log('构造函数 ' + this);};
	new f3();
	
	//绑定事件函数:指向HTMLButtonElement
	var btn1 = document.getElementById("b1");
	btn1.onclick = function(){console.log('绑定事件函数 ' + this);};
	
	//定时器函数:指向window
	window.setTimeout(function(){console.log('定时器函数 ' + this);}, 1000); //3秒后执行
	
	//立即执行函数:指向window
	(function(){console.log('立即执行函数 ' + this);})();
  1. 代码结果:
    在这里插入图片描述

call()

  • 调用函数
  • 修改this指向
  • 后续参数是原函数的参数
  1. 使用call()修改this指向:
	//函数
	function fn(){console.log(this);}
	//定义新对象
	var obj = {name: 'newobj'};
	
	//直接调用
	fn.call();
	//参数1:修改this指向
	fn.call(obj);
	
	//参数2-参数n:函数参数
	function fn2(a, b){console.log(this);console.log(a+b);}
	fn2.call(obj, 1, 2);
  1. 代码结果:
    在这里插入图片描述

  2. 使用call()实现继承:

	function Father(name){
		this.name = name;
	}

	function Son(name){
		Father.call(this, name); //调用Father构造函数,this改成Son的this
	}
	
	//测试
	var t = new Son("SonName");
	console.dir(t);

apply()

  • 调用函数
  • 修改this指向
  • 后续参数是原函数的参数的数组形式
  1. 使用apply()修改this指向:
	//函数
	function fn(){console.log(this);}
	//定义新对象
	var obj = {name: 'newobj'};
	
	//不带参数:直接调用
	fn.apply();
	//参数1:修改this指向
	fn.apply(obj);
	
	//参数2:数组,函数全部参数
	function fn2(a, b){console.log(this);console.log(a+b);}
	fn2.apply(obj, [1, 2]);
  1. 使用apply()简化数组操作:
	var arr = [9, 32, 6, 28, 1];
	var mymax = Math.max.apply(null, arr); //无需指向this
	var mymax2 = Math.max.apply(Math, arr); //理论上应该指向Math
	console.log(mymax);
	console.log(mymax2);

bind()

  • 调用函数,返回修改后的拷贝
  • 修改this指向
  • 后续参数是原函数的参数
  1. 使用bind()修改this指向:
	//函数
	function fn(){console.log(this);}
	//定义新对象
	var obj = {name: 'newobj'};
	
	//参数1:修改this指向
	var f = fn.bind(obj); //必须使用变量承接 无参数时无效
	f(); //使用
	
	//参数2-参数n:函数参数
	function fn2(a, b){console.log(this);console.log(a+b);}
	var f2 = fn2.bind(obj, 1, 2);
	f2();

  1. 使用bind()做按钮功能:点击后禁用,3秒后恢复

普通做法:

	//普通做法1:使用DOM直接操作
	var btn1 = document.getElementById("b1");
	btn1.onclick = function(){ //点击按钮
		this.disabled = true; //禁用按钮 //this指向button
		setTimeout(function(){
			//this.disabled = false; //this指向window
			btn1.disabled = false; //正确
		}, 3000);
	}
	//普通做法2:使用变量承接button的this
	var btn1 = document.getElementById("b1");
	btn1.onclick = function(){ //点击按钮
		this.disabled = true; //禁用按钮 //this指向button
		var that = this;
		setTimeout(function(){
			that.disabled = false;
		}, 3000);
	}

使用bind()

	//更改this指向做法:使用bind()
	var btn1 = document.getElementById("b1");
	btn1.onclick = function(){ //点击按钮
		this.disabled = true; //禁用按钮 //this指向button
		setTimeout(function(){
			this.disabled = false; //this修改为指向外层this,即button
		}.bind(this), 3000);
	}

多按钮情况:

	//多按钮情况:使用bind() 增加this的使用价值
	var btns = document.querySelectorAll('button');
	var len = btns.length;
	for(let i = 0; i < len; i++)
		btns[i].addEventListener("click", function(){
			this.disabled = true; //禁用按钮 //this指向button
			setTimeout(function(){
				this.disabled = false; //this修改为指向外层this,即button
			}.bind(this), 3000);
		})

结论:bind()用途特别广泛,因为其调用时不执行,且能改变this指向。
具体应用例如:在this指向不同的函数之间过渡使用时,用bind()强行将this指回。



严格模式

在严格的条件下运行JS代码。

  • 使用方式:"use strict"
  1. 对脚本使用(引用参考文档):
"use strict";
x = 3.14;       // 这会引发错误,因为 x 尚未声明
  1. 对函数使用(引用参考文档):
x = 3.14;       // 这不会引发错误
myFunction();

function  myFunction() {
	"use strict";
	 y = 3.14;   // 这会引发错误
}

  • 更改:
  1. 参考文档:JavaScript Use Strict

  2. 全局函数this指向从window变为undefined

<script>
	(function(){console.log(this)})(); //window
</script>

<script>
	"use strict";
	(function(){console.log(this)})(); //undefined
</script>

对象方法和构造函数依然指向Object
定时器方法依然指向Window
绑定事件方法依然指向调用对象

  1. 构造函数不加new调用会报错:
<script>
	function F(){ //构造函数
		this.name = "myname";
	}
	F(); //正常
</script>

<script>
	"use strict";
	function F(){ //构造函数
		this.name = "myname";
	}
	F(); //报错
</script>
  1. 不推荐函数写在非函数代码块:

iffor中不推荐写function

<script>
	if(true){
		function f1(){console.log(this)};
		f1(); //window
	}
	for(let i = 0; i < 1; i++) {
		function f2(){console.log(this)};
		f2(); //window
	}
</script>

<script>
	"use strict";
	if(true){
		function f3(){console.log(this)};
		f3(); //undefined
	}
	for(let i = 0; i < 1; i++) {
		function f4(){console.log(this)};
		f4(); //undefined
	}
</script>

function中可以写function

<script>
	function a(){
		function b(){} //函数里可以定义函数
	}
</script>



高阶函数

函数也是一种数据类型

  • 参数是一个函数
	function f(val){
		val && val(); //回调函数 //jQuery常用
	}
	
	function g(){ 
		console.log(this);
	}
	
	f(g); //主函数调用时,将副函数作为参数传入
  • 返回值是一个函数
	function f(){
		return function(){console.log(this)};
	}
	
	//用法1
	f()();
	
	//用法2
	var t = f();
	t();



闭包

参考文档:JavaScript 闭包

JS两大难点:闭包、异步


概念和作用

闭包(closure):有权访问另一个函数作用域中的变量。主要作用是延伸变量的作用范围

  • 产生闭包:内部的作用域
	function f1(){
		var num = 1;
		function f2(){
			console.log(num); //可以访问num
		}
		f2();
	}
	f1();
  1. 验证:Chrome在最后一行打断点,观察Scope作用域
    在这里插入图片描述
  • 产生闭包:外部的作用域
	function f1(){
		var num = 1;
		function f2(){
			console.log(num); //可以访问num
		}
		return f2; //高阶函数,返回值是一个函数
	};
	
	var f = f1();
	f(); //可以访问num //可多次使用
  1. 等价写法:将函数变量赋值提到前面,但调用时要两个括号
	var f = function f1(){
		var num = 1;
		function f2(){
			console.log(num); //可以访问num
		}
		return f2; //高阶函数,返回值是一个函数
	};
	
	f()(); //可以访问num //可多次使用
  1. 等价写法:将函数变为自调用函数
	var f = (function f1(){
		var num = 1;
		function f2(){
			console.log(num); //可以访问num
		}
		return f2; //高阶函数,返回值是一个函数
	})();
	
	f(); //可以访问num //可多次使用
  1. 等价写法:将内部返回值函数改为匿名函数
	var f = (function f1(){
		var num = 1;
		return function(){
			console.log(num); //可以访问num
		}; //高阶函数,返回值是一个函数
	})();
	
	f(); //可以访问num //可多次使用



案例1:点击获取列表项索引号

  • 原写法
<ul class="mynav">
	<li>内容</li>
	<li>内容</li>
	<li>内容</li>
</ul>
<script>
	var mylis = document.querySelector(".mynav").querySelectorAll("li");
	
	var len = mylis.length;
	for(let i = 0; i < len; i++) {
		mylis[i].setAttribute("index", i); 
		//使用监听器:结果错误,因为是一个异步任务
		mylis[i].onclick = function(){ 
			//直接输出i:结果错误,因为是一个异步任务
			console.log(this.getAttribute("index")); 
		}
	}
</script>
  • 利用闭包获得索引号
<ul class="mynav">
	<li>内容</li>
	<li>内容</li>
	<li>内容</li>
</ul>
<script>
	var mylis = document.querySelector(".mynav").querySelectorAll("li");
	
	var len = mylis.length;
	for(let i = 0; i < len; i++) {
		//立即执行函数,把i作为参数传入,i便可以使用了
		(function(index){
			//此时也可以使用监听器写法
			mylis[index].onclick = function(){
				console.log(index);
			}
		})(i);
	}
</script>

使用立即执行函数,获得其它作用域的变量。

立即执行函数又被叫做小闭包

缺点:

  1. 不够简洁
  2. 内存空间使用过多
  3. 严格模式无法通过


案例2:立即执行函数

	var obj = (function(){
		var sum = 0;
		return {
			f1: function(str){console.log(str)},
			f2: function(str){return str;}
		}
	})();
	
	//返回值中定义的方法,可直接用
	obj.f1("myf");


思考题:输出结果与是否产生闭包

  • 题目1:
	var name = "In Window";
	var obj = {
		name: "In Object",
		getName: function(){
			return function(){
				return this.name;
			};
		}
	}
	
	console.log(obj.getName()());
  1. 输出结果为"In Window",因为相当于调用了一个自调用函数,其this指向Window
  2. 不产生闭包。

  • 题目2:
	var name = "In Window";
	var obj = {
		name: "In Object",
		getName: function(){
			var t = this; //新增
			return function(){
				return t.name; //更改
			};
		}
	}
	
	console.log(obj.getName()());
  1. 输出结果为"In Object",因为返回的t指向Object
  2. 产生闭包,在getName()


递归

  • 求n的阶乘
	function f(n){
		if(n > 1)
			return n * f(n - 1);
		else
			return 1;
	}
  • 求斐波那契数列第n项
	function f(n){
		if(n > 2)
			return f(n - 1) + f(n - 2);
		else
			return 1;
	}



拷贝


引用

  • 基本数据类型:引用值,修改副本时不会修改原值
	var obj1 = "str1"; //原值
	var obj2 = ""; //副本
	
	obj2 = obj1; //引用
	obj2 += "teststr2"; //修改副本,原值不会修改
  • 对象、数组:引用地址空间,修改副本时修改原值
	var obj1 = {id: 1, name:"s"}; //原值
	var obj2 = {}; //副本
	
	obj2 = obj1; //引用
	obj2.id = 2; //修改副本,原值也会修改



浅拷贝

只拷贝一层,更深层次对象级别的只拷贝引用。

  • for in 实现
	var obj1 = {id: 1, val:{value: 1}}; //原值
	var obj2 = {}; //副本
	
	for(let i in obj1){
		//i是属性名 obj1[i]相当于属性值
		obj2[i] = obj1[i];
	}

如果原值中某个属性是对象,那么循环中的直接赋值=就相当于引用

  • ES6语法糖Object.assign()
Object.assign(obj2, obj1); //浅拷贝

参数1:副本
参数2:原值



深拷贝

拷贝多层,每一层数据都拷贝。

  • 递归实现
	var obj1 = {id: 1, val:{value: 1}}; //原值
	var obj2 = {}; //副本
	
	function deepCopy(newobj, oldobj){
		for(let i in oldobj){
			/*获取循环中的当前属性(i)与属性值oldobj[i]/item*/
			let item = oldobj[i];
			/*当前元素=数组,不可以直接赋值*/
			if(item instanceof Array){ //Array也属于Object的子集,故而提前
				/*创建空数组*/
				newobj[i] = []; 
				/*对当前元素=数组,进行递归*/
				deepCopy(newobj[i], item);
			}
			/*当前元素=对象,不可以直接赋值*/
			else if(item instanceof Object){
				/*创建空对象*/
				newobj[i] = {}; 
				/*对当前元素=对象,进行递归*/
				deepCopy(newobj[i], item);
			}
			/*当前元素=其它(简单数据类型),可以直接赋值*/
			else{ //函数类型:暂时未考虑
				/*直接赋值*/
				newobj[i] = item;
			}
			
		}
	}
	
	deepCopy(obj2, obj1);



正则表达式

匹配字符串中的字符组合,是对象的一种。

参考文档:JavaScript 正则表达式



验证工具与参考

参考文档:正则表达式在线测试 | 菜鸟工具


创建

  • 使用:字面量
	var reg1 = /456/;
  • 使用:RegExp对象
	var reg2 = new RegExp(/123/);



语法

  • 规范
  1. 包含在//之间
  2. No:不能为空,否则会变成注释//
  3. No:正则表达式内部不需要加引号"
  • 验证方法正则对象.test(str):目标字符串是否符合
	console.log(/123/.test(1)); //false
	console.log(/123/.test(123)); //true
	console.log(/123/.test("123")); //true
	console.log(/123/.test(1234)); //true
	console.log(/123/.test(1123423)); //true



组成字符

  1. 简单字符:1、a、A等
  2. 特殊字符(元字符):+、*、$、^等
    参考文档:正则表达式 - JavaScript | MDN


边界符:字符的位置

  • 相关字符
字符说明
^匹配开头(放在开头)
$匹配结尾(放在结尾)
^ $精准匹配
  • 测试代码
	var reg_start = /^abc/; // ^匹配开头(放在开头)
	var reg_end = /abc$/; // $匹配结尾(放在结尾)
	var reg =  /^abc$/; //精确匹配
	
	abc_start = "abc" + "zzz"; //以abc开头
	abc_end = "zzz" + "abc"; //以abc结尾
	abc = "abc"; //精确的abc字符串
	
	console.log(reg_start.test(abc_start)); //true
	console.log(reg_start.test(abc_end)); //false
	console.log(reg_start.test(abc)); //true
	
	console.log(reg_end.test(abc_start)); //false
	console.log(reg_end.test(abc_end)); //true 
	console.log(reg_end.test(abc)); //true 
	
	console.log(reg.test(abc_start)); //false
	console.log(reg.test(abc_end)); //false
	console.log(reg.test(abc)); //true 



字符类:包含的字符

  • 相关字符
字符说明
[ ]一系列字符可供选择,匹配其中一个就可以
-范围符,指定取值范围
^(在[ ]之内)取反
  • 测试代码
	var reg = /[abc]/; //匹配a,b,c其中之一
	
	console.log(reg.test("123")); //false
	console.log(reg.test("123A")); //false
	console.log(reg.test("123a")); //true
	console.log(reg.test("123abc")); //true
	console.log(reg.test("a")); //true
	var reg1 = /[a-c]/; //等价于/[abc]/
	var reg2 = /[a-z]/; //等价于/[abcd……xyz]/

  • 字符类+边界符:边界(开头/结尾)匹配其中之一
	var reg1 = /^[a-c]/; //开头匹配a,b,c
	var reg2 = /[a-z]$/; //结尾匹配a,b,c
  • 字符类+精准匹配:精准匹配其中之一
	var reg = /^[a-c]$/; //精准匹配a,b,c 必须完全相同

  • 字符类取反:
	var reg = /[^a-c]/; //结果取反
  • 字符类取反+边界符:
	var reg1 = /[^a-c]/; //所有字符都匹配成功,返回false
	var reg2 = /^[^a-c]$/; //任何一个字符匹配成功,返回false
	var reg3 = /^[^a-c]/; //开头字符匹配成功,返回false
	var reg4 = /[^a-c]$/; //结尾字符匹配成功,返回false

  • 字符类组合:
	var reg1 = /[a-zA-Z]/; //添加范围
	var reg2 = /[a-zA-Z0-9]/; //再添加范围
	var reg3 = /[a-zA-Z0-9_-]/; //添加字符



量词符:出现次数

  • 相关字符
字符说明
( )优先级
*重复次数>=0
+重复次数>=1
?重复次数==0或1
{n}重复次数==n
{n,}重复次数>=n
{n, m}重复次数>=n且<=m
  • 测试代码
	var reg1 = /^a*$/; //*:出现次数>=0时返回true
	var reg2 = /^a+$/; //+:出现次数>=1时返回true
	var reg3 = /^a?$/; //?:出现次数==0或==1时返回true
	
	var reg4 = /^a{3}$/; //出现次数==3时返回true
	var reg5 = /^a{3,}$/; //出现次数>=3时返回true
	var reg6 = /^a{3,5}$/; //出现次数>=3且<=5时返回true
	//不能有空格

  • 量词符+边界符:边界出现次数
	var reg1 = /a*/; //*:出现次数>=0时返回true
	var reg2 = /^a*/; //*:开头出现次数>=0时返回true
	var reg3 = /a*$/; //*:结尾出现次数>=0时返回true
	var reg4 = /^a*$/; //*:精准出现次数>=0时返回true

  • 量词符+小括号:优先级提升
	var reg1 = /abc{2}/; //匹配abcc //就近原则
	var reg2 = /(abc){2}/; //匹配abcabc //小括号提高优先级

  • 量词符应用:定义某个模式出现的次数
	var reg1 = /^[a-zA-Z0-9_-]$/; //只可出现一次
	var reg2 = /^[a-zA-Z0-9_-]{6,16}$/; //出现指定次数



预定义类

一些常见模式的简写

  • 相关字符
预定义类等价表达式说明
\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]匹配非空格类的字符
  • 测试代码
	var reg1 = /\d/; //0-9
	var reg2 = /\D/; //非0-9
	var reg3 = /\w/; //A-Za-z0-9_
	var reg4 = /\W/; //非A-Za-z0-9_
	var reg5 = /\s/; //\t\r\n\v\f
	var reg6 = /\S/; //非\t\r\n\v\f



相关案例

  • 用户名验证
	var reg = /^\w{6,16}$/;
  • 座机号码验证
	//010-12345678
	//0100-1234567
	var reg = /^\d{3}-\d{8}|\d{4}-\d{7}$/;
	//前3-4,后7-8
	var reg = /^\d{3,4}-\d{7,8}$/;



参数

  • 参数
参数作用
g全局匹配
i忽略大小写
gi全局匹配,忽略大小写
  • 使用方法
	var reg1 = /\d/g;
	var reg2 = /\d/i;
	var reg3 = /\d/gi;



替换字符串

replace()方法:替换字符串或正则表达式

  • 普通方法
	var s1 = "hello world";
	var s2 = s1.replace("world", "javascript"); //old, new
	console.log(s2); //answer: "hello javascript"

  • 使用正则表达式进行单次匹配
	var s1 = "hello world";
	var s2 = s1.replace(/\s/, "-"); //old, new
	console.log(s2); //answer: "hello-world"

  • 使用正则表达式参数进行全局匹配
	var s1 = "hello world";
	var s2 = s1.replace(/\w/g, "-"); //old, new
	console.log(s2); //answer: "----- -----"
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大熊软糖M

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值