前端面试题之JavaScript

闭包

闭包就是有权访问其他函数内部变量的函数,或者子函数在外调用,子函数所在的父函数的作用域不会被释放。

const fn=()=>{
    let count=0;
    return function(){
        return count++;
    }
}
let c=fn();
console.log(c());//0
console.log(c());//1

防抖与节流

  • 防抖

不让频繁执行

function debounce(fn,space)
{
    let task=null;
    return function()
    {
        //先开启一个定时任务,当再次调用的时候,若定时任务还在存在就清空
        if(task)
        {
            clearTimeOut(task);
        }
        task=setTimeOut(fn.apply(this,arguments),space);
    }
}
  • 节流

可以频繁执行,但是实在一定间隔内频繁执行

function trottle(fn,space)
{
    let task=null;
    return function()
    {
        if(!task)
        {
            task=setTimeOut(()=>{
                task=null;
                fn.apply(this,arguments);
            },space)
        }
    }
}

call apply bind

  • bind(this or obj) 返回的是一个函数必须要调用
  • apply(this,[arguments])
  • call(this,…arguments)

原型链

  • 显示原型prototype

所有的函数包括构造函数都有一个prototype属性,这叫显示原型,是一个对象
记住只有构造函数才有prototype属性就对了

  • 隐式原型__proto__

所有的对象,数组或者函数都有一个__proto__属性,叫隐式原型,也是对象
记住只有对象才有__proto__属性就对了

  • 显示原型和隐式原型相等 Function.ptoto==Function.prototype
对象.__proto__=Constructor.prototype=Constructor{}
Constructor.prototype.__ptoto__=Object.prototype
Object.prototype.__proto__=null

类的继承

  • 原型链继承
function Animal(){}
function Cat(){}
Cat.prototype=new Animal();//原型链继承
Cat.prototype.name="cat";
//无法向父类构造函数传参
  • 构造继承
function Cat(name)
{
    Animal.call(this)
    //此时的this指向父类
    //只能继承父类实例的属性和方法,不能继承父类原型的实例和方法
    //所以  new Cat() instanceof Animal ==false
    //new Cat() instanceof Cat==true
    this.name=name
}
  • 实例继承
function Cat(name)
{
    let cat=new Animal();
    cat.name=name;
    return cat;
}
  • 组合继承(构造继承加原型继承)
function Cat(name)
{
    //构造函数里面加子类的新属性
    Animal.call(this);
    this.name=name
}
Cat.prototype=new Animal();
//既可以继承实例的方法也可以继承原型的方法

解决回调地狱

Promise async/await Generator

懒加载与预加载

  • 懒加载

当访问一个页面的时候,把img元素的背景图换位1*1px的图,之后当img元素出现在浏览器可视区域内时,才让图片显示

  • 预加载

提前加载图片,当用户需要查看时,从本地缓存中渲染

JavaScript的new

var cat = new Animal("cat")
//等价于
{
    var obj={};//创建一个空的临时对象
    obj.__proto__=Animal.prototype;//绑定构造函数的原型
    var result = Animal.call(obj,"cat");//得到构造函数的返回对象
    //若构造函数没有返回对象或者返回的是非对象就返回临时对象
    return typeof result === 'object'?result:obj;
}

JavaScript垃圾回收

计算机的动态内存不再需要的时候就应该释放,让出内存。

垃圾回收的原理=>考虑某个变量在未来不会被用。

  • 引用计数
let user={
    name:"simple"
}
//现在user就引用了{name:"simple"}这个对象
user=null;
//现在{name:"simple"}变成不可到达的了,不能访问,JS就会回收他


//若是
let user={
    name:"simple"
}
let admin=usr;
//现在{name:"simple"}就被两个对象引用
user=null;
//即使执行这个,{name:"simple"}还可以通过admin获取,故未被回收

