【学习前提】
完成基于ECharts数据可视化项目
【阶段说明】
本阶段主要是针对JS高级的一个学习。学习的主要内容有:
JS知识的补充与拓展
ES6语法
【学习时长】
3天
【学习技巧】
本阶段的学习难点在于对“闭包”的理解。
一、函数进阶
一、函数的定义和调用
1、定义方式
方式一:函数声明方式 function 关键字(命名函数)
function fn(){}
方式二:函数表达式(匿名函数)
var fn = function(){}
方式三:new Function()
var fn = new Function('参数1','参数2'..., '函数体')
注意:
Function 里面参数都必须是字符串格式;
第三种方式执行效率低,也不方便书写,因此较少使用;
所有函数都是 Function 的实例(对象);
函数也属于对象。
2、调用方式
1. 普通函数
function fn() {
console.log('人生的巅峰');
}
fn(); // fn.call();
2. 对象的方法
var o = {
sayHi: function() {
console.log('人生的巅峰');
}
}
o.sayHi();
3. 构造函数
function Star() {};
new Star();
4. 绑定事件函数
btn.onclick = function() {}; // 点击了按钮就可以调用这个函数
5. 定时器函数
setInterval(function() {}, 1000); // 这个函数是定时器自动1秒钟调用一次
6. 立即执行函数(自调用函数)
(function() {
console.log('人生的巅峰');
})();
二、this
1、函数内 this 的指向
这些 this 的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this 的指向不同,一般指向我们的调用者。
调用方式 | this 的指向 |
---|---|
普通函数调用 | window |
对象方法调用 | 实例对象 原型对象里面的方法也指向实例对象 |
构造函数调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
立即执行函数 | window |
2、改变函数内部 this 的指向
1. call 方法
call() 方法调用一个对象。简单理解为调用函数的方式,但它可以改变函数的 this 指向。
应用场景:实现继承
代码示例:
var o = {
name: 'andy'
}
function fn(a, b) {
console.log(this);
console.log(a+b)
};
fn(1,2); // 此时的this指向的是window 运行结果为3
fn.call(o,1,2); // 此时的this指向的是对象o,参数使用逗号隔开,运行结果为3
// call 的主要作用 可以实现继承
function Father (uname, age, sex) {
this.uname = uname;
this.age = age;
this.sex = sex;
}
function Son (uname, age, sex) {
Father.call(this, uname, age, sex);
}
var son = new Son('刘德华', 18, '男');
console.log(son);
2. apply 方法
apply() 方法调用一个函数。简单理解为调用函数的方式,但它可以改变函数的 this 指向。
应用场景:经常跟数组有关系
代码示例:
var o = {
name: 'andy'
}
function fn(a, b) {
console.log(this);
console.log(a+b)
};
fn(); // 此时的this指向的是window 运行结果为3
fn.apply(o,[1,2]); // 此时的this指向的是对象o,参数必须使用(伪)数组传递 运行结果为3
// apply 的主要应用 比如利用 apply 借助Math内置对象求最大值、最小值
var arr = [1,22,34,555,43,7];
// var max = Math.max.apply(null, arr);
var max = Math.max.apply(Math, arr);
var min = Math.min.apply(Math, arr);
console.log(max,min);
3. bind 方法
bind() 方法不会调用函数,但是能改变函数内部 this 指向。
应用场景:不调用函数,但是还想改变this指向
代码示例:
var o = {
name: 'andy'
};
function fn(a, b) {
console.log(this);
console.log(a + b);
};
var f = fn.bind(o, 1, 2); // 此处的f是bind返回的新函数
f(); // 调用新函数 this指向的是对象o 参数使用逗号隔开
// 例:按钮点击后禁用,3秒后又开启这个按钮
var btn = document.querySelector('button');
btn.onclick = function() {
this.disabled = true; // 这个 this 指向的是 btn 按钮
setTimeout(function() {
this.disabled = false; // 此时定时器的 this 指向的不再是 window,而是 btn
}.bind(this),3000) // 绑定 bind 方法使 this 指向 btn 这个对象
}
4.总结
相同点:都可以改变函数内部的this指向
区别:
①call 和 apply 会调用函数,并且改变函数内部this指向;
②call 和 apply 传递的参数不一样,call 传递参数 aru1,aru2..形式 apply 必须数组形式[arg];
③bind 不会调用函数,可以改变函数内部 this 指向。
主要应用场景:
①call 经常做继承;
②apply 经常跟数组有关系。比如借助于数学对象实现数组最大值最小值;
③bind 不调用函数,但是还想改变this指向。比如改变定时器内部的this指向。
三、严格模式
1、什么是严格模式?
JavaScript 除了提供正常模式外,还提供了严格模式(strict mode)。ES5 的严格模式是采用具有限制性 JavaScript 变体的一种方式,即在严格的条件下运行 JS 代码。
严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
严格模式对正常的 JavaScript 语义做了一些更改:
- 消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为;
- 消除代码运行的一些不安全之处,保证代码运行的安全;
- 提高编译器效率,增加运行速度;
- 禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫。比如一些保留字如:class, enum, export, extends, import, super 不能做变量名
2、开启严格模式
严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况。
情况一:为脚本开启严格模式
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句“use strict”;(或‘use strict’;)
<script>
(function (){
// 在当前的这个自调用函数中有开启严格模式,当前函数之外还是普通模式
"use strict";
var num = 10;
function fn() {}
})();
</script>
// 或者
<script>
"use strict"; // 当前script标签开启了严格模式
</script>
<script>
// 当前script标签未开启严格模式
</script>
因为"use strict"加了引号,所以老版本的浏览器会把它当作一行普通字符串而忽略。
情况二:为函数开启严格模式
要给某个函数开启严格模式,需要把“use strict”; (或 'use strict'; ) 声明放在函数体所有语句之前
function fn(){
"use strict";
return "这是严格模式。";
}
//当前fn函数开启了严格模式
将 "use strict" 放在函数体的第一行,则整个函数以 "严格模式" 运行。
3、严格模式中的变化
1.变量规定
①在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用var 命令声明,然后再使用;
②严禁删除已经声明变量。例如,delete x; 语法是错误的。
2.this 指向问题
①以前在全局作用域函数中的 this 指向 window 对象;
②严格模式下全局作用域中函数中的 this 是 undefined;
③以前构造函数时不加 new 也可以调用,当普通函数,this 指向全局对象;
④严格模式下,如果 构造函数不加 new 调用,this 指向的是 undefined 如果给他赋值则会报错;
⑤new 实例化的构造函数指向创建的对象实例;
⑥定时器 this 还是指向 window ;
⑦事件、对象还是指向调用者。
3.函数变化
①函数不能有重名的参数;
②函数必须声明在顶层。新版本的 JavaScript 会引入“块级作用域”( ES6 中已引入)。为了与新版本接轨不允许在非函数的代码块内声明函数。
4.更多严格模式要求参考
四、高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。
<script>
function fn(callback){
callback&&callback();
}
fn(function(){alert('hi')}
</script>
<script>
function fn(){
return function() {}
}
fn();
</script>
此时 fn 就是一个高阶函数;
函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。同理函数也可以作为返回值传递回来。
五、闭包和递归
1、闭包
1.变量的作用域复习
变量根据作用域的不同分为两种:全局变量和局部变量
①函数内部可以使用全局变量;
②函数外部不可以使用局部变量;
③当函数执行完毕,本作用域内的局部变量会销毁。
2.什么是闭包?
闭包(closure)指有权访问另一个函数作用域中变量的函数。简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。
<script>
function fn1(){ // fn1 就是闭包函数
var num = 10;
function fn2(){
console.log(num); // 10
}
fn2();
}
fn1();
</script>
在 chrome 中调试闭包:
- 打开浏览器,按 F12 键启动 chrome 调试工具。
- 设置断点。
- 找到 Scope 选项(Scope 作用域的意思)。
- 当我们重新刷新页面,会进入断点调试,Scope 里面会有两个参数(global 全局作用域、local 局部作用域)。
- 当执行到 fn2() 时,Scope 里面会多一个 Closure 参数 ,这就表明产生了闭包。
3.闭包的作用
作用:延伸变量的作用范围。
提问:我们怎么能在 fn() 函数外面访问 fn() 中的局部变量 num 呢 ?
<script>
function fn() {
var num = 10;
return function {
console.log(num); // 10
}
}
var f = fn();
f()
</script>
4.闭包案例
// 1、利用闭包的方式得到当前li 的索引号
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
// 利用for循环创建了4个立即执行函数
// 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这个变量
(function (i) {
lis[i].onclick = function () {
console.log(i);
}
})(i);
};
// 2、闭包应用-3秒钟之后,打印所有li元素的内容
for (var i = 0; i < lis.length; i++) {
(function (i) {
setTimeout(function () {
console.log(lis[i].innerHTML);
}, 3000)
})(i);
};
// 3、闭包应用-计算打车价格
// 需求分析
// 打车起步价13(3公里内), 之后每多一公里增加 5块钱. 用户输入公里数就可以计算打车价格
// 如果有拥堵情况,总价格多收取10块钱拥堵费
var car = (function () {
var start = 13; // 起步价 局部变量
var total = 0; // 总价 局部变量
return {
// 正常的总价
price: function (n) {
if (n <= 3) {
total = start;
} else {
total = start + (n - 3) * 5;
}
return total;
},
// 拥堵之后的费用
jam: function (flag) {
return flag ? total + 10 : total;
}
}
})();
console.log(car.price(5)); // 23
console.log(car.jam(true)); // 33
console.log(car.price(1)); // 13
console.log(car.jam(false)); // 13
2、递归
递归:如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己,,这个函数就是递归函数。
注意:递归函数的作用和循环效果一样,由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return。
案例:
<!-- 例1: -->
<script>
// 利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n
function fn(n) {
if (n == 1) { // 结束条件
return 1;
}
return n * fn(n - 1);
}
console.log(fn(3));
// 详细思路 用户输入 3
// return 3 * fn(3 - 1)
// return 3 * (2 * fn(2 - 1))
// return 3 * (2 * 1)
// return 3 * (2)
// return 6
</script>
<!-- 例2: -->
<script>
// 利用递归函数求斐波那契 Fibonacci 数列(兔子序列) 1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
// 我们只需要知道用户输入的 n 的前面两项(n-1 n-2)就可以计算出 n 对应的序列值
function fb(n) {
if (n === 1 || n === 2) {
return 1;
}
return fb(n - 1) + fb(n - 2);
}
console.log(fb(4));
</script>
<!-- 例3: -->
<script>
// 辗转相除法
function measure(a, b) {
var n = a % b // 取余数
if (n == 0) { // 判断余数是否为0,为0输出b为最大公约数
return b;
} else {
a = b; // 将上一轮的b的值给a
b = n; // 将上一轮的n的值给b
return measure(a, b); // 递归 继续调用函数直到余数n为0
}
}
console.log(measure(9, 18));;
</script>
<!-- 例4: -->
<script>
// 我们想要做输入id号,就可以返回的数据对象
var data = [{
id: 1,
name: '家电',
goods: [{
id: 11,
gname: '冰箱',
goods: [{
id: 111,
gname: '海尔'
}, {
id: 112,
gname: '美的'
}]
}, {
id: 12,
gname: '洗衣机'
}]
}, {
id: 2,
name: '服饰'
}];
// 1.利用 forEach 去遍历里面的每一个对象
function getID(json, id) {
var o = {};
json.forEach(function (item) {
// console.log(item); // 2个数组元素
if (item.id == id) {
// console.log(item);
o = item;
return o;
// 2. 我们想要得里层的数据 11 12 可以利用递归函数
// 里面应该有goods这个数组并且数组的长度不为 0
} else if (item.goods && item.goods.length > 0) {
o = getID(item.goods, id);
}
});
return o;
}
console.log(getID(data, 1));
console.log(getID(data, 2));
console.log(getID(data, 11));
console.log(getID(data, 12));
console.log(getID(data, 111));
console.log(getID(data, 112));
</script>
<!-- 例5: -->
<body>
<button>赞(1)</button>
<button>赞(1)</button>
<button>赞(1)</button>
<button>赞(1)</button>
<script>
// 闭包缓存数据
function getValue() {
var value = 2;
return function () {
// 每一次点击的时候,都应该改变当前点击按钮的value值
this.innerHTML = "赞(" + (value++) + ")";
}
}
// 获取所有的按钮
var btnObjs = document.getElementsByTagName("button");
// 循环遍历每个按钮,注册点击事件
for (var i = 0; i < btnObjs.length; i++) {
// 注册事件
btnObjs[i].onclick = getValue();
}
</script>
</body>
二、正则表达式
一、概述
1、什么是正则表达式?
正则表达式( Regular Expression )是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象。
正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)。 此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等。
2、特点
- 灵活性、逻辑性和功能性非常的强。
- 可以迅速地用极简单的方式达到字符串的复杂控制。
- 对于刚接触的人来说,比较晦涩难懂。比如:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
- 实际开发,一般都是直接复制写好的正则表达式。但是要求会使用正则表达式并且根据实际情况修改正则表达式。 比如用户名: /^[a-z0-9_-]{3,16}$/
二、在JS中的使用
1、正则表达式的创建
方式一:通过调用 RegExp 对象的构造函数创建
var 变量名 = new RegExp(/表达式/);
var regexp = new RegExp(/123/);
console.log(regexp);
方式二:利用字面量创建
var 变量名 = /表达式/;
var rg = /123/;
2、测试正则表达式
test() 正则对象方法,用于检测字符串是否符合该规则,该对象会返回 true 或 false,其参数是测试字符串。
语法:
regexObj.test(str);
var rg = /123/;
console.log(rg.test(123)); // 匹配字符中是否出现123 出现结果为true
console.log(rg.test('abc')); // 匹配字符中是否出现123 未出现结果为false
regexObj 是写的正则表达式
str 是要测试的文本
就是检测 str 文本是否符合我们写的正则表达式规范
三、特殊字符
1、正则表达式的组成
一个正则表达式可以由简单的字符构成,比如 /abc/,也可以是简单和特殊字符的组合,比如 /ab*c/ 。其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如 ^ 、$ 、+ 等。
特殊字符非常多,可以参考:
jQuery 手册:正则表达式部分
2、边界符
正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符:
边界符 | 说明 |
---|---|
^ | 表示匹配行首的文本(以谁开始) |
$ | 表示匹配行尾的文本(以谁结束) |
如果 ^和 $ 在一起,表示必须是精确匹配。
var rg = /abc/; // 正则表达式里面不需要加引号 不管是数字型还是字符串型
// /abc/ 只要包含有abc这个字符串返回的都是true
console.log(rg.test('abc'));
console.log(rg.test('abcd'));
console.log(rg.test('aabcd'));
console.log('---------------------------');
var reg = /^abc/;
console.log(reg.test('abc')); // true
console.log(reg.test('abcd')); // true
console.log(reg.test('aabcd')); // false
console.log('---------------------------');
var reg1 = /^abc$/; // 精确匹配 要求必须是 abc字符串才符合规范
console.log(reg1.test('abc')); // true
console.log(reg1.test('abcd')); // false
console.log(reg1.test('aabcd')); // false
console.log(reg1.test('abcabc')); // false
3、字符类
字符类表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内。
1.[ ] 方括号
/[abc]/.test('andy') // true
// 后面的字符串只要包含 abc 中任意一个字符,都返回 true
2.[-] 方括号内部 范围符-
/^[a-z]$/.test('c') // true
// 方括号内部加上 - 表示范围,这里表示 a 到 z 26个英文字母都可以
3.[^] 方括号内部 取反符^
/[^abc]/.test('andy') // false
// 方括号内部加上 ^ 表示取反,只要包含方括号内的字符,都返回 false
注意和边界符 ^ 区别,边界符写到方括号外面。
4.字符组合
/[a-z1-9]/.test('andy') // true
// 方括号内部可以使用字符组合,这里表示包含 a 到 z 的26个英文字母和 1 到 9 的数字都可以
4、量词符
量词符用来设定某个模式出现的次数。
量词符 | 说明 |
---|---|
* | 重复0次或更多次 |
+ | 重复1次或更多次 |
? | 重复0次或1次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
5、括号总结
括号 | 说明 |
---|---|
{ } 大括号 | 量词符。里面表示重复次数 |
[ ] 中括号 | 字符集合。匹配方括号中的任意字符 |
( ) 小括号 | 表示优先级 |
6、预定义类
预定义类指的是某些常见模式的简写方式。
预定义类 | 说明 |
---|---|
\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] |
例:
手机号验证: /^1[3|4|5|7|8][0-9]{9}$/
QQ号验证: /^[1-9]\d{4,}$/
昵称验证: /^[\u4e00-\u9fa5]{2,8}$/
验证码验证 : /^\d{6}$/
密码验证: /^[a-zA-Z0-9_-]{6,16}$/
四、替换
1、replace 替换
replace() 方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。
语法:
stringObject.replace(regexp/substr,replacement)
// 第一个参数: 被替换的字符串或者正则表达式
// 第二个参数: 替换为的字符串
// 返回值是一个替换完毕的新字符串
2、参数
/表达式/[switch]
switch(也称为修饰符) 按照什么样的模式来匹配。有三种值:
g:全局匹配
i:忽略大小写
gi:全局匹配 + 忽略大小写
3、代码示例
var str = 'andy和red';
var newStr = str.replace('andy', 'baby');
console.log(newStr); // baby和red
// 等同于 此处的andy可以写在正则表达式内
var newStr2 = str.replace(/andy/, 'baby');
console.log(newStr2); // baby和red
// 全部替换
var str = 'abcabc';
var nStr = str.replace(/a/,'哈哈');
console.log(nStr); // 哈哈bcabc
// 全部替换g
var nStr = str.replace(/a/a,'哈哈');
console.log(nStr); // 哈哈bc哈哈bc
// 忽略大小写i
var str = 'aAbcAba';
var newStr = str.replace(/a/gi,'哈哈'); // "哈哈哈哈bc哈哈b哈哈"
过滤敏感词汇:
<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.onclick = function() {
div.innerHTML = text.value.replace(/激|情|gay/g, '**');
}
</script>
三、ES6
一、简介
1、什么是 ES6?
ES 的全称是 ECMAScript,它是由 ECMA 国际标准化组织,制定的一项脚本语言的标准化规范。ES6 实际上是一个泛指,泛指 ES2015 及后续的版本。
2、为什么使用 ES6?
每一次标准的诞生都意味着语言的完善,功能的加强。JavaScript语言本身也有一些令人不满意的地方:
1.变量提升特性增加了程序运行时的不可预测性;
2.语法过于松散,实现相同的功能,不同的人可能会写出不同的代码。
二、新增语法
1、let
作用:用于声明变量的关键字。
小结:
1.let 声明的变量只在所处于的块级有效;在一个大括号中,使用 let 关键字声明的变量才具有块级作用域,使用 var 声明的变量不具备块级作用域特性;
2.不允许重复声明;
3.不存在变量提升;
4.暂时性死区:利用 let 声明的变量会绑定在这个块级作用域,不会受外界的影响。
2、const
作用:声明常量,常量就是值(内存地址)不能变化的量
小结:
1. 声明常量时必须赋值;
2.具有块级作用域;
3.常量赋值后,值不能修改。如果是基本数据类型,不能更改值;如果是复杂数据类型,不能更改地址值。
3、let、const、var 的区别
1. 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象。
2. 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升。
3. 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值。
4、解构赋值
ES6中允许从数组中提取值,按照对应位置,对变量赋值,对象也可以实现解构。
数组解构:
let [a, b, c] = [1, 2, 3];
console.log(a)//1
console.log(b)//2
console.log(c)//3
//如果解构不成功,变量的值为undefined
对象解构:
let person = { name: 'zhangsan', age: 20 };
let { name, age } = person;
console.log(name); // 'zhangsan'
console.log(age); // 20
let {name: myName, age: myAge} = person; // myName myAge 属于别名
console.log(myName); // 'zhangsan'
console.log(myAge); // 20
小结:
1.解构赋值就是把数据结构分解,然后给变量进行赋值;
2.如果结构不成功,变量跟数值个数不匹配的时候,变量的值为 undefined;
3.数组解构用中括号包裹,多个变量用逗号隔开,对象解构用花括号包裹,多个变量用逗号隔开;
4.利用解构赋值能够让我们方便的去取对象中的属性跟方法。
5、箭头函数
ES6中新增的定义函数的方式。
语法:
() => {} // ():代表是函数; =>:必须要的符号,指向哪一个代码块;{}:函数体
const fn = () => {} // 代表把一个函数赋值给 fn
函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号;
function sum(num1, num2) {
return num1 + num2;
}
//es6写法
const sum = (num1, num2) => num1 + num2;
如果形参只有一个,可以省略小括号;
function fn (v) {
return v;
}
// es6写法
const fn = v => v;
箭头函数不绑定 this 关键字,箭头函数中的 this,指向的是函数定义位置的上下文 this;可以简单理解成,定义箭头函数中的作用域的 this 指向谁,它就指向谁。
const obj = { name: '张三'}
function fn () {
console.log(this); // this 指向是 obj 对象
return () => {
console.log(this);
// this 指向的是箭头函数定义的位置,那么这个箭头函数定义在 fn 里面,
// 而这个 fn 指向是的 obj 对象,所以这个this也指向是 obj 对象
}
}
const resFn = fn.call(obj);
resFn();
优点:箭头函数的优点在于解决了 this 执行环境所造成的一些问题。比如:解决了匿名函数 this 指向的问题(匿名函数的执行环境具有全局性),包括 setTimeout 和 setInterval 中使用 this 所造成的问题。
面试题:
var age = 100;
var obj = {
age: 20,
say: () => {
alert(this.age)
}
}
obj.say(); // 箭头函数 this 指向的是被声明的作用域里面,而对象没有作用域的,
// 所以箭头函数虽然在对象中被定义,但是 this 指向的是全局作用域
6、剩余参数
剩余参数语法允许我们将一个不定数量的参数表示为一个数组,不定参数定义方式,这种方式很方便的去声明不知道参数情况下的一个函数。
function sum (first, ...args) {
console.log(first); // 10
console.log(args); // [20, 30]
}
sum(10, 20, 30)
剩余参数和解构配合使用:
let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students;
console.log(s1); // 'wangwu'
console.log(s2); // ['zhangsan', 'lisi']
三、内置对象扩展
1、Array 的扩展方法
1.扩展运算符(展开语法)
①扩展运算符可以将数组或者对象转为用逗号分隔的参数序列
let ary = [1, 2, 3];
...ary // 1, 2, 3
console.log(...ary); // 1 2 3,相当于下面的代码
console.log(1,2,3);
②扩展运算符可以应用于合并数组
// 方法一
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2];
// 方法二
ary1.push(...ary2);
③将类数组或可遍历对象转换为真正的数组
let oDivs = document.getElementsByTagName('div');
oDivs = [...oDivs];
2.构造函数方法:Array.from()
①将伪数组或可遍历对象转换为真正的数组
// 定义一个集合
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// 转成数组
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
②方法还可以接受第二个参数,作用类似于数组的 map 方法,用来对每个元素进行处理,将处理后的值放入返回的数组
let arrayLike = {
"0": 1,
"1": 2,
"length": 2
}
let newAry = Array.from(arrayLike, item => item *2); // [2,4]
注意:如果是对象,那么属性需要写对应的索引。
3.实例方法
①find():用于找出第一个符合条件的数组成员,如果没有找到返回 undefined
let ary = [{
id: 1,
name: '张三'
}, {
id: 2,
name: '李四'
}];
let target = ary.find((item, index) => item.id == 2);
// 找数组里面符合条件的值,当数组中元素id等于2的查找出来,注意,只会匹配第一个
②findIndex():用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1
let ary = [1, 5, 10, 15];
let index = ary.findIndex((value, index) => value > 9);
console.log(index); // 2
③includes():判断某个数组是否包含给定的值,返回布尔值
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
2、String 的扩展方法
1.模板字符串
①ES6新增的创建字符串的方式,使用反引号定义
let name = `张三`;
②模板字符串中可以解析变量
let name = '张三';
let sayHello = `hello,my name is ${name}`; // hello, my name is 张三
③模板字符串中可以换行
let result = {
name: 'zhangsan',
age: 20,
sex: '男'
}
let html = ` <div>
<span>${result.name}</span>
<span>${result.age}</span>
<span>${result.sex}</span>
</div> `;
④在模板字符串中可以调用函数
const sayHello = function () {
return '哈哈哈哈 追不到我吧 我就是这么强大';
};
let greet = `${sayHello()} 哈哈哈哈`;
console.log(greet); // 哈哈哈哈 追不到我吧 我就是这么强大 哈哈哈哈
2.实例方法
①startsWith() 和 endsWith()
startsWith():表示参数字符串是否在原字符串的头部,返回布尔值。
endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值。
let str = 'Hello world!';
str.startsWith('Hello') // true
str.endsWith('!') // true
②repeat()
repeat 方法表示将原字符串重复 n 次,返回一个新字符串。
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
3、Set 数据结构
1.简介
ES6 提供了新的数据结构 Set 。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构。
const s = new Set();
Set 函数可以接受一个数组作为参数,用来初始化。
const set = new Set([1, 2, 3, 4, 4]); // {1, 2, 3, 4}
2.实例方法
add(value):添加某个值,返回 Set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为 Set 的成员。
clear():清除所有成员,没有返回值。
const s = new Set();
s.add(1).add(2).add(3); // 向 set 结构中添加值
s.delete(2) // 删除 set 结构中的2值
s.has(1) // 表示 set 结构中是否有1这个值 返回布尔值
s.clear() // 清除 set 结构中的所有值
// 注意:删除的是元素的值,不是代表的索引
3.遍历
Set 结构的实例与数组一样,也拥有 forEach 方法,用于对每个成员执行某种操作,没有返回值。
s.forEach(value => console.log(value))