…运算符
rest参数(接受剩余参数)
代码案例:
// rest参数,形式为... ,是在函数的形参中进行定义,只要定义了rest参数,那么此参数永远是个数组
// 是为了代替arguments(接收所有的实参:它是伪数组)
//1、rest 完善了arguments,并且还可以接收剩余参数 。
function fn1( ...arg ){// 接收所有的参数
console.log( arg )
}
fn1( )//就算没有实参,rest参数也是个数组,只不过是个空数组
fn1( 10,20 )//[ 10,20 ] 接收所有的参数
//2、rg : 接收剩余参数 。
function fn2( a,b,...arg ){
console.log( a,b,arg )
}
// fn2( 10,20,30,40 )
fn2( 10,20 )
扩展运算符(spread)
- 展开数据
- 可以直接展开数组
- 展开对象,不可以直接展开对象,要在{}里展开。
函数传参
function fn1(a,b){
console.log( a,b )
}
// fn1( 10,20 )
// fn1( [11,22] )//a:[11,22] b:undefined
fn1( ...[111,222] ) //...spread参数 展开数据 // => fn1( 111,222 )
// console.log( 1,2,2,3 )
// console.log( ...[11,22])
实现浅拷贝(展开对象或数组)
let obj1 = { a:1,b:2,c:[10,20] }
let obj2 = { ...obj1 } //实现了浅拷贝
obj1.a = 10
obj1.c[0] = 100 //注意:因为是浅拷贝,所以obj2中的c数组也会跟着改变
console.log( obj1,'obj1' )
console.log( obj2,'obj2' )
数组合并/伪数组转数组
<button>按钮1</button><button>按钮2</button><button>按钮3</button>
<script>
//1. 数组合并: concat
//...
// let arr1 = [ 50,40,33 ]
// let arr2 = [ 111,20 ]
let arr3 = [ ...arr1,...arr2 ]
// console.log( arr3,'实现数组合并' )
//2. 伪数组转数组
let oBtns = document.querySelectorAll('button')
oBtns = [ ...oBtns ]
console.log( oBtns ,'转换成功 ') // Array.from Array.prototype.slice.call( 伪数组 )
</script>
展开对象小技巧( 加薪优化 )
let persons = [
{
username:'张飞',
sex:'男',
salary:50000
},
{
username:'关羽',
sex:'男',
salary:60000
}
]
let newPersons = persons.map( function(value){
//return { username:value.username,sex:value.sex,salary:value.salary+2000 }
return { ...value,salary:value.salary+2000 } //每天一个涨薪小技巧
} )
console.log( newPersons,'newPersons' )
console.log( persons,'persons' )
函数扩展
箭头函数基础用法
箭头函数不会更改this 指向,用来指定回调函数会非常合适,且没有function关键字,无函数名,写起来更加简洁。99%的场景是用在回调中。
特点
-
如果形参只有一个,则小括号可以省略
-
函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
-
箭头函数 this 指向声明时所在作用域下 this 的值 (即上一级作用域)
-
箭头函数不能作为构造函数实例化
-
不能使用 arguments
参数特性
没有参数、一个参数、多个参数
//es6为我们提供了箭头函数的用法 。
//1. 箭头函数没有function关键字,无函数名,写起来更加简洁。99%的场景是用在回调中。
// let fn1 = ()=>{
// console.log('my is fn1 function')
// }
// fn1()
//2. 回忆回调的形式。
// function A( cb ){
// cb()
// }
// function B(){
// console.log('my is B')
// }
// A( B ) //函数B是A函数的回调,回调函数一定是在另一个函数内部执行的。
//3. 运动框架中,数组的很多函数都有回调: forEach map filter some every ...
//4. 没有参数、一个参数、多个参数
//4.1 如果箭头函数只有一个形参,那么箭头函数的小括号可以省略。
// let fn2 = (a)=>{
// console.log( a,'只有一个形参' )
// }
// let fn2 = a=>{
// console.log( a,'只有一个形参' )
// }
// fn2(20)
//4.2 多个参数:无特殊说明
// let fn3 = (a,b)=>{
// console.log( a,b,'多个形参' )
// }
// fn3( 20,30 )
返回值特性
//5.1 完整的不省略return
let fn4 = ()=>{
return 20
}
console.log( fn4() ) //20
//5.2 省略return关键字:前提条件(函数内只有一行代码,那么可以省略return,同时也必须省略花括号)
let fn5 = ()=> true
console.log( fn5() )//true
//5.3 返回对象的问题,需要使用 () 解决。
let fn6 = ()=>( { username:'zs',age:20 } )
console.log( fn6() )//{username: 'zs', age: 20}
不做为构造函数
//1. 不做为构造函数,也就是说不能实例化
let fn1 = ()=>{
}
// new fn1()// 报错:fn1 is not a constructor
函数参数默认值
//为某个元素设置背景色,如果未背景色默认为红色
// function setEleBgColor( ele,bgColor='blue' ){ //默认值为blue
// ele.style.backgroundColor = bgColor
// }
let setEleBgColor = ( ele,bgColor='green' )=>{//默认值为green
ele.style.backgroundColor = bgColor
}
let oBtn = document.getElementById('btn')
setEleBgColor( oBtn )
// setEleBgColor( oBtn,'yellow' )
箭头函数没有arguments
箭头函数无arguments,使用...rest代替。
let fn2=( ...arg )=>{
//console.log( arguments )//arguments is not defined
console.log( arg )//[ 20,10 ]
}
fn2( 20,10 )
箭头函数的this指向(本身无this指向)
//箭头函数中的this永远指向它上级所处的this是谁。即箭头函数 this 指向声明时所在作用域下 this 的值
// 案例1
let fn1 = ()=>{
console.log( this,'my fn1' )
}
// fn1() //window
// 案例2
// document.onclick = function(){
// console.log( this,'事件' )//document
// }
// console.log( this,'全局' )
// document.onclick = ()=>{
// console.log( this,'事件' )//window 此箭头函数所处在全局中,指向window
// }
// 案例3
function Person(){
this.username = 'zs'
let fn = ()=>{
console.log( this,'fn函数' )
}
fn()
}
// Person() //this => window window.Person()
// new Person() //this=> Person的实例对象 new 改变的是Person函数中的this指向 。
// Person.call( document ) //this = > document
//
// 案例4
let fn2 = ()=>{
console.log( this )
}
fn2.call( document )//window
关于环境的补充
// 只能是全局或函数内才有上下文环境 。
let obj = {
fn1:function(){
console.log( this,'fn1' )
},
fn2(){
console.log( this,'fn2' )
},
fn3:()=>{
console.log( this,'fn3' )
}
}
obj.fn1() //obj
obj.fn2() //obj
obj.fn3() //window
类class
面向对象编程
概念
面向对象是一种以对象为中心的编程思想。面向对象是相对于面向过程来讲的,面向对象把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模。
面向过程
面向过程思想强调的是步骤,当碰见问题时,思考的是“我该怎么做”,分析出解决问题所需要的步骤,一步步的去实现。
例如:想吃蛋炒饭,首先想到的是我要如何做,包括哪些步骤。比如:起锅烧油,加入鸡蛋,加入米饭,加入调料,翻炒,装盘等。
面向对象
面向对象思想强调的是对象,当碰见问题时,思考的是“我该让谁来做”。这个“谁”其实就是对象。找合适的对象做合适的事情。而对象如何去做(采用什么样的步骤)我们就不关心了,最终把问题解决掉就可以了。
例如:还是想吃蛋炒饭,首先想到的是让谁来帮我做蛋炒饭,比如找厨师来帮我做蛋炒饭。具体厨师如何去做这个蛋炒饭,做饭的步骤是怎么样的我们并不关心。只要最终把蛋炒饭做好就可以了。
类与对象
现实中:先有对象,再归类 。
程序中:先有类(构造函数),再有具体实例对象
-
class 声明类
-
constructor 定义构造函数初始化
-
extends 继承父类
-
super 调用父级构造里的属性和方法
-
static 定义静态方法和属性
-
父类方法可以重写
class的基本使用
代码示例:
//class : 就是定义类的。
// 定义有属性和方法的类
class Person{
username = 'zs'
age = 20
say2 = ()=>{ //这叫定义了一个say2属性,里面存储了一个函数
console.log( this) //注意箭头函数this指向
}
say1(){ //它会自动加到原型上。
console.log( this )
}
}
let p1 = new Person()
// console.log( p1,'p1对象' )
p1.say1() //p1
p1.say2() //p1
构造器函数方式定义属性
class Person{
constructor( username,age ){ //实例化的时候,可以传递实参
// 这是类中的固定的函数,不能手动调用,每实例化一次会调用一次
this.uname = username
this.uage = age
}
say(){
console.log('say方法')
}
}
// let p1 = new Person()
// let p2 = new Person()
let p3 = new Person( 'zs',20 )
console.log( p3 ,'实例化完成')
静态属性和静态方法
代码示例1:函数模拟静态
function fn(){} //引用类型
fn.aa = '我是aa属性' //这就是静态属性
// console.log( fn.aa )
let fnObj = new fn()
console.log( fnObj.aa )//undefined
代码示例2:类中的static关键字语法
// 使用static关键字修饰静态属性或方法
//这个属性或方法不想操作对象,那么可以用static进行修饰
class Person{
static a = '20'
static fn(){
console.log('my is fn函数')
}
}
console.log( Person.a )
Person.fn()//'my is fn函数'
// let p1 = new Person()
// console.log( p1 );
继承
Class 可以通过extends
关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
基本用法
代码案例:
//继承的原则 :1. 子类继承父类的所有特性 2. 不能影响父类
class Person{ //父类
username = 'zs'
say(){
console.log('say 方法')
}
}
//extends : 就是继承的意思 。
class Student extends Person {
xuehao = 'a0001'
zuozuoye(){
console.log( '做作业' )
}
}
let stu1 = new Student()
// console.log(stu1.username) //zs
// stu1.say() //方法也继承了
// console.log( stu1,'学生对象' )
let p1 = new Person()
// console.log( p1 )
p1.zuozuoye()//zuozuoye is not a function : 这是对的。
super的用法
代码案例:
//完整的继承写法 。super的用法 。
class Person{ //父类
constructor( username,age ){
this.username = username
this.age = age
}
call(){
log('父级');
}
}
//子类
// class Student extends Person{
//1. 子类可以不写constructor,那么实例化子类时候,实参会自动传递给父类的constructor
// }
class Student extends Person{
//2. 子类如果写constructor,那么必须在子类的constructor中的第1行写super调用 。
constructor( username,age,xuehao ){
super()//父级方法自动调用不需要传参。
super(username,age)//必须传参调用父类的构造函数里的属性
this.xuehao = xuehao
}
//方法重写
call(){
log('子级');
}
}
let stu1 = new Student( 'zs',15,'a0002' )
console.log( stu1 )
面向对象的应用场景
JavaScript的底层就是面向对象思想封装的。
Jquery使用原理
//https://cdn.bootcdn.net/ajax/libs/jquery/1.9.1/jquery.min.js
//$() 为jquery的对象
// $.extend
$.prototype.aa = 123345
let jq = $()
console.log( jq.aa ) //123345
四、iterator 迭代器
简介
Iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制, 遍历器, 因为现在有Array / Object / Set / Map 4种结构, 所以ES6 加入了Iterator遍历器, 只要拥有这个Iterator遍历器, 就都可以使用for…of进行遍历
作用
- 为各种数据结构,提供一个统一的、简便的访问接口
- 使得数据结构的成员能够按某种次序排列
- ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of使用
- Iterator是一个抽象的概念, 具体的实现 for…of / Symbol.iterator
工作原理
-
创建一个指针对象,指向当前数据结构的起始位置
-
第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
-
接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
-
每调用 next 方法返回一个包含 value 和 done 属性的对象
注:需要自定义遍历数据的时候,要想到迭代器。
for…of
代码案例:
//Iterator:迭代、遍历、循环、递归
//for while do...while for...in each
//es6觉得以前遍历方式太多了。就统一了遍历方式( for...of )
//前提条件是:数据的原型上都有 Symbol.iterator才可以使用 for...of。
//数组、字符、arguments、set、map、元素集合。唯独没有object
// for( let 变量 of 可遍历的数据 ){
// //变量就是当前的循环项
// }
let arr1 = [10,5,20]
// for( let value of arr1 ){
// console.log( value,'--' )
// }
console.log( arr1,'数组身上有Symbol.iterator' )
let set1 = new Set( [50,10,20,30,10] )
// for( let value of set1 ){
// console.log( value,'--' )
// }
console.log( set1,'Set身上有Symbol.iterator' )
let obj1 = {
username:'zs',
age:20
}
console.log( obj1,'对象身上没有Symbol.iterator' )
// for( let value of obj1 ){
// //console.log( value,'--' )
// }
注意事项:在使用for…of遍历数据时, 要确保被遍历的数据, 拥有Iterator功能
数组和Iterator
代码案例:
//分析数组身上的即可,因为每个数据身上的Symbol.itertaor都一样
let arr = [10,20,30]
// console.log( arr )
// console.log( typeof arr[Symbol.iterator] )
let iter = arr[Symbol.iterator]() //Symbol.iterator是个函数, 它返回一个(指针)对象
//console.log( iter,'这是一个指针对象' )//此对象有一个非常重要的next方法。
console.log( iter.next() )//{value: 10, done: false}
console.log( iter.next() )//{value: 20, done: false}
console.log( iter.next() )//{value: 30, done: false}
console.log( iter.next() )//{value: undefined, done: true}
//for...of 会自动调用 arr[Symbol.iterator](),再自动调用 next(),会根据done:tue的时候跳出循环。
为对象部署iterator接口
代码案例:
let obj = {
username:'zs',
age:20,
sex:'男'
}
obj[ Symbol.iterator ] = function(){
let values = Object.values( obj )
// console.log( values,'---' )// ['zs', 20,'男']
let index = 0 //控制数组的索引下标
//这里最重要的一个知识点是闭包。函数套函数,函数内部可以访问外部的变量并且不销毁
return {
next:function(){
if( index >= values.length ){
return {value: values[index++], done: true}
}else{
return {value: values[index++], done: false}
}
}
}
}
for( let value of obj ){
console.log( value,'----' )
}
// let iter = obj[ Symbol.iterator ]()
// // console.log( iter,'指针' )
// console.log( iter.next() )//{value: zs, done: false}
// console.log( iter.next() )//{value: 20, done: false}
// console.log( iter.next() )//{value: 男, done: false}
// console.log( iter.next() )//{value: undefined, done: false}