解构赋值
是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值
数组解构
将数组里的值批量赋值给变量
let a,b,c=[1,2,3]
对象解构
将对象的属性和方法快速批量赋值给一系列变量
注:
- 对象属性的值将被赋值给与属性名相同的变量(属性名===变量名)
- 解构的变量名不要和外面的变量名冲突否则报错
- 对象中找不到与变量名一致的属性时变量值为undefine
// 1
let {uname,age}={uname:'feng',age:18}
// 2
let user={
address:'china',
hobby:'dance'
}
let {address,hobby}=user
//对象解构的变量名可更改 旧变量名:新变量名
let {uname:username,age}={uname:'feng',age:18}
// 例子
const pig={name:'peiqii',age:16}
//const {name,age}=pig
const {name:uname,age}=pig
console.log(uname)
console.log(age)
// 数组对象
const goods=[
{
goodsName:'小米',
price:199
}
]
const [{goodsName,price}]=goods
console.log(goodsName)
console.log(price)
多级对象解构
const pig={
uname:'sd',
family:{
mom:'mama',
fa:'baba',
sis:'jie'
},
age:6
}
const {uname,family:{mom,fa,sis},age}=pig
console.log(uname,mom,fa,sis,age);
forEach
遍历数组的每个元素,并将元素传递给回调函数
(只能遍历数组)
与map的区别:
- map返回一个新数组
- foreach只遍历不返回数组
filter 筛选数组
创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
使用场景:筛选数组符合条件的元素,并返回筛选之后元素的新数组
const arr=[10,20,30]
const newa=arr.filter(function(item,index){
return item>=20
})
console.log(newa);
// 简化
const newArr=arr.filter(item=>item>=20)
console.log(newArr);
创建对象的三种方式
- 对象字面量
const obj={
name:'明'
}
- 利用 new Object 创建
const obj=new Object({uname:'pink})
- 利用构造函数创建对象
构造函数
特殊的函数,主要用来初始化对象
使用场景:可通过构造函数来快速创建多个类似的对象
- 约定
- 命名以大写字母开头
- 它只能由new来执行
function Pig(name,age,gender){
this.name=name
this.age=age
this.gender=gender
}
// 创建对象
const peppa=new Pig('pepp',6,'女')
const dad=new Pig('pdad',27,'男')
const mum=new Pig('pmom',24,'女')
说明:
new实例化执行过程
1、创建新的空对象
2、构造函数this指向新对象
3、执行构造函数代码,修改this,添加新属性
4、返回新对象
实例成员和静态成员
通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员
- 实例对象是相互独立
构造函数的属性和方法称为静态成员
注:
- 静态成员只能构造函数来访问
- 静态方法中的this指向构造函数
function Pig(uname,age){
this.uname=uname
this.age=age
}
Pig.eyes=2
Pig.sayhi=function(){
console.log(this);
}
Pig.sayhi()
内置构造函数
其实字符串、数值、布尔等基本类型也都有专门的构造函数,这些我们称为包装类型
const str='pink'
console.log(str.length)
// js 底层完成,把简单数据类型包装为引用数据类型
const str1=new String('pink')
- 引用类型
- Object
- Array
- RegExp
- Date
- 包装类型
- String
- Number
- Boolean
Object
创建普通对象
常用的静态方法:
- Object.keys(对象)
const o={uname:'mini',age:18}
console.log(Object.keys(o)); // 返回为数组形式
console.log(Object.values(o));
- Object.assign()
对象的拷贝,经常用于给对象添加属性
const o={uname:'mini',age:18}
const obj={}
Object.assign(obj,o)
console.log(obj);
console.log(obj,{gender:'女'});
Array
- 数组常见的实例方法(核心)
// reduce实现数组求和
const arr=[1,5,8]
// 1\没有初始值
const total=arr.reduce(function(pre,current){
return pre+current
})
// 2\有初始值
const total1=arr.reduce(function(pre,current){
return pre+current
},10)
reduce过程:
1、若没有起始值,则上一次值以数组的第一个数组元素的值
2、每一次循环,把返回值给做为下一次循环的上一次值
3、若有起始值,则起始值作为上一次值
// 如果是数组对象,必须添加初始值
const arr1=[{
name:'feng',
salary:1000
},{
name:'xiang',
salary:1000
},{
name:'duan',
salary:1000
}]
const final=arr1.reduce(function(prev,current){
console.log(prev,current);
return prev+current.salary
},0)
console.log(final);
- 数组其他方法
// 小案例
// 获取所有属性,拼接成字符串
const spec={size:'40*40cm',color:'blank'}
const newA= Object.values(spec).join('/')
console.log(newA);
伪数组转化为真数组
- 伪数组没有pop()方法
String
includes 区分是大小写的
Number
- toFixed() 设置保留两位小数,四舍五入
编程思想
面向过程:分析解决问题的步骤,然后用函数一步一步实现,使用的时候一次一次调用
面向对象:把事务分解成一个个对象,然后由对象之间分工与合作。根据对象功能划分,而不是步骤
- 灵活、代码复用、易维护和开发
- 特性
- 封装性
- 继承性
- 多态性
构造函数
JS实现面向对象需通过构造函数实现
构造函数 存在浪费内存的问题
希望 所有对象使用一个函数
原型
是构造函数的一个对象。称prototype为原型对象
- 构造函数通过原型分配的函数是所有对象所共享的
- js规定,每一个构造函数都有一个prototype属性,指向另一个对象,所以我们也称为原型对象
- 这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存
- 我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法
- 构造函数和原型对象中的this都指向实例化对象
公共的属性写在构造函数中
==公共的方法写在原型对象上 ==
- 原型作用
- 共享方法
- 可以把那些不变的方法直接定义在prototype对象上
const arr=[1,2,3]
// 给数组扩展求最大值方法和求和方法
Array.prototype.max=function(){
return Math.max(...this)
// 原型函数中的this指向实例对象
}
console.log(arr.max());
constructor属性
- 每个原型对象里面都有个constructor属性
- 作用:指向该原型对象的构造函数
思考:为什么实例对象可以访问原型对象里面的属性和方法
对象原型
- 对象都会有一个属性__proto__指向构造函数的prototype原型对象,之所以我们对象可以在使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在
let that
function Person(name){
this.name=name
}
Person.prototype.sing=function(){
that=this
console.log('sing');
}
const peppa=new Person('佩奇')
peppa.sing()
console.log(peppa===that); // 判断构造函数===原型对象
console.log(peppa.__proto__===Person.prototype); // 判断对象原型===原型对象
原型继承
JS中大多是借助原型对象实现继承的特性
- 父构造函数(父类) 子构造函数(子类)
- 子类的原型=new 父类
原型链
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链
查找规则
浅拷贝
拷贝的是地址,适合单层简单数据类型,不适合复杂数据类型
常见方法:
1、拷贝对象:Object.assign()/展开运算符{…obj} 拷贝对象
2、拷贝数组:Array.prototype.concat() 或者 […arr]
let obj={
uname:'niuniu',
age:18
}
let o={...obj} // 已经重新new了一个对象,不会改变原来obj的值
o.age=20
console.log(o); //20
console.log(obj); //18
const o={}
Object.assign(o,obj)
o.age=20
console.log(o);
console.log(obj);
注:
-
直接赋值的方法,只要是对象,都会相互影响,因为是直接拷贝对象栈里面的地址
-
浅拷贝如果是一层对象,不相互影响,如果出现多层对象拷贝会相互影响
-
理解浅拷贝
- 拷贝对象之后,里面的属性值是简单数据类型直接拷贝值
- 如果属性值是引用数据类型则拷贝的是地址
深拷贝
拷贝的是对象,不是地址
常见方法:
- 通过递归实现深拷贝
// 拷贝函数
function deepCopy(newObj,oldObj){
for(let k in oldObj){
if(oldObj[k] instanceof Array){
newObj[k]=[]
// newObj[k] 接受 []
// oldObj[k] ['dance','sing']
deepCopy(newObj[k],oldObj[k])
}
// 要先写Array再写Object,因为数组也属于对象
else if(oldObj[k] instanceof Object){
newObj[k]={}
// newObj[k] 接受 []
// oldObj[k] ['dance','sing']
deepCopy(newObj[k],oldObj[k])
}
else{
newObj[k]=oldObj[k]
}
}
}
- lodash/cloneDeep
<script src="./lodash.min.js"></script>
<script>
const obj={
uname:'pink',
age:18,
hobby:['sing','dance'],
family:{
baby:'小pink'
}
}
const o=_.cloneDeep(obj)
o.family.baby='lao'
console.log(o);
console.log(obj);
</script>
- 通过JSON.stringfy() 实现
const obj={
uname:'pink',
age:18,
hobby:['sing','dance'],
family:{
baby:'小pink'
}
}
// 把对象转换为JSON字符串
console.log(JSON.stringify(obj));
// 再转换成对象时创建了一个新的对象,与原来无关,因此能实现深拷贝
const o=JSON.parse(JSON.stringify(obj))
o.family.baby='lao'
console.log(o);
console.log(obj);
函数递归:在函数内部可以调用其本身,那么这个函数就是递归函数
// 用setTimeout模拟setInterval,用递归方式实现
function getTime(){
document.querySelector('div').innerHTML=new Date().toLocaleString()
setTimeout(getTime,1000)
}
getTime()
异常处理
- throw 抛异常
- try/catch 捕获错误信息
- debugger
处理this
this指向
- 普通函数的调用方式决定了this的值,即【谁调用this指向谁】
- 普通函数没有明确调用者时this值为window,严格模式下没有调用时this的值为undefine
- 对象里面没有this,函数作用域中才有this
改变this指向
- call()
const obj={
uname:'pink',
age:18
}
function fn(x,y){
console.log(this); //window
console.log(x+y);
}
// 1\调用函数
// 2\改变this指向
fn().call(obj,1,2)
- apply()
//apply()
function fn(){
console.log(this);
}
fn.apply(obj,[1,2])
// 使用场景:求数组最大值
// 1写法
//const max=Math.max(1,2,3)
//console.log(max);
// 2写法
const arr=[100,44,22]
const max=Math.max.apply(Math,arr)
const min=Math.min.apply(Math,arr)
// 3写法
console.log(Math.max(...arr));
- bind()(!important)
call 和apply的区别
- 都是调用函数,都能改变this指向
- 参数不一样,apply传递的必须是数组
call apply bind 总结
防抖
单位时间内,频繁触发事件,只执行最后一次
(多次触发,会取消上一次执行,重新计时,执行下一次的)
-
lodash库实现防抖-500毫秒后采取+1
_.debounce(fun,时间) -
手写防抖函数(用定时器)
节流
单位时间内,频繁触发事件,只执行一次
(500ms内,不管触发多少次事件,只执行一次)
使用场景:
高频事件:鼠标移动mousemove、页面尺寸缩放resize7滚动条滚动scroll
核心思路:
function throttle(fn,t){
let timer=null
return function(){
if(!timer){
timer=setTimeout(function(){
fn()
//清空定时器
timer=null
// setTimeout中无法删除定时器,因为定时器还在运作,所以使用timer=null 而不是clearTimeout(timer)
})
}
}
}
box.addEventListener('mousemove',throttle(mouseMove,500))