JavaScript面向对象
两大编程思想:
-
面向过程
-
面向过程:POP(Process-oriented programming)
- 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。
-
-
面向对象
-
面向对象:OOP (Object Oriented Programming)
-
面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。
-
-
面向对象三大特性
- 封装性【已经把扫把功能准备好,负责开即可】
- 继承性【继承与拖拉机,会开拖拉机就会弄这个,继承自拖拉机】
- 多态性【可以放到一起,也可以单独拿下来,而且那个扫把坏了换哪个不影响其他的】
面向对象和过程优缺点
-
面向过程:
-
优点:性能比面向对象高,步骤练习紧密
-
缺点:不好维护,不易多次使用及扩展
-
-
面向对象:
-
优点:易维护,可复用,可扩展,灵活性高
-
缺点性能没有面向过程高
-
- 面向过程就是一份蛋炒饭,味道均匀,但是假如有的人不喜欢吃鸡蛋,没办法分开
-
面向对象就是一个盖浇饭,但是味道不均匀,而不想吃某种味道,可以分开
-
简单程序面向过程,复杂程序用面向对象
ES6(es2015)
-
let 和 const 关键字
- 我们之前定义变量的关键字是var,它定义的变量有很多奇怪的特点,如下有两点比较突出:
-
变量先使用再定义------ 变量提升。
console.log(a);
var a = 1;
console.log(a)
- 缺少块级作用域。
// 此处为了使用循环,定义一个循环变量i。
for(var i=1; i<10;i++){}
// i在循环体之外也能使用。
console.info(i);
这两个问题都容易让初学者混淆,也容易让从其它语言转过来的程序员混淆。为了解决这些问题,ES6中新增了let。 而ES6引入const的原因是,因为ES5中并没有提供常量的功能。
let 变量
-
作用:定义变量。(var也是用来定义变量)
-
let基本使用
- 格式: let 变量名 = 变量值; 它用来定义变量,基本使用格式与var关键字一样。在可以在使用var 的地方改成let。
-
与var的区别
- 不能重复定义
- 没有变量提升(var定义的变量是有变量提升的),必须先定义再使用
- 全局变量不会附加到window对象的属性中
- 具有块级作用域
-
代码演示
- 不能重复定义
// 1. let 定义变量,变量不可以再次定义
let name = 'zhangsan';
name = 'lisi';
console.log(name); // lisi
let name = 'wangwu'; // 再次定义,报错:Identifier 'name' has already been declared
-
块级作用域
- 作用域
- 全局作用域
- 局部作用域
- 块级作用域(ES6中提出的)
- 作用域
-
如果是通过let关键字来定义变量的,则js代码中就可以通过{}来切分作用域了。
// 具有块级作用域,块就是大括号
{
let myAge = 18;
console.log(myAge); // 18
}
console.log(myAge); // 报错,此作用域中没有myAge的定义
// 块级作用域的嵌套
{
let a = 1;
{
let a = 2;
console.log(a); // 2
a = 3;
console.log(a); // 3
}
console.log(a); // 1
}
// 隐藏的块级作用域
for (let i = 0; i < 10; i++) {
// i 只能在此范围内使用,因为有块级作用域
}
console.log(i); // 报错,此作用域中没有i的定义
- 没有变量提升
- 通过let定义的变量,不再有没有变量提升,必须先定义再使用
console.log(myGender); // 报错,此时还没有定义myGender
let myGender = '男';
console.log(myAge); // 不报错,输出undefined
var myAge = 13;
-
全局变量不会赋值加在顶级对象中
- 浏览器环境中顶级对象为window
- nodejs环境中顶级对象为global
-
let声明的全局变量不会以属性的形式附加在window对象中,全局变量与window对象再无关系。
// 浏览器环境下代码:
let hobby = '吃饭';
var a = 1
console.log(window.hobby); // undefined
console.log(window.a); // 1
// nodejs环境下代码:
let hobby = '吃饭';
var a = 1
console.log(window.hobby); // undefined
console.log(window.a); // 1
- ES6中保留了对var的支持,你可以在代码中同时使用var和let。当然,建议使用let。
const 常量
- 使用场景
程序员在协同开发项目时,会遇到一种场景:有一些数据大家都需要用到,但是都不能修改,即数据是只读的。举个例子:在开发公司的网站时,公司的基本信息:地址,公司名,电话等等信息可以在多个地方都需要使用,但不允许去修改。 显示,使用变量来保存这些信息是不适合的,因为变量不是只读的。这种情况下, 我们就可以把这些数据保存在常量中。
语法格式及命名规范
-
作用:定义一个只读的常量。
-
格式: const 常量名 = 常量值;
-
示例:var COMPANY_NAME = "XXX公司"
- 注意:
- 区别于变量名,常量名推荐采用全大写的方式,多个单词之间使用_下划线分隔。
- 注意:
-
const特点
- 所谓常量,就是不能修改,即:
- 一旦定义就不能修改;
- 一旦声明,就必须立即初始化;
- 所谓常量,就是不能修改,即:
const a = 1;
a = 2; // 报错
其它与let相似的特点
-
具有块级作用域
-
没有变量提升,必须先定义再使用
-
常量也是独立的,定义后不会添加为window对象的属性
- 本质
- const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存单元中保存的数据不得改动
- 本质
const obj = { a:1 };
obj.a = 2;
console.log(obj); // 问题:obj对象的属性值a是否修改了。
- 对于简单类型数据(数值、字符串、布尔值),值就保存在变量指向的那个内存单元,因此等同于常量值。
- 对于复杂类型数据(如对象和数组),变量指向的内存单元,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就不能控制了。因此,将一个对象声明为常量必须非常小心,它的属性是可以修改的。
如果你真的希望定义一个不能修改的对象(属性不能添加,修改,删除),你可以使用Object.freeze()。下面是一段参考代码:它一个可以把一个对象全部冻结的函数(深冻结):
// 设置函数,对一个对象进行深冻结(用于防止复杂数据被修改)
function deepFreeze(obj) {
Object.freeze(obj);
for (var k in obj) {
if (typeof obj[k] === 'object') {
deepFreeze(obj[k]);
}
}
}
// 声明要冻结的对象
const obj = {
name: 'jack',
age: 18,
hobbies: {
eat: '吃',
drink: '喝',
run: '跑步',
playBall: ['篮球', '弹球', '悠悠球']
}
};
// 调用函数冻结对象obj
deepFreeze(obj);
obj.name = 'rose';
obj.hobbies.eat = '各种吃';
obj.hobbies.playBall[2] = '各种弹';
console.log(obj); // obj没有变化,说明冻结完成
总结:
解构赋值
-
我们经常会遇到一个场景:从对象中取出它的属性值,并保存在一个变量中。
- 例如:
// 这里的item是一个对象{},其中包含属性name和age let name = item.name; let age = item.age;- 另外,如ajax请求的回调函数
$.ajax({ // ..省略其它参数 success : function(result){ // 这里的result是一个对象,其中有code属性和data属性,data是一个数组 if(result.code === 200){ let data = result.data; data.forEach(); // ..省略内部操作 } } })ES6提供了一个更加方便的方式从对象中取出属性值来,这就解构赋值。
定义
-
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
- 它有两个动作:
- 解构:意思是把有结构的数据分解开成为一个一个的值
- 赋值:把解构之后的值保存到变量
-
基本示例:
let arr = [1, 2, 3, 4]; // 需求: 快速地从数组中取出值来赋值给变量a, b, c, d let a = arr[0], b = arr[1], c = arr[2], d = arr[3]; console.log(a, b, c, d); // 1 2 3 4
数组的解构赋值
-
它能够快速从数组中取出值保存到变量中。它的本质是给变量赋值。
-
内容:
- 语法格式及解构规则 - 常规使用:变量个数等于数组长值 -
非常规使用
- 变量个数大于数组长度
- 变量个数小于数组长度
-
高级使用
- 用空跳过不需要的值
- 剩余值
- 复杂嵌套场景
- 小面试题
-
-
语法格式及解构规则
- 格式:
let [变量1=默认值1, 变量2=默认值2, 变量n=默认值n] = [数组元素1, 数组元素2, 数组元素n];
- 格式:
注:默认值是可选的,可以设置,也可以不设置;
规则:
-
赋值符号左右两边都是数组,把右边的数组按下标从小到大取出来,放入左边的数组中。
-
如果左边的变量并没有分配到某一个值:
- 有默认值,使用其默认值。
- 无默认值,其值是undefined
-
最开始的let可选,也可写成var或省略,与解构赋值功能无关,仅代表变量的声明方式。
- 基本示例
//最简单的场景: 变量个数和数组中元素的是相等的 let arr = [5, 9, 10]; let [a, b, c] = arr; console.log(a, b, c); // 输出 5 9 10 // 等价于: let a = arr[0]; let b = arr[1]; let c = arr[2];注意:
- “=”左右两边的格式要统一。
-
闯关练习(重要)
- 我们可以通过如下练习来掌握数组的解构赋值,搞定这些练习,解构赋值操作就没有什么问题啦~
// 1 变量多,值少: let arr = [5, 9, 10]; let [a, b, c, d] = arr; console.log(a, b, c, d); // 5 9 10 undefined // 结论:没有匹配上的值是undefined // 2 默认值: let arr = [5, 9]; let [a, b, c, d = 4] = arr; console.log(a, b, c, d); // 5 9 undefined 4 // 结论:对于没有匹配上的变量,有默认值就使用默认值,否则就是 undefined, // 3 变量少,值多: let arr = [5, 9, 10, 8, 3, 2]; let [a, b] = arr; console.log(a, b); // 5 9 // 结论:多余的忽略 // 4 用空跳过不需要的值: let arr = [5, 9, 10, 8, 3, 2]; let [, , a, , b] = arr; console.log(a, b); // 10 3 // 结论:不需要用变量接收的值,用空位占位 // 5 复杂的场景,只要符合模式,即可解构: let arr = ['zhangsan', 18, ['175cm', '65kg']]; // 5.1 如何让a的值是175cm,b的值是65kg? let [, , [a, b]] = arr; console.log(a, b); // '175cm' '65kg' // 5.2 如何让wxyz对应arr中的四个数据呢? let [w, x, [y, z]] = arr; console.log(w, x, y, z); // 'zhangsan' 18 '175cm' '65kg'
剩余值
let arr = [5, 9, 10, 8, 3, 2];
let [a, b, ...c] = arr; // ...c 接收剩余的其他值,得到的c是一个数组
console.log(a, b, c); // 结果为: 5 9 [10, 8, 3, 2]
// 等价于:
// let a = 5,
// let b = 9,
// let c = [10, 8, 3, 2]
-
注意:
- ... 只能用在最后一个变量上。这个变量一定是一个数组。
- 在解构赋值的过程中,如果有出现数组元素个数大于变量个数的情况,它将会把多余的参数起来,保存在这个数组。如果元素个数不足(没有剩余元素),也会返回空数组。
-
一个解构赋值的小面试题
- 交换两个变量的值?
let a = 1, b = 2; // 写代码,实现互换a,b的值 // ???? [a, b] = [b, a]; console.log(a,b); // 要求输出 2, 1
- 交换两个变量的值?
对象的解构赋值
-
作用:快速从对象中获取值保存到变量中。它的本质是给变量赋值。
-
使用格式及规则
- 完整格式
let {"属性名1":变量名1=默认值1, "属性名2":变量名2=默认值2,... } = {"属性名1":属性值1,"属性名2":属性值2,...} -
解析规则:
-
默认值是可选的。你可以指定默认值,也可以不指定。
-
右边的"属性名"与左边的“属性名” 一致,则把右边的属性值赋值给左边的变量名。
-
如果右边的匹配不成立,看看是否有使用默认值,有默认值就使用默认值,没有就是undefined。
-
精简格式
- 如果左侧对象中属性名与变量名相同,则可左侧合并: ```javascript
let {变量名1=默认值1,变量名2=默认值2} = {"属性名1":属性值1,"属性名2":属性值2,...}```
-
-
解析规则:
- 右边的"属性名"与左边的变量名 一致,则把右边的属性值赋值给左边的变量名。
-
基本使用
-
场景1,变量名和属性名一样
// 场景1,默认要求变量名和属性名一样 let { name, age } = {age: 27, name: 'jack'}; console.log(name, age); // 'jack' 27 let {a, c} = {a: 'hello', b: 'world'}; console.log(a, c); // 'hello' undefined
-
-
注意:
- “=” 左右两边的格式一致。
- 对象是属性的无序集合,所以不需要管顺序
-
场景2,变量改名
// 场景2,可以通过:为变量改名
let {b, name:a} = {name: '李雷'};
console.log(b, a, name); // undefined '李雷' undefined
// 解释:操作中,将属性name的值保存给变量a,所以只有a有值
- 闯关练习
// 默认值:
var {b=1, name:a,age:b=20 } = {name: '韩梅梅'};
console.log(b, a, name); // 2 '韩梅梅' undefined
// 复杂的嵌套,只要符合模式,即可解构
let obj = {
name: '小红',
age: 22,
dog: {
name: '小明',
gender: '男'
}
};
// 如何才能把age和name解析出来
let {name, age, dog: {name:dogName, gender}} = obj;
console.log(name, age, dogName, gender); // '小红' 22 '小明' '男'
// 假设从服务器上获取的数据如下
let response = {
data: ['a', 'b', 'c'],
meta: {
code: 200,
msg: '获取数据成功'
}
}
// 如何获取到 code 和 msg
let { meta: { code, msg } } = response;
console.log(code, msg); // 200 '获取数据成功'
let {max,min,PI} = Math;
剩余值
// 把其它的属性全收集起来
let obj = {name:'zs', age:20, gender:'男'};
let {name, ...a} = obj;
console.log(name, a);
// 结果:
// name = zs
// a = {age: 20, gender: "男"};
定义对象的简洁方式
-
对属性名的简化
-
对方法的简化,
- 回想js高级课程class中的方法设置,写法是一样的。
-
注意
- {} 是专门解构对象使用的
- [] 是专门解构数组使用的
- 不能混用
let name = 'zhangsan', age = 20, gender = '女';
let obj = {
name: name, // 原来的写法
age, // 对象属性和变量名相同,可以省略后面的 “:age”,下面的gender同理
gender,
fn1 : function(){ // 常规写法
console.log(123);
},
fn2 () { // function可以省略
console.log(456);
}
};
console.log(obj.age); // 20
obj.fn2(); // 456
函数的拓展
es6对函数的功能进行了一些拓展,补充了很多功能。学习这个部分的内容我们就可以更加灵活优雅地使用函数啦。
参数默认值
在定义一个函数时,我们可以给形参设置默认值:当用户不传入对应实参时,我们有一个保底的值可以使用。这个特性在ES6之前,是不支持的,我们以前会通过一些变通的方式来实现。
传统的默认值设置方式
参数的默认值在之前的课程内容已经涉及,例如在xhr.open(请求类型,请求地址,是否异步)方法中,第3个参数默认是true,即表示异步ajax。
- 默认值的意思是:
- 如果传入对应实参,就用你传的值。
- 如果不传,就使用某个特殊的、事先定义好的值。这个值也就是默认值。
- 示例代码:(以ajax课程中的ajax函数封装为示例)
// ES5 中给参数设置默认值的变通做法,以ajax函数部分封装为示例:
function ajax (type, url, isAsync) {
// 基本写法:
if(isAsync === undefined){
isAsync = true;
}
// 简化写法:
// isAsync = isAsync || true;
console.log(type, url, isAsync);
//.. 其他代码略
}
// 下面这两句是等价的
open("get","common/get"); // 参数3使用默认值true
open("get","common/get",true);
open("get","common/get",false);
- 以上代码是利用了形参的一个特点:没有传入对应实参的话,其默认值是undefined。
- 观察后发现,代码是可以工作的,但是显得很累赘,es6提供了更简单的实现。
ES6的实现
- 格式
function 函数名 (参数名1=默认值1,参数名2=默认值2,参数名3=默认值3){
// 函数体
}
- 示例
function open(type, url, isAsync = true) {
console.log(type, url, isAsync);
}
// 下面这两句是等价的
open("get","common/get");// // 参数3使用默认值true
open("get","common/get",true);
open("get","common/get",false);
-
思考与回忆:
- 能否跳过isAsync,url,而只给type设置默认值?
-
注意:
- 带默认值的形参放在形参列表的最右边。
-
练习
function f(a = 1,b = 2){
console.log(a, b);
}
f(10);
f(10,20);
f();
function f2(a = 1,b){
console.log(a, b);
}
f2(10);
f2(10,20);
f2(,3); // 报错:Uncaught SyntaxError: Unexpected token
f2();
// 与解析赋值一起使用:
function f1({a = 1, b = 2} = {}){
console.log(a, b);
}
f1({a:10,b:20});
f1({a:20});
f1({c:1});
f1(); // 注意:如果形参位置不设置 = {},会出现报错
rest 参数
-
rest (其它的,剩余的)参数 用于获取函数多余参数,把它们放在一个数组中。
- 语法格式
- rest参数不能设置默认值,且必须设置在参数列表最后位置
- 语法格式
在定义函数时,在最后一个参数前面加上..., 则这个参数就是剩余参数;
let fn = function(参数1,参数2,...rest参数){}
let fn = (参数1,参数2,...rest参数) => { };
只是在定义函数时,在形参列表中区别一下,而在调用函数时并无区别。
-
示例
- 回答如下问题
function f2 (x,...y){
console.log(x,y)
}
f2(1,2);
f2(2,3,4);
function f1 (x,y){
console.log(x,y)
}
f1(1,2);
f1(2,3,4);
-
应用--代替arguments
-
问题:编写一个函数,求所有参数之和;
-
方法一:arguments
-
方法二:rest参数
-
-
function getSum (){
// 在这里写你的代码
var sum = 0 ;
for(var i = 0; i < arguments.length; i++){
console.info( arguemnts[i])
sum += arguments[i];
}
}
- 如果以箭头函数 的方式去定义这个函数,则内部不可以使用arguments这个对象了。此时,我们就可以使用
-
rest 参数,它可以替代 arguments 的使用。 代码如下:
// 参数很多,不确定多少个,可以使用剩余参数 const getSum = (...values) => { var sum = 0 ; for(var i = 0; i < values.length; i++){ console.info( values[i]) sum += values[i]; } } // 调用 console.log(fn(6, 1, 100, 9, 10));
- 与arguments相比,它是一个真正的数组,可以使用全部的数组的方法。
**箭头函数**
- 什么是箭头函数
- 箭头函数本质上是 匿名函数的简化写法。
- ES6 中允许使用箭头函数的方式来定义一个函数。前面学习的定义一个函数有两种方式:
- 函数声明式
- 函数表达式
- 现在有了一个新的方式:
3. 箭头函数
- 格式
```javasript
let 函数名 = (形参1,...形参n) => {
// 函数体
};
- 定义一个箭头函数
// 1. 函数声明式 function fu1(x){ return x * 2; } // 2. 函数表达式 let fn2 = function (x) { return x * 2; }; // 3. 箭头函数 (赋值给变量,作为函数表达式使用) let fn3 = (x) => { return x * 2; }; // 对比2、3两种写法会发现,箭头函数的写法要比传统匿名函数简洁。
格式小结:
- 去除function关键字
- 在形参和函数体之间设置 =>
-
注意:
-
=>是一个整体,不要加空格
-
箭头函数只在书写格式上有些区别,但在函数调用方式上,是没有区别的。
-
-
简化写法
- 当形参有且只有一个,可以省略小括号
let f = (x) => {console.log(x)} // 可以简化成: let f = x => {console.log(x)}- 当函数体只有一条语句,可以省略大括号;
let f = x => {console.log(x);} // 可以简化成: let f = x => console.log(x);-
当函数体只有一条语句,并且就是return语句,则可以省略return和大括号。
- 如果省略了大括号,则必须省略return,否则报错
let f = x => {return x*2; } // 可以简化成: let f = x => x*2; -
注意如果返回值是一个对象,要加()
let f = x => { return {a:1}; } // 可以简化成: let f = x => {a:1}; // 如果不加,{}会被认为是函数的代码块,不会被解析 器当成对象处理 let f = x => ({a:1});
箭头函数与普通函数的区别
-
内部没有this
-
内部没有arguments(了解)
-
不能作为构造器(了解)
- 内部的this对象,指向定义时所在的对象,而不是使用时所在的对象。
// 箭头函数中this的问题: let obj = { name: 'jack', age: 18, sayHi() { // 方法中直接访问this: console.log(this); // 对象obj // 普通调用的函数中的this: var f1 = () => { console.log(this); // 对象obj }; f1(); // 同上: setTimeout(() => { console.log(this); // 对象obj }, 0); // 另一个对象的方法中的this let obj2 = { name: 'rose' }; obj2.sayHehe = () => { console.log(this); // 对象obj }; obj2.sayHaha = function () { console.log(this); // 对象obj2,因为没有使用箭头函数 }; obj2.sayHehe(); obj2.sayHaha(); } }; obj.sayHi();
- 内部的this对象,指向定义时所在的对象,而不是使用时所在的对象。
没有 arguments
- 因为es6中提供了函数的rest剩余值功能,可以替代arguments
let fn = (a,b) => {
console.log(arguments); // 报错,arguments is not defined
};
fn(1, 2);
- 箭头函数不能作为构造函数
let Person = () => {
// ..代码
};
let obj = new Person(); // 报错,Person is not a constructor
// 换个角度理解,箭头函数中都没有自己的this,无法处理成员,所以不能当构造函数
在javascript中,函数的功能太多了,除了起到最基本的封装代码之外,还可以当做构造器来使用。ES6中提出的箭头函数让函数减负了,只适合代码的封装操作。
数组的扩展
数组对象是js中非常重要的对象,它本身提供了非常多的方法,例如:push,pop,shift,unshift,splice,concat,sort,indexOf,join,map,forEach,filter,every,some,reduce... 。由于前端的主要工作内容之一是与后端进行数据交互,而数据交互的载体大多是数组和对象,所以我们对数组的操作要求也非常
扩展运算符
-
功能:它的作用是把数组中的元素一项项地展开:把一个整体的数组拆开成单个的元素。
-
格式:...数组
- 基本用法
let arr = [1, 2, 3, 4, 5] console.log(...arr) // 1 2 3 4 5 -
数组合并
- 从把一个数组中的值全取出来,放在另一个数组中的
var arr0 = ['a', 'b']; var arr1 = [1, 2, 3]; var arr2 = [...arr1]; var arr3 = [...arr0, ...arr1]; -
对象合并
let obj = { name: 'Jack', age: 18 } let obj2 = { ...obj, gender: '男' } console.log(obj2) -
在函数传递参数的时候也可以使用
let arr = [1, 2, 3]
function fn(a, b, c) {
console.log(a)
console.log(b)
console.log(c)
}
fn(...arr)
// 等价于 fn(1, 2, 3)
- Math.max()
Math.max(1,3,4,6);
var arr = [1,3,4,6];
Math.max(...arr);
// 或者 Math.max.apply(this, [1, 2, 3, 566]);
- Array.from()
-
功能:把其它伪数组的对象转成数组。
-
格式: 数组 = Array.from(伪数组对象)
- 它的实参有三种情况:
1.自定义的,类似数组格式的对象。 ``` let fakeArr = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
2. arguments对象 3. DOM 操作返回的 NodeList 集合
-
find方法
在实际的开发中,我们经常会遇到一种场景:从一个数组中找出符合条件的元素。我们要的讨论的重点是如何从数组中找出符合条件的元素,当然,我们可以通过手写循环的方式来实现这个功能,但是现在es6中提供了现成的功能。find/findIndex
-
作用:从数组中找出我们符合条件的第一个元素(或者是下标)。
-
格式
- find和findIndex的格式是一致的。
let result = [].find(function(item,index,self){ //... // 如果满足查找的条件 return true; });-
回调函数有三个参数,分别表示:数组元素的值、索引及整个数组
-
如果某次循环返回的是true,find和findIndex方法的返回值就是满足这个条件的第一个元素或索引
-
-
执行流程
-
find和findIndex方法,会遍历传递进来的数组
-
如果在回调函数体内,某个时刻return true,则表示查找过程结果,返回值就是本轮循环中的元素(或者是下标);如果全部的循环结束,也没有return true,则表示没有找到,没有找到会返回undefined。
-
findIndex 找到数组中第一个满足条件的成员并返回该成员的索引,如果找不到返回 -1。
let arr = [1, 2, 4, 0, -4, 3, -2, 9]; arr.find(function (item, index, self) { console.log(item); // 数组中的每个值 console.log(index); // 数组中的每个索引/下标 console.log(self); // 当前的数组 }); -
-
简单示例
// 用法:找数组中第一个小于0的数字
let arr = [1, 2, 4, 0, -4, 3, -2, 9];
let result = arr.find(function (item) {
return item < 0; //遍历过程中,根据这个条件去查找
});
console.log(result); // -4
- 注意通过箭头函数来简化代码。
- 从一个复杂的对象数组中找出符合条件的对象。
let data = [
{id:2,name:'严高',age:15},
{id:3,name:'徐阶',age:17},
{id:4,name:'高拱',age:18},
{id:1,name:'张居正',age:12},
];
findIndex()
- findIndex 的使用和 find 类似,只不过它查找的不是数组中的元素,而是元素的下标。
includes()
-
功能:判断数组是否包含某个值,返回 true / false
-
格式:数组.includes(参数1,参数2)
- 参数1,必须,表示查找的内容
- 参数2,可选,表示开始查找的位置,0表示从第一个元素开始找。默认值是0。
let arr = [1, 4, 3, 9];
console.log(arr.includes(4)); // true
console.log(arr.includes(4, 2)); // false, 从2的位置开始查,所以没有找到4
console.log(arr.includes(5)); // false
Map 和 Set
- 是 ES6 新增的两个数据类型
- 都是属于内置构造函数
- 使用 new 的方式来实例化使用
Set
- 使用方式就是和 new 连用
const s = new Set()
console.log(s)
/*
Set(0) {}
size: (...)
__proto__: Set
[[Entries]]: Array(0)
length: 0
*/
- 就是一个数据集合
- 我们可以在 new 的时候直接向内部添加数据
// 实例化的时候直接添加数据要以数组的形式添加
const s = new Set([1, 2, 3, {}, function () {}, true, 'hwllo'])
console.log(s)
/*
Set(7) {1, 2, 3, {…}, ƒ, …}
size: (...)
__proto__: Set
[[Entries]]: Array(7)
0: 1
1: 2
2: 3
3: Object
4: function () {}
5: true
6: "hwllo"
length: 7
*/
- 看上去是一个类似数组的数据结构,但是不是,就是 Set 数据结构
常用方法和属性
- size : 用来获取该数据结构中有多少数据的
const s = new Set([1, 2, 3, {}, function () {}, true, 'hwllo'])
console.log(s.size) // 7
看上去是一个和数组数据类型差不多的数据结构,而且我们也看到了 length 属性 但是不能使用,想要获取该数据类型中的成员数量,需要使用 size 属性
- add : 用来向该数据类型中追加数据
const s = new Set()
s.add(0)
s.add({})
s.add(function () {})
console.log(s.size) // 3
-
这个方法就是向该数据类型中追加数据使用的
-
delete : 是删除该数据结构中的某一个数据
const s = new Set()
s.add(0)
s.add({})
s.add(function () {})
s.delete(0)
console.log(s.size) // 2
- clear : 清空数据结构中的所有数据
const s = new Set()
s.add(0)
s.add({})
s.add(function () {})
s.clear()
console.log(s.size) // 0
- has : 查询数据解构中有没有某一个数据
const s = new Set()
s.add(0)
s.add({})
s.add(function () {})
console.log(s.has(0)) // true
- forEach : 用来遍历 Set 数据结构的方法
const s = new Set()
s.add(0)
s.add({})
s.add(function () {})
s.forEach(item => {
console.log(item) // 0 {} function () {}
})
- 方法介绍的差不多了,有一个问题出现了,那就是
- 我们的方法要么是添加,要么是删除,要么是查询,没有获取
- 因为要获取 Set 结构里面的数据需要借助一个 ... 展开运算符
- 把他里面的东西都放到一个数组里面去,然后再获取
const s = new Set([1, 2, 3, 4, 5, 6])
const a = [...s]
console.log(a) // (6) [1, 2, 3, 4, 5, 6]
console.log(a[0]) // 1
console.log([...s][0]) // 1
- 又一个问题出现了,new 的时候需要以数组的形式传递
- 然后获取的时候又要转成数组的形式获取
- 那么我为什么不一开始就定义数组,要这个 Set 数据类型干什么
- 这就不得不提到一个 Set 的特点
- Set 不允许存储重复的数据
const s = new Set([1, 2, 3])
s.add(4) // 此时 size 是 4
s.add(1) // 此时 size 是 4
s.add(2) // 此时 size 是 4
s.add(3) // 此时 size 是 4
数组去重
let arr = [1,1,2,3,3];
console.log([...new Set(arr)])
Map
- 也是要和 new 连用
- 是一个数据集合,是一个很类似于 对象 的数据集合
const m = new Map()
console.log(m)
/*
Map(0) {}
size: (...)
__proto__: Map
[[Entries]]: Array(0)
length: 0
*/
- 我们的对象中不管存储什么,key 一定是一个字符串类型
- 但是再 Map 里面,我们的 key 可以为任意数据类型
- 我们也管 Map 叫做 (值 = 值 的数据类型)
const m = new Map([[{}, {}], [function () {}, function () {}], [true, 1]])
console.log(m)
/*
Map(3) {{…} => {…}, ƒ => ƒ, true => 1}
size: (...)
__proto__: Map
[[Entries]]: Array(3)
0: {Object => Object}
key: {}
value: {}
1: {function () {} => function () {}}
key: ƒ ()
value: ƒ ()
2: {true => 1}
key: true
value: 1
length: 3
*/
常用方法和属性
- size : 用来获取该数据类型中数据的个数
const m = new Map([[{}, {}], [function () {}, function () {}], [true, 1]])
console.log(m.size) // 3
- delete : 用来删除该数据集合中的某一个数据
const m = new Map([[{}, {}], [function () {}, function () {}], [true, 1]])
m.delete(true)
console.log(m.size) // 2
-
set : 用来向该数据集合中添加数据使用
const m = new Map() m.set({ name: 'Jack' }, { age: 18 }) console.log(m.size) // 1 -
get : 用来获取该数据集合中的某一个数据
const m = new Map() m.set({ name: 'Jack' }, { age: 18 }) m.set(true, function () {}) console.log(m.get(true)) // function () {} -
clear : 清除数据集合中的所有数据
const m = new Map() m.set({ name: 'Jack' }, { age: 18 }) m.set(true, function () {}) m.clear() console.log(m.size) // 0 -
has : 用来判断数据集合中是否存在某一个数据
const m = new Map() m.set({ name: 'Jack' }, { age: 18 }) m.set(true, function () {}) console.log(m.has(true)) // true
String的扩展String的扩展
es6中对字符串提供了新的特性,我们介绍其中几个方法:
- 模板字符串
- includes
- startsWith
- endsWith
- repeat
模板字符串
在做字符串拼接时,使用+来拼接复杂内容是很麻烦的,而模板字符串可以解决这个问题。
-
格式:
${变量} ${表达式} -
语法:
-
模板字 符串使用反引号 ` 把内容括起来,类似于普通字符串的""。
-
${}充当界定符的作用,其中可以写变量名,表达式等。
-
允许字符串内部进行换行,代码更好读更好的体验
let name = 'zs'; let age = 18; // 拼接多个变量,在模板字符串中使用占位的方式,更易懂 let str = `我是${name},今年${age}`; // 内容过多可以直接换行 let obj = [{name: 'flex', age: 20},{name: 'james', age: 21}]; let arr = ['175cm', '60kg']; let html = ` <div> <ul> <li>${obj.name}</li> <li>${obj.age}</li> <li>${arr[0]}</li> <li>${arr[1]}</li> </ul> </div>`;
-
includes()
- 格式:str.includes(searchString, [position])
- 功能:返回布尔值,表示是否找到了参数字符串
- position: 从当前字符串的哪个索引位置开始搜寻子字符串,默认值为0。
startsWith()
- 格式:str.startsWidth(searchString, [position])
- 功能:返回布尔值,表示参数字符串是否在原字符串的头部或指定位置
- position: 在 str 中搜索 searchString 的开始位置,默认值为 0,也就是真正的字符串开头处。
endsWith()
- 格式:str.endsWith(searchString, [len])
- 功能:返回布尔值,表示参数字符串是否在原字符串的尾部或指定位置.
- len:可选。作为 str 的长度。默认值为 str.length。
repeat()
repeat方法返回一个新字符串,表示将原字符串重复n次。
-
语法:str.repeat(n)
let html = '<li>itheima</li>'; html = html.repeat(10);
ECMAScript 6 降级处理
ES 6 的兼容性问题
- ES6 (2015年10月)虽好,但是有兼容性问题,IE7-IE11 基本不支持 ES6 ES6 兼容性列表
- 在最新的现代浏览器、移动端、Node.js 中都支持 ES6
- 后续我们会讲解如何处理 ES6 的兼容性
- 因为 ES 6 有浏览器兼容性问题,可以使用一些工具进行降级(把ES6的代码转成ES5的代码·),例如:babel
babel官网](www.babeljs.cn/)
实时转码:babeljs.io/repl