三阶段课程——Day03(函数扩展:箭头函数;类class;iterator迭代器)
1、函数扩展
1.1、箭头函数基本用法
主要用在回调函数中,如map、forEach等,一般不用在事件函数和对象方法中。
语法:箭头函数通过声明变量的形式去定义它(本质上是变量中存储了一个箭头函数)
let 变量名 = ( 参数 )=>{ ......代码块 return 返回值 }
参数相关特性
- 没有参数
- 多个参数
- 一个参数(有且只有一个参数,可以省略小括号)
- 剩余参数(箭头函数没有arguments,只能使用rest接收剩余参数)
// 没有参数 let fn = () => { console.log('我是箭头函数,我执行了'); } fn(); // 有多个参数 let fn1 = (a, b) => { return a + b; } console.log(fn1(3, 5)); // 有一个参数(有且只有一个参数,可以省略小括号) let fn2 = a => { console.log('我有一个参数是:' + a); } fn2(3); // 箭头函数中没有arguments // 如果想获取所有的实参的集合,用rest参数 let fn3 = (...rest) => { // console.log(arguments); // arguments is not defined console.log(rest); } fn3(3, 4, 5);
返回值的特性
- 完整的写法:没有省略return关键字
- 省略return:函数中只有一行代码,且可以省略return,同时大括号也需要省略
- 返回对象的注意事项,需要用小括号括起来
let fn1 = () => { return 10; } console.log(fn1()); let fn2 = (a, b) => { return a + b; } console.log(fn2(3, 5)); // 箭头函数中只有一句代码且要返回这句代码,可以不写return,同时也不写大括号
// let fn3 = (a, b) => {
// return a + b;
// }
// console.log(fn3(4, 5));
let fn3 = (a, b) => a + b; console.log(fn3(4, 5)); let fn4 = a => a.repeat(3); console.log(fn4('小王')); // 返回的是对象,则要将这个对象用小括号括起来 let fn5 = () => ({ name: 'zs', age: 3 }) console.log(fn5());
不作为构造函数
无法使用new调用箭头函数,调用会报错
函数参数默认值
// 如果没有传参,index为undefined,则使用默认值 // 如果传参,则用传入的参数 function indexOf(arr, val, index = 0) { for (var i = index; i < arr.length; i++) { if (arr[i] === val) { return i; } } return -1; } var arr = [3, 2, 3, 2, 32]; console.log(indexOf(arr, 3)); console.log(indexOf(arr, 3, 1)); console.log(indexOf(arr, 5));
1.2、箭头函数中的this问题
箭头函数没有this,箭头函数的this取决于它所处的上层环境是哪个对象,那么此this就指向这个对象。普通函数可以通过事件、apply或call等改变this指向,箭头函数无法通过上面方法修改this指向。
// 普通函数中的this:是在调用的时候确定的,不是在定义的时候确定的 // 箭头函数中的this:是在函数定义的时候确定的(函数所处的环境是谁,this指向谁),不是在调用的时候确定的 let fn = () => { console.log(this); } fn(); // window document.onclick = fn; // window 事件调用不能改变箭头函数的this fn.call(document); // window call和apply也不能改变箭头函数中的this
关于环境的补充
只有script全局和函数内才能称为环境,对象的花括号不属于环境
let obj = { fn: function () { console.log(this); }, fn1() { console.log(this); }, fn2: () => { console.log(this); } } obj.fn(); // obj obj.fn1(); // obj obj.fn2(); // window 对象的方法中,一般不用箭头函数
1.3、箭头函数的使用场景
场景1:
let persons = [ { username: '张飞', sex: '男', salary: 50000 }, { username: '关羽', sex: '男', salary: 60000 } ] // 完整写法 // let v = persons.map((item) => { // return { // ...item, // salary: item.salary + 2000 // } // }); // 简写 let v = persons.map((item) => ({ ...item, salary: item.salary + 2000 }) ); console.log(v); console.log(persons);
场景2:
var cartGoods = [ { goodsname: '小米10', price: 5000, isChecked: true,//代表是否选中了 }, { goodsname: '苹果10', price: 3000, isChecked: false,//代表是否选中了 } ] // 完整写法 var a = cartGoods.every((item) => { return item.isChecked; }); //简写 var a = cartGoods.every(item => item.isChecked); console.log(a);
2、类class
2.1、面向对象编程
2.1.1、概念
面向对象是一种以对象为中心的编程思想。面向对象是相对于面向过程来讲的,面向对象把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模。
面向过程
面向过程思想强调的是步骤,当碰见问题时,思考的是“我该怎么做”,分析出解决问题所需要的步骤,一步步的去实现。 例如:想吃蛋炒饭,首先想到的是我要如何做,包括哪些步骤。比如:起锅烧油,加入鸡蛋,加入米饭,加入调料,翻炒,装盘等。
面向对象
例如:还是想吃蛋炒饭,首先想到的是让谁来帮我做蛋炒饭,比如找厨师来帮我做蛋炒饭。具体厨师如何去做这个蛋炒饭,做饭的步骤是怎么样的我们并不关心。只要最终把蛋炒饭做好就可以了。
2.1.2、类和对象的关系
现实中先有对象,根据不同的对象有着相似的特点(归为一类)
程序中(先有类,使用类实例化对象),之前都是使用构造函数创建对象
es5的构造函数有一些问题:方法必须加在原型上。使用function创建对象对于其它语言的工程师觉得很另类 。
2.2、class的基本使用
2.2.1、传统方式创建对象
// 创建对象的标准模式 // 构造函数 + 原型 = 混合模式 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.showName = function () { console.log(this.name); } Person.prototype.showAge = function () { console.log(this.age); } let p1 = new Person('zs', 3); console.log(p1);
2.2.2、ES6创建类
class Person { constructor(name, age) { // 实例化的时候,此构造器函数自动调用 this.name = name; this.age = age; } // 方法都是自动加到类的原型上 showName() { console.log(this.name); } showAge() { console.log(this.age); } } let p1 = new Person('李四', 5); // console.log(p1); console.log(p1.name); p1.showName();
2.3、静态属性和静态方法
实例属性和实例方法:通过实例调用的属性和方法。
静态属性和方法:通过构造函数访问到的属性和方法。
// js中存在的静态方法 // Array.isArray() Array.of() Array.from() // Object.assign() Object.keys() Object.values()
class Person { // 静态的属性和方法 static attr = '人类'; static eat() { console.log('吃东西'); } // 构造函数上的属性和方法 constructor(name, age) { this.name = name; this.age = age; } // 原型上的属性和方法 showName() { console.log(this.name); } showAge() { console.log(this.age); } } let p1 = new Person('张三', 5); console.log(p1); // --------------------- console.log(Person.attr); Person.eat();
2.4、继承
class可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。
// 格式:class 子类 extends 父类 { } // 父类 class Person { constructor(name, age) { this.name = name; this.age = age; } showName() { console.log(this.name); } showAge() { console.log(this.age); } } // 子类 class Student extends Person { constructor(name, age, sex) { super(name, age); // 写在constructor里面第一行,可以理解为把父类的constructor调用了 this.sex = sex; } showSex() { console.log(this.sex); } } let s1 = new Student('张三', 3, '男'); console.log(s1);
2.5、面向对象的应用场景
对面向对象不要有负担,只要懂语法即可。我们更多的是使用面向对象思想封装的框架去写业务,而不是用面向对象思想封装框架。
2.5.1、封装工具原理
class JS { constructor() { this.version = '1.0.1' } Array() { return { indexOf() { }, slice() { } } } Object() { return { keys() { } } } DOM() { return { add() { console.log('我是添加方法'); }, delete() { console.log('我是删除方法'); } } } } let js = new JS(); // console.log(js); let dom = js.DOM(); // console.log(dom); dom.add(); dom.delete();
3、iterator迭代器
3.1、简介
Iterator是一种接口机制,
// 以下数据类型能够被for-of循环,是因为它的原型上面都有Symbol.iterator // 循环数组 var arr = [11, 22, 33]; // console.log(arr); for (let value of arr) { console.log(value); } // 循环字符串 var str = 'abcd'; for (let value of str) { console.log(value); } // 循环set let set = new Set(['aa', 'bb', 'cc']); // console.log(set); for (let val of set) { console.log(val); } // 循环map let map = new Map([ [true, 1], ['ab', 345], [null, 123] ]) for (let val of map) { console.log(val); } // 循环NodeList let btn = document.getElementsByTagName('button'); for (let b of btn) { console.log(b); }
注意事项:在使用for...of遍历数据时, 要确保被遍历的数据, 拥有Iterator功能
原生具备iterator接口的结构有:Array / Set / Map / String / NodeList / HTMLCollection / Arguments
3.4、数组的Iterator
数组的原型上有一个 Symbol.iterator属性,这个属性上存在一个方法
分析:
1、Symbol.iterator是个函数,这个函数执行完毕会返回一个指针对象
2、指针对象有一个next方法,每调用一次next则返回一个值 { value:当前值, done:true/false }
let arr = [11, 22, 33]; // console.log(arr); // console.log(arr[Symbol.iterator]); // 在数组的原型上有Symbol.iterator,它是一个函数,哪我们就可以调用它 let ab = arr[Symbol.iterator](); // 函数调用返回一个对象,这个对象下面有一个next()方法 console.log(ab.next()); // next方法调用一次,返回一个对象 {value: 11, done: false} console.log(ab.next()); // {value: 22, done: false} console.log(ab.next()); // {value: 33, done: false} console.log(ab.next()); // {value: undefined, done: true} // ------------------------------------ // 总结:Symbol.iterator在原型上,它是一个函数,这个函数被调用,会返回一个对象,这个对象的下面有next()方法,next()方法调用一次,就返回{value: 值, done: 是否到最后了} // 不断的next就可以遍历这个数组
3.5、为对象部署iterator接口
Object.prototype[Symbol.iterator] = function () { let keyArr = Object.keys(this); let valueArr = Object.values(this); let index = 0; return { next() { let arr = [keyArr[index], valueArr[index]]; return { value: arr, done: index++ >= keyArr.length } } } } let obj = { name: 'zs', age: 3, sex: '男' } for (let val of obj) { console.log(val); // ["name", "zs"] ["age", 3] ["sex", "男"] }
Part1