JavaScript垃圾回收机制。
- 标记清理
- 引用计数
释放内存 把声明的对象赋值为 null
let obj = {} // 开辟内存
obj.name = 'haha' // 标记内存
obj = null // 释放内存
闭包
let obj = {
name:'哈哈哈',
fn:function(){
return this.name
}
}
obj.fn();
// 释放obj的内存
obj = null;
优点:可以隔离作用域,不造成全局污染,可以让垃圾回收机制不会回收这个变量,让他能一直被标记为被引用。
缺点:由于闭包长期驻留内存,则会导致大量内存永远不会被释放。
如何释放内存:将暴露外部的闭包变量置为 null。
this指向
function fn1(){
console.log(this)
}
fn1(); // window
// window.fn1();
let obj = {
name:'哈哈哈',
fn:function(){
return this.name
}
}
obj.fn() // obj
<div id="dom">你好</div>
let dom = document.getElementById('dom')
dom.addEventListener('click'function(){
console.log(this) // dom
}
)
setTimeout(()=>{
console.log(this) // window
})
setInterval(()=>{
console.log(this) // window
})
var num = 2
var obj = {
num:1,
fn:function(){
console.log(this.num); // 1
}
}
// 如果是箭头函数,那么会把this指向他的父级作用域
{
var num = 2
var obj = {
num:1,
fn:() => {
console.log(this.num); // 2
}
}
}
箭头函数的特点
1、没有构造函数,不能new实例对象
2、本身没有this指向,this指向父级
3、不能使用argumetns
4、没有原型
// 箭头函数使表达更加简洁,隐式返回值
// 正常的写法
const isEven = function(n){
return n % 2 === 0;
}
const square = function(n){
return n * n;
}
const isEven = n => n % 2 === 0;
const square = n => n * n;
// 箭头函数的一个用处是简化回调函数
// 正常函数写法
[1,2,3].map(function (x) {
return x * x;
});
// 箭头函数写法
[1,2,3].map(x => x * x); //[1,4,9]
var let 和 const 区别
// var 的变量会挂载到 window上
var a = 1 // window.a
console.log(a); // 1
console.log(window.a); // 1
let b = 2; //
console.log(b); // 2
console.log(window.b); // undefined
const c = 3; //
console.log(b); // 3
console.log(window.b); // undefined
// var 可以被重复声明,let const 不可以,如果重复声明会报错
var a = 1;
var a = 100;
console.log(a) // 100
let b = 2 ;
let b = 200 ;
// Uncaught SyntaxError: Identifier 'b' has already been declared
let const = 3 ;
let const = 300 ;
// Uncaught SyntaxError: Identifier 'c' has already been declared
// var有变量提升, let 和 const 没有
console.log(a) // undefined
var a = 10;
console.log(b)
let b = 20;
//Uncaught ReferenceError: can't access lexical declaration 'b' before initialization
console.log(c)
let c = 20;
//Uncaught ReferenceError: can't access lexical declaration 'c' before initialization
// let const 生成块级作用域
if(1){
var a = 100;
let b = 10
console.log(b)
}
function fn(){
return {
a:100
}
}
fn()
console.log(a);
console.log(b)
// let const 会形成一个暂时性死区
var a = 100
function fn(){
console.log(a);
}
fn();
//
var a = 100
function fn(){
console.log(a);
let a = 10;
}
fn(); // 暂时性死区
// 1、一旦声明必须赋值。
// 2、声明后不能再修改
// 3、如果声明的是复合类型数据,可以修改其属性
const a = 10;
a = 100; // 报错
const arr = [];
arr = [1,2] // 报错,改变了内存指针的指向
arr.push(1); // [1],只是修改指针指向的内存地址里的属性
let obj = {}
obj = {name:'tan'} // 报错
obj.name = 'tan' // 不报错,内存指针未变,只改变了内存里的属性
js内存存储机制
基本数据类型:bigInt、string、number、boolean、null、undefined、symbol
引用数据类型:Object、Function、Array
基本数据类型放在栈内存里面
引用数据类型有一个占位牌在栈内存里,真实的数据在堆内存中,并且占位牌有一个指针指向对应的内存空间。
这是为什么const 可以对原引用数据类型进行修改,而不能直接重新赋值一个新对象的原因,第一种指针未发生改变,第二种指针指向新的内存地址所以会报错。
js的值传递和引用传递
function setAge(i) {
alert(i); // 24
i = 18;
alert(i);//18,i的改变不会影响外面的age
};
let age = 24;
setAge(age);
alert(age); // 24
js的全局变量和局部变量
var a = 10;
function fn(){
var a = 20;
}
console.log(a);
------------------------
var a = 10;
function fn(){
console.log(a) // undefined
var a = 20;
}
fn();
console.log(a); // 10
------------------------
var a = 10;
function fn(){
// Uncaught ReferenceError: can't access lexical declaration 'a' before initialization
console.log(a)
let a = 20;
}
fn();
console.log(a);
------------------------
var a = 10;
function fn(){
a = 20;
}
fn();
console.log(a); // 20
------------------------
var a = 10;
function fn(a){
a = 20;
}
fn();
console.log(a); // 10
------------------------
Promise的使用
有两个接口,第二接口的数据依赖于第一个接口
console.log(1)
console.log(2)
// 1 -> 2
get(/list),then(res=>{
this.id = res.id
}).catch(err=>{
})
get(/list/user).then(res2=>{
if(this.id){
console.log(res2)
}
})
// 回调地狱
get(/list),then(res=>{
get(/list/user).then(res2=>{
if(res.id){
console.log(res2)
}
})
}).catch(err=>{
})
----------------------------
console.log(1)
let p = new Promise ((resolve,reject)=>{
setTimeout(()=>{
console.log(2)
resolve(true)
},1500);
})
p.then(res=>{
if(res){
console.log(3);
}
})
----------------------------
console.log(1)
let p = new Promise ((resolve,reject)=>{
setTimeout(()=>{
console.log(2)
resolve(1) // then
// reject(2) // catch
},1500);
})
p.then(res=>{
console.log(res)
if(res){
console.log(3);
}
}).catch(err=>{
console.log(err);
}).finally(res=>{
console.log('finally')
})
---------------------------- Promise.all
返回多个promise的结果,形成一个数组
let p1 = new Promise ((resolve,reject)=>{
setTimeout(()=>{
resolve(1)
},1500);
})
let p2 = new Promise ((resolve,reject)=>{
setTimeout(()=>{
resolve(2)
},0);
})
Promise.all([p1,p2]).then(res=>{
console.log(res)
})
---------------------------- Promise.race
返回多个promise中最快的那一个,其他不返回
let p1 = new Promise ((resolve,reject)=>{
setTimeout(()=>{
resolve(1)
},1500);
})
let p2 = new Promise ((resolve,reject)=>{
setTimeout(()=>{
resolve(2)
},0);
})
Promise.race([p1,p2]).then(res=>{
console.log(res)
})
---------------------------- Promise 联用
new Promise((resolve,reject)=>{
resolve(1);
}).then(res =>{
// console.log(res);
return res
}).then(res=>{
console.log(res);
return res
})
async 和 await
async function fn(){
return new Promise((resolve,reject)=>{
resolve(1)
})
}
async function test(){
let p = await fn();
console.log(p)
}
test();
----------------------------
let p1 = new Promise((resolve,reject)=>{
resolve(1)
})
let p2 = new Promise((resolve,reject)=>{
resolve(2)
})
let p3 = new Promise((resolve,reject)=>{
resolve(3)
})
async function test(){
let p = await Promise.all([p1,p2,p3]);
console.log(p)
}
async function test(){
let p = await Promise.all([p1,p2,p3]);
console.log(p)
if(p[0]===1&&p[1]===2){
console.log(p[2]);
}
}
test();
----------------------------
async function test(){
let pone = await p1;
// console.log(pone)
let ptwo = await p2;
// console.log(ptwo);
if(pone===1&&ptwo===2){
let pthree = await p3;
console.log(pthree)
}
}
js的继承方式
// 原型链继承
var Base = function(){
this.level = 1;
this.name = "base";
this.toString = function(){
return "base"
}
}
Base.CONSTANT = 'constant'
var Sub = function(){
}
Sub.prototype = new Base();
console.log(Sub.prototype.name)
// 缺点不能打包继承父构造函数的属性,需要通过原型链一个个的去找