//循环引用问题
function func() {
    let obj1 = {};
    let obj2 = {};

    obj1.a = obj2; // obj1 引用 obj2
    obj2.a = obj1; // obj2 引用 obj1
}
// 当函数 func 执行结束后,返回值为 undefined,所以整个函数以及内部的变量都应该被回收,但根据引用计数方法,obj1 和 obj2 的引用次数都不为 0,所以他们不会被回收。

// 要解决循环引用的问题,最好是在不使用它们的时候手工将它们设为空。上面的例子可以这么做:
obj1 = null;
obj2 = null;
  • 标记-清除
/*
垃圾回收器在运行的时候会给存储在内存中的变量都加上标记(所有都加),然后去掉环境变量中的变量,以及被环境变量中的变量所引用的变量(条件性去除标记),删除所有被标记的变量,删除的变量无法在环境变量中被访问所以会被删除,最后垃圾回收器,完成了内存的清除工作,并回收他们所占用的内存
**/

eval 的作用

//eval将js中的字符串转化成为一个表达式,然后执行

前端模块化

将复杂的文件编程成为一个个独立的模块,有利于复用和维护。

  • Commonjs

服务端的模块化规范

var clock=require('clock');
clock.start()//但是这样必须要等待clock模块加载完成才能调用
  • AMD

浏览器端的异步加载模块,先定义所有依赖,然后在加载完成后的回调函数执行

require(['clock'],()=>{
    clock.start()
    //但是一开始就把所有依赖写进来不符合书写逻辑
})
  • CMD

用的时候再require

commonjs与es6 module区别

commonjs 
静态复制更改,可以修改引入变量的值
var axois=require("axios")//必须加载完成才能使用,而且未使用也要加载

module 动态引用
import axios from 'axios'
不允许更改引入变量的值
且需要用到才加载

深拷贝与浅拷贝

首先说说JS的基本数据类型与引用数据类型

  • 基本数据类型(大小固定,放在栈中,栈中存放引用数据类型的地址)
    使用Typeof可以检测基本数据类型,注意 typeof null==object
    String, Boolean,Number,Undefined,Null
  • 引用数据类型(大小不固定,放在堆中)
    使用typeof检测引用数据类型均为 object 要得到确切的 用 instanceof 或者对应类型的原型
    arr instanceof Array==true arr.proto==Array.prototype
    Object(Array,Date,Regexp,Function)

浅拷贝

浅拷贝只会复制指向某个对象的指针,而不复制对象本身,也就是复制对象的引用,新旧对象还是指向的同一块内存,基本数据类型实质是栈的传值,引用数据类型实质是栈的传址

//1.赋值
a=b
//2.Object.assign(des,src) return newDes
let obj={a:"simple"}
let newObj=Object.assign({},obj);//当obj只有一层的时候是深拷贝
//3.函数
function simpleCopy(obj)
{
    var result=Array.isArray(obj)?[]:{}
    for(let i in obj)
    {
        result[i]=obj[i]
    }
    return result
}

深拷贝

深拷贝会创造一个一模一样的新对象,新对象与旧对象不共享内存,修改新对象不会改到源对象

//1.手动赋值===太麻烦
//2.JSON转字符串处理  但是不能拷贝函数
var obj1={name:"simple"}
var obj2=JSON.parse(JSON.stringfy(obj))
//3.实现深度克隆
function deepCopy(obj)
{
    //遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝
    let result=Array.isArray(obj)?[]:{}//如果是array返回结果为array,否则为object
    for(let i in obj)
    {
        let value=obj[i]
        if(typeof value=="object")
            result[i]=deepCopy(value)
        else
            result[i]=value
    }
    return result
}

实现一个once函数,传入参数只执行一次

function once(fn)
{
    let flag=true;
    return function(){
        if(flag)
        {
            fn.call(null,arguments);
            flag=false;
        }
    }
}
function test(name)
{
    console.log(name)
}
test=once(test("simple"));
test()//simple
test()//不执行

=的区别

对于string,number等基本类型=是有区别的

不同类型之间进行比较==会将转化为同一类型之后看值是否相等,而===如果类型不同,就是不同

