参考视频: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:指向调用者
-
结论:

-
测试:
//普通函数:指向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);})();
- 代码结果:

call()
- 调用函数
- 修改
this指向 - 后续参数是原函数的参数
- 使用
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);
-
代码结果:

-
使用
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指向 - 后续参数是原函数的参数的数组形式
- 使用
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]);
- 使用
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指向 - 后续参数是原函数的参数
- 使用
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();
- 使用
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"
- 对脚本使用(引用参考文档):
"use strict";
x = 3.14; // 这会引发错误,因为 x 尚未声明
- 对函数使用(引用参考文档):
x = 3.14; // 这不会引发错误
myFunction();
function myFunction() {
"use strict";
y = 3.14; // 这会引发错误
}
- 更改:
-
全局函数
this指向从window变为undefined:
<script>
(function(){console.log(this)})(); //window
</script>
<script>
"use strict";
(function(){console.log(this)})(); //undefined
</script>
对象方法和构造函数依然指向Object。
定时器方法依然指向Window。
绑定事件方法依然指向调用对象。
- 构造函数不加
new调用会报错:
<script>
function F(){ //构造函数
this.name = "myname";
}
F(); //正常
</script>
<script>
"use strict";
function F(){ //构造函数
this.name = "myname";
}
F(); //报错
</script>
- 不推荐函数写在非函数代码块:
if和for中不推荐写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();
- 验证:Chrome在最后一行打断点,观察Scope作用域

- 产生闭包:外部的作用域
function f1(){
var num = 1;
function f2(){
console.log(num); //可以访问num
}
return f2; //高阶函数,返回值是一个函数
};
var f = f1();
f(); //可以访问num //可多次使用
- 等价写法:将函数变量赋值提到前面,但调用时要两个括号
var f = function f1(){
var num = 1;
function f2(){
console.log(num); //可以访问num
}
return f2; //高阶函数,返回值是一个函数
};
f()(); //可以访问num //可多次使用
- 等价写法:将函数变为自调用函数
var f = (function f1(){
var num = 1;
function f2(){
console.log(num); //可以访问num
}
return f2; //高阶函数,返回值是一个函数
})();
f(); //可以访问num //可多次使用
- 等价写法:将内部返回值函数改为匿名函数
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>
使用立即执行函数,获得其它作用域的变量。
立即执行函数又被叫做小闭包。
缺点:
- 不够简洁
- 内存空间使用过多
- 严格模式无法通过
案例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()());
- 输出结果为"In Window",因为相当于调用了一个自调用函数,其
this指向Window。 - 不产生闭包。
- 题目2:
var name = "In Window";
var obj = {
name: "In Object",
getName: function(){
var t = this; //新增
return function(){
return t.name; //更改
};
}
}
console.log(obj.getName()());
- 输出结果为"In Object",因为返回的
t指向Object。 - 产生闭包,在
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/);
语法
- 规范
- 包含在
/和/之间 - No:不能为空,否则会变成注释
// - 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、a、A等
- 特殊字符(元字符):+、*、$、^等
参考文档:正则表达式 - 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: "----- -----"
本文深入探讨JavaScript函数的高级特性,包括this的call()、apply()、bind()用法,严格模式,高阶函数,闭包及其应用场景。同时,介绍了正则表达式的创建、语法、组成字符及预定义类,结合实例讲解正则验证和字符串替换操作。
206

被折叠的 条评论
为什么被折叠?



