前端面试题之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==truethis.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 引用 obj2obj2.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,否则为objectfor(let i in obj){let value=obj[i]if(typeof value=="object")result[i]=deepCopy(value)elseresult[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,否则rejectlet 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,参数是reasonfunction 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.thenif(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]+/
前端面试题之JavaScript相关推荐
- 前端面试题集锦——JavaScript
前端面试题集锦--JavaScript 1.请你谈谈 Cookie 的优缺点 cookie是存储于访问者计算机中的变量 cookie是浏览器提供的一种机制 可以由JavaScript对其进行控制(设置 ...
- 手撕前端面试题【javascript~ 总成绩排名、子字符串频次统计、继承、判断斐波那契数组等】
前端JavaScript面试题
- 前端面试题之JavaScript【this指向】
JavaScript this指向 全局环境下:this 始终指向全局对象(window), 无论是否严格模式 console.log(this.document === document); // ...
- 手撕前端面试题(Javascript~事件委托、数组去重、合法的URL、快速排序、js中哪些操作会造成内存泄漏......
前端的那些基本标签
- 手撕前端面试题【javascript~ 列表动态渲染、无重复数组、数组排序、新数组、创建数组、深浅拷贝、内存泄露等】
前端的那些基本标签
- 手撕前端面试题【javascript~模板字符串、类继承、参数解析器、生成页码等】
前端的那些基本标签
- 「前端面试题系列7」Javascript 中的事件机制(从原生到框架)
前言 这是前端面试题系列的第 7 篇,你可能错过了前面的篇章,可以在这里找到: 理解函数的柯里化 ES6 中箭头函数的用法 this 的原理以及用法 伪类与伪元素的区别及实战 如何实现一个圣杯布局? ...
- JavaScript中的load事件的作用_史上最全的web前端面试题汇总及答案JavaScript之二(二)...
作者:樱桃小丸子儿 链接:https://www.jianshu.com/p/abadcc84e2a4 JavaScript JS的基本数据类型 number,string,boolean,objec ...
- javascript array添加图片_史上最全的web前端面试题汇总及答案JavaScript之二(二)...
作者:樱桃小丸子儿 链接:https://www.jianshu.com/p/abadcc84e2a4 JavaScript JS的基本数据类型 number,string,boolean,objec ...
最新文章
- php mysql execute语法_PHP PDOStatement::execute讲解
- 2020年10月GitHub上最热门的开源项目
- 基本入门程序编写格式和注意事项
- 管理系统页面脚手架(一)
- 利用Python进行数据分析--时间序列
- Windows下配置QGIS和Python
- Wix 3.0正式发布
- java获取键盘输入
- svg转换pdf用php实现,如何使用javascript在JSPDF中将SVG文件转换为PDF
- reuntion 题解
- 红外光电开关的原理与实验
- 前端框架中的大熊猫Ember
- 计算机专业关于Java读书笔记_《Java8学习笔记》读书笔记(四)
- UE5/C++ 基于GAS创建攻击伤害 5.1.1准备碰撞体
- MarkdownPad2 使用教程
- 图片格式网页在线一键转换源码
- 淘宝宝贝详情页的优化技巧
- CTYZ的树论赛(P5557 旅行/P5558 心上秋/P5559 失昼城的守星使)
- javascript中使用枚举定义一个对象进行数据转换
- R和pandas实现透视表(pivot; cast/dcast/acast)和逆透视表(melt)过程
热门文章
- Java在Quant应用_GitHub - tigerfintech/tiger_quant: Java 实盘量化框架
- web前端之——图片上传
- MST54XXB 45V,350mA,2.5uA,高PSRR,具有使能功能的低压线性稳压器
- 2022个人邮箱注册哪个好?163企业邮箱怎么申请注册个人电子邮箱
- SecureFx设置密钥登陆
- 【ReID】局部特征
- 从零开始,用5年时间,攒够100w,如何够到800w 上海房
- 逐步实现一个简易的飞机大战(c++)
- sync.Once化作一道光让我顿悟
- [linux] bash str字符串转换为int