对于Array,Object等引用类型=没有区别,进行指针地址的比较

对于基本类型与引用类型进行比较,==将引用转为基本进行值比较

setTimeOut setInterval

//setInterval会一直不停执行
//setTimeOut只执行一次

promise

promise对象初始化为pending态
当调用resolve的时候,会由pending=>fulfilled
当调用reject的时候,会由pending=>rejected
promise.then(successValue,failedValue)

promise.all

const p1=new Promise((resolve,reject)=>{
    resolve(1);
})
const p2=new Promise((resolve,reject)=>{
    resolve(2);
})
const p3=new Promise((resolve,reject)=>{
    resolve(3);
})
Promise.all([p1,p2,p3])//接收一个promise数组参数
.then(res=>{
    console.log(res) //1 2  3
})

手写promise

const PENDING="pending"
const FULFILLED="fulfilled"
const REJECTED="rejected"
function MyPromise(executor)
{
    //构建promise对象的时候传递executor函数,立即执行
    //executor接收两个参数,resolve和reject
    //若executor执行车工则调用resolve,否则reject
    let that=this;//缓存当前promise对象
    that.status=PENDING;//初始状态
    that.value=undefined;//fulfilled返回的信息,value保存成功值
    that.reason=undefined;//reject返回的原因,reason保存失败原因
    that.onFullfilledCallbacks=[];//成功的回调函数
    that.onRejectedCallbacks=[];//失败的回调函数  
    //---均在then方法中,then方法的参数就是成功和失败的回调函数
    //若成功执行onFullfilledCallbacks,参数是value
    //失败onRejectedCallbacks,参数是reason
    function resolve(value)
    {
        if(value instanceof Promise)
        {
            //针对resolve返回promise对象的处理
            return value.then(resolve,reject);
        }
        //执行顺序  1.同步代码  2.Promise.then  3.setTimeOut(function(){},0)
        setTimeOut(()=>{
            //加settimeout是为了确保onFulfilledCallBack和
            //onRejectedCallBack异步执行,而且在promise.then之后执行
            if(that.status==PENDING)
            {
                //只能由pending=>fulfilled态
                that.status=FULFILLED;
                that.value=value;
                that.onFulfilledCallbacks.forEach(fn=>fn(that.value));
            }
        },0)
    }
    function reject(reason)
    {
        setTimeOut(()=>{
            if(that.status==PENDING)
            {
                that.status=REJECTED;
                that.reason=reason;
                that.onRejectedCallbacks.forEach(fn=>fn(that.reason));

            }
        },0)
    }
    try
    {
        executor(resolve,reject);
    }
    catch(e){
        reject(e)
    }
    that.then(onFulfilledCallbacks,onRejectedCallbacks)
    {
        //then方法链式调用
        //或者写成MyPromise.prototype.then
        if(that.status==FULFILLED)
        {
            onFulfilledCallbacks(that.value)
        }
        else if(that.status==REJECTED)
        {
            onRejectedCallbacks(that.reason)
        }
    }
}

Node的events模块

events模块对应的是观察者模式或者叫发布/订阅模式

const EventEmitter=require('events')
const myEmitter=new EventEmitter()
myEmitter.on("eventName",callback)//订阅-监听事件
myEmitter.emit("eventName",params)//发布-触发事件

Js类型判断

  • typeof =>对基本类型如String,Number,Boolean能判断, typeof null==object 对引用类型判断均为object

  • instanceof left instanceof right 能判断是否为真

function myInstanceof(left,right)
{
    let proto=left.__ptoto__;
    let prototype=right.prototype;
    while(true)
    {
        if(!proto)
        {
            return false;
        }
        if(proto==prototype)
        {
            return true;
        }
        
    }
}
  • Object.prototype.toString.call(params)=>[object Number]等结果

forEach、map、filter的区别

  • forEach没有返回值,直接修改原数组,map创建一个新数组使用,有返回值
