面试常问题

81 阅读4分钟

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)

// 缺点不能打包继承父构造函数的属性,需要通过原型链一个个的去找