目录
垃圾回收机制
其实最主要的主要是:
全局变量不会自动回收,只会在页面关闭的时候回收,所以要少使用全局变量;
局部变量会自动回收,使用完就会自动回收了
引用计数
实时的计算内存的引用次数,当其变为0时就销毁内存
但是有缺点:嵌套引用会造成内存泄漏的问题:
function fn(){
let o1 = {}
let o2 = {};
o1.a = o2;
o2.a = o1
}
fn();
代码结束时,o2 o1都有被引用次数,所以使用引用计数不会销毁内存。所以现在的浏览器大多不采用这种方法。
标记清除法
闭包
例如:
function fn(){
let a = 1;
function f2(){
console.log(a);
}
f2()
}
fn()
内层函数 f2 调用了外层函数变量 a 吗,这是最简单的写法
最基础的写法为:
闭包的最大作用是外部可以访问函数内部的变量!!
function fn(){
let a = 1;
function f2(){
return a
}
return f2
}
let sw = fn()()
console.log(sw)//1
也就是说函数fn的返回值为f2函数,所以fn()==f2,fn()()==f2()从而使得外部可以访问a
应 用:使用变量a表示函数f2被调用的次数:
function fn(){
let a = 0;
function f2(){
a++
console.log(a);
}
return f2
}
let sw = fn()
sw()
这样的好处是,a是函数内部的变量,外部不能直接修改这个值,只能通过f2得到,实现了数据私有
变量提升
这是js中的一种‘缺陷’,允许在变量声明之前被访问(存在于var)
比如:
console.log(num+'个');
var num = 10
结果为undefined个
其本质为:
var num
console.log(num+'个');
num = 10
把所有的var声明的变量提升到当前作用域的最前面,但是只提升声明,不提升幅值
let const没有变量提升的问题
动态参数和剩余参数
需求:求不定数量的数的和:
function add(){
let sum = 0;
for(let i = 0;i<arguments.length;i++){
sum+=arguments[i];
}
console.log(sum);
}
add(2,3,4,5,9)
arguments就称为动态参数,将输入看成一个数组 (伪数组,不能用pop push等操作)
第二种方法:
function add(...args){
let sum = 0;
console.log(args);
for(let i = 0;i<args.length;i++){
sum+=args[i];
}
console.log(sum);
}
add(2,3,4,5,9)
...args就为剩余参数,也是将输入看成一个数组(真数组,可以使用pop等操作) ,但是其优点在于可以提取剩余变量,例如刚刚的那个例子,我再加一个需求:最少有两个参数:
function add(a,...args){
let sum = 0;
console.log(args);
for(let i = 0;i<args.length;i++){
sum+=args[i];
}
console.log(sum);
}
add(2,3,4,5,9)
这样的话,会把2给a ,【3,4,5,9】给agrs,需要注意的是...args只能放最后。
展开运算符
作用:把数组展开:
const arr = [2,3,4,6];
console.log(...arr);//2 3 4 6
经典应用为,得到数组的最大值(因为数组是不能直接使用math.max的):
const arr = [2,3,4,6];
console.log(Math.max(...arr));//6
或者,合并数组:
const arr = [2,3,4,6];
console.log([3,4,...arr,99]);//[3, 4, 2, 3, 4, 6, 99]
箭头函数(重要)
主要作用是 使得代码更简洁,且改变this的指向
基本形式:
const fn = (x)=>{
console.log(x);
}
fn(3)//3
如果只有一个形参,可以忽略小括号:
const fn = x=>{
console.log(x);
}
fn(3)//3
当函数只有一行代码时 可以省略大括号:
const fn = x=>console.log(x);
fn(3)//3
如果是一行代码 且是return,可以省略:
const fn = x=>x+x;
console.log(fn(3));//6
箭头函数没有arguments,只有...args:
const fn = (...args)=>{
let sum = 0;
for(let i = 0;i<args.length;i++){
sum+=args[i];
}
return sum
}
console.log(fn(3,4,5,7));//19
this指向
一般来说,this是谁调用函数 ,this指向谁
但是箭头函数不会创造自己的this,而是从自己作用域的上一层沿用this,这个地方this是静态的,是函数声明时所在作用域下的this值
let btn = document.querySelector('button');
btn.addEventListener('click',function(){
console.log(this);//btn
})
btn.addEventListener('click',()=>{
console.log(this);//window
})
第一个的this 指向调用者btn
第二个this 指向的是箭头函数声明时的作用域,也就是上一个作用域,既window
经典应用:需求是点击一个方块 一秒后背景颜色变红
let divs = document.querySelector('.box')
divs.addEventListener('click',function(){
setTimeout(()=>{
this.style.backgroundColor = 'red' //这个this还是指向divs
},1000)
})
但是如果setTimeout里面的函数还是用function,那么this指向的是window,因为是window调用了setTimeout
数组解构
数组解构是将数组的单元值快速批量赋值给变量的方法
const arr = [123,22,33]
const[max,min,avr] = arr;
console.log(max);//123
应用:变量值交换
let a = 1;
let b = 2;
[a,b]=[b,a]
console.log(a);//2
console.log(b);//1
变量多 单元值少的情况
const[a,b,c,d] = [1,2,3]
console.log(a,b,c,d);//1 2 3 undefined
变量少 单元值多的情况
const[a,b] = [1,2,3]
console.log(a,b);//1 2
对象解构
const person = {
name :'aa',
age:17
}
const{name,age} = person;
console.log(name);//aa
console.log(age);//17
可以改名:
const person = {
name :'aa',
age:17
}
const{name:username,age} = person;
console.log(username);//aa
console.log(age);//17
多重对象解构:
const pig = {
name: 'aa',
family:{
mom :'bb',
papa:'cc',
sist:'dd'
},
age:18
}
const {name,family:{mom,papa,sist}}=pig;
console.log(mom);//bb
遍历数组forEach方法
const arr = [11,22,33];
arr.forEach(function(item,index){
console.log(item);
console.log(index);
})
结果为:11 0 22 1 33 2
item必须要写 index可以不写
贴一个小案例 可以看一下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>商品渲染</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.list {
width: 990px;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
}
.item {
width: 240px;
margin-left: 10px;
padding: 20px 30px;
transition: all .5s;
margin-bottom: 20px;
}
.item:nth-child(4n) {
margin-left: 0;
}
.item:hover {
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
transform: translate3d(0, -4px, 0);
cursor: pointer;
}
.item img {
width: 100%;
}
.item .name {
font-size: 18px;
margin-bottom: 10px;
color: #666;
}
.item .price {
font-size: 22px;
color: firebrick;
}
.item .price::before {
content: "¥";
font-size: 14px;
}
.filter {
display: flex;
width: 990px;
margin: 0 auto;
padding: 50px 30px;
}
.filter a {
padding: 10px 20px;
background: #f5f5f5;
color: #666;
text-decoration: none;
margin-right: 20px;
}
.filter a:active,
.filter a:focus {
background: #05943c;
color: #fff;
}
</style>
</head>
<body>
<div class="filter">
<a data-index="1" href="javascript:;">0-100元</a>
<a data-index="2" href="javascript:;">100-300元</a>
<a data-index="3" href="javascript:;">300元以上</a>
<a href="javascript:;">全部区间</a>
</div>
<div class="list">
<!-- <div class="item">
<img src="" alt="">
<p class="name"></p>
<p class="price"></p>
</div> -->
</div>
<script>
const goodsList = [
{
id: '4001172',
name: '称心如意手摇咖啡磨豆机咖啡豆研磨机',
price: '289.00',
picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
},
{
id: '4001594',
name: '日式黑陶功夫茶组双侧把茶具礼盒装',
price: '288.00',
picture: 'https://yanxuan-item.nosdn.127.net/3346b7b92f9563c7a7e24c7ead883f18.jpg',
},
{
id: '4001009',
name: '竹制干泡茶盘正方形沥水茶台品茶盘',
price: '109.00',
picture: 'https://yanxuan-item.nosdn.127.net/2d942d6bc94f1e230763e1a5a3b379e1.png',
},
{
id: '4001874',
name: '古法温酒汝瓷酒具套装白酒杯莲花温酒器',
price: '488.00',
picture: 'https://yanxuan-item.nosdn.127.net/44e51622800e4fceb6bee8e616da85fd.png',
},
{
id: '4001649',
name: '大师监制龙泉青瓷茶叶罐',
price: '139.00',
picture: 'https://yanxuan-item.nosdn.127.net/4356c9fc150753775fe56b465314f1eb.png',
},
{
id: '3997185',
name: '与众不同的口感汝瓷白酒杯套组1壶4杯',
price: '108.00',
picture: 'https://yanxuan-item.nosdn.127.net/8e21c794dfd3a4e8573273ddae50bce2.jpg',
},
{
id: '3997403',
name: '手工吹制更厚实白酒杯壶套装6壶6杯',
price: '99.00',
picture: 'https://yanxuan-item.nosdn.127.net/af2371a65f60bce152a61fc22745ff3f.jpg',
},
{
id: '3998274',
name: '德国百年工艺高端水晶玻璃红酒杯2支装',
price: '139.00',
picture: 'https://yanxuan-item.nosdn.127.net/8896b897b3ec6639bbd1134d66b9715c.jpg',
},
]
let list = document.querySelector('.list');
function get(arr) {
let str = ``
arr.forEach((item, index) => {
let { id, name, price, picture } = item;
str += `<div class="item" id=${id}>
<img src=${picture} alt="">
<p class="name">${name}</p>
<p class="price">${price}</p>
</div>`
});
list.innerHTML = str;
}
get(goodsList)
let filter = document.querySelector('.filter').querySelectorAll('a');
for(let i=0;i<filter.length;i++){
filter[i].addEventListener('click',function(){
if(this.getAttribute('data-index')==1){
arr = goodsList.filter(item => item.price > 0 && item.price <= 100)
}
else if(this.getAttribute('data-index')==2){
arr = goodsList.filter(item => item.price > 100 && item.price <= 300)
}
else if(this.getAttribute('data-index')==3){
arr = goodsList.filter(item => item.price > 300)
}
else{
arr = goodsList;
}
get(arr)
})
}
</script>
</body>
</html>
创建类
需要使用constructor函数来声明参数
class fa{
constructor(x,y){
this.x = x;
this.y = y;
}
sum(){
console.log(this.x+this.y);
}
}
let a = new fa()
子类继承:
利用extends
class fa{
constructor(x,y){
this.x = x;
this.y = y;
}
sum(){
console.log('hhhh');
}
}
class son extends fa{}
let b = new son()
b.sum()//hhhh
super调用父类函数
如果想将子类的参数传到父类并调用父类的函数,需要使用super方法,主要就是可以将子类的参数传给父类
class fa{
constructor(x,y){
this.x = x;
this.y = y;
}
sum(){
console.log(this.x + this.y);;
}
}
class son extends fa{
constructor(x,y){
super(x,y)
}
}
let a = new son(7,4)
a.sum()//11
如果子类本身也有参数和函数,也可以声明super,但是需要注意的是,super必须在this之前
class fa{
constructor(x,y){
this.x = x;
this.y = y;
}
sum(){
console.log(this.x + this.y);
}
}
class son extends fa{
constructor(x,y){
super(x,y)
this.x = x;
this.y = y;
}
jian(){
console.log(this.x - this.y);
}
}
let a = new son(7,4)
a.jian()//3
a.sum()//11