let arr=[1,2,3,4,5,6]
let newArr=arr.forEach(item=>{
    return item*item
})//newArr为undefined
let mapArr=arr.map(item=>{
    return item*item
})
  • filter返回过滤后的数组

js数组去重

  • 双层循环判断
  • indexOf 返回数组某个元素的第一个下标 arr.indexOf(value)!=index
  • ES6 Set去重 new Set(array)
  • reduce
let arr=[1,1,2,2,3,3,4,5,6,7]
let newarr=arr.reduce((pre,cur)=>{
    if(!pre.includes(cur))
    {
        pre.push(cur)
    }
    else
    {
        return pre;
    }
},[])

js数组扁平化

var arr=[1,2,[3,4,5,[6,7,8],9],10,[11,12]];
  • 递归实现
function flat(arr)
{
    let newarr=[]
    arr.forEach(item={
        if(item instanceof Array)
        {
            //contact用于连接连个数组
            //var a = [1,2,3];
            //a.concat(4,5)=>[1,2,3,4,5]
            newarr=newarr.contact(flat(arr));
        }
        else
        {
            newarr.push(item)
        }
    })
    return newarr
}
  • reduce方法实现
const newarr=function(arr)
{
    return arr.reduce((pre,cur)=>{
        pre.concat(Array.isArray?newArr(arr):cur)
    },[])//最后一个参数为调用reduce的数组
}

暂时性死区

ES6新增了let,const命令,用来声明变量,和var作用差不多,但是旨在所在代码块有效,不存在变量提升,所以声明前调用变量都会报错,这就叫暂时性死区。

if(1)
{
    temp="abc";
    let temp;//要报错
}

什么是virtual dom

用JavaScript对象表示DOM树结构,然后区构建一个真正的DOM树,当文档状态发生改变的时候,用新树和旧的树进行比较,记录两棵树的差异,然后将差以构建到真正的DOM树上,实现视图的更新。本质就是在JS和DOM之间做一个缓存

js执行上下文

  • 全局执行上下文

默认的在浏览器中是window对象

  • 函数执行上下文

函数每次调用的时候都会创建一个上下文

var count=0
const fn=(count)=>{
    count+=1;
    console.log(count)
}
fn(count)//1
fn(count)//1   因为函数每次调用的时候都会创建新的执行上下文,退出的时候会销毁执行执行上下文


const fn=()=>{
    count+=1;
    console.log(count)//这个结果就不一样了,用的是全局的上下文

}

js函数柯里化

所谓柯里化,就是收集参数的方法,只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。

//1.收集参数=>利用闭包
//2.知道什么时候参数收集完成
function curry(fn)
{
    let args=[]//保存上一次传递进来的参数
    return function()
    {
        args=args.concat([...arguments]);
        if(args.length>=fn.length)
        {
            //参数收集完成,执行函数
            fn(...args)
        }
        return arguments.callee//参数未收集完,继续收集
    }
}

ES6的Symbol

ES5的对象署名都是字符串,容易造成署名冲突,Symbol就是为了解决这个问题产生的。Symbol是一种新的原始类型数据,返回值是变量,所以只能通过Symbol()调用

let keyName=Symbol("keyName description")
//这样一个简单的Symbol就创建好了!

事件委托与冒泡

事件委托是利用冒泡阶段的运行机制来实现的,就是把一组子元素的事件绑定到父元素上面,可以减少内存消耗,提高效率

for in for of的区别

var arr=[1,2,3,4,5,6,7,8,9]
for(index in arr)
{
    console.log(arr[index])
}
for(let item of arr)
{
    console.log(item)
}

//使用for in遍历对象
var obj={
    a:1,
    b:2
}
//当然Object.key(obj)也能得到对象key数组
for(key in obj)
{
    console.log(obj[key])
}

正则表达式

  • 匹配电话
var exp=/^1[3,5,7,8,9]\d{9}$/
  • 匹配邮箱
//[\.\w+]*可能遇到有些邮箱@前面有小数点的
var exp=/\w+[\.\w+]*@\w+[\.\w]+/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值