文章目录
最近在准备春招的面试,JS的基础问题是每一家公司必问的方面,在准备JS的面试题的时候总结了下面几个经常出现的高频问题,希望可以帮助到大家
1. 闭包
**闭包函数:**声明在一个函数中的函数,叫做闭包函数。
**闭包:**内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后
闭包找到的是同一地址中父级函数中对应变量最终的值
缺点:容易造成内存泄漏,不会被垃圾回收机制回收
function funA(){
var a = 10; // funA的活动对象之中;
return function(){ //匿名函数的活动对象;
console.log(a);
}
}
var b = funA();
b(); //10
2. JavaScript有几种类型的值?
栈:原始数据类型(Undefined,Null,Boolean,Number、String、Symbol)
堆:引用数据类型(对象、数组和函数)
两种类型的区别是:存储位置不同;
1,原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
2,引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定,如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体
在参数传递方式上,原始类型是按值传递,引用类型是按共享传递
3.new操作符具体干了什么呢?
1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、最后隐式的返回 this 。
用new和不用new调用构造函数,有什么区别
一、没有return
function Person(name) {
this.name = name;
console.log(this.name);
}
var a = Person('Tom'); // Tom
var b = new Person('LI') // LI
console.log(a) // undefined
console.log(b) // Person {name: "LI"}
1.不使用new:this对象指向window,并且,不会默认返回任何对象
2.使用new: this对象指向的是Person 实例
二、有return
1.简单数据类型:数值,字符串、布尔值、null、undefined
function Person(name) {
this.name = name;
console.log(this.name);
return 1
}
var a = Person('Tom') // Tom
var b = new Person('LI') // LI
console.log(a) // 1
console.log(b) // Person {name: "LI"}
console.log(a.name) // undefined
console.log(b.name) // LI
2.复杂数据类型:对象即属性的集合(function、Array、Object)
function Person(name) {
this.name = name;
console.log(this.name);
return [1,2,3]
}
var a = Person('Tom') // Tom
var b = new Person('LI') // LI
console.log(a) // [1,2,3]
console.log(b) // [1,2,3]
console.log(a.name) // undefined
console.log(b.name) // undefined
(1)不使用new:return之后,得到的就是return的返回值
(2)使用new:return之后,就会出现两种情况,一种是返回的是简单数据类型,这时返回值会被自动忽略,返回的还是原来的实例。另一种是返回的是复杂数据类型,此时得到的就是return的返回对象。
4.箭头函数和普通函数的区别
- 箭头函数是匿名函数,不能作为构造函数,不能使用new
- 箭头函数不绑定arguments,取而代之用rest参数…解决
- this的作用域不同,箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
- 箭头函数没有原型属性
5.bind,call和apply
bind会创建一个新的函数,这个函数的this始终指向bind()方法传入的第一个参数
call和apply
共同点:
都可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象。
另一种说法,都能够改变方法的执行上下文(执行环境),将一个对象的方法交给另一个对象来执行,并且是立即执行。
不同点:
apply();//最多只能有两个参数–新this对象和一个数组argArray,如果给该方法传递多个参数,则把参数都写进这个数组里边,当然,即使只有一个参数,也要写进数组里边。
call();//可以接收多个参数,第一个参数apply()一样,后面则是一串参数列表。
6.设计模式
// 单例模式
class Singleton {
constructor() {}
}
Singleton.getInstance = (function() {
let instance
return function() {
if (!instance) {
instance = new Singleton()
}
return instance
}
})()
let s1 = Singleton.getInstance()
let s2 = Singleton.getInstance()
console.log(s1 === s2) // true
// 工厂模式
class Man {
constructor(name) {
this.name = name
}
alertName() {
alert(this.name)
}
}
class Factory {
static create(name) {
return new Man(name)
}
}
Factory.create('yck').alertName()
// 适配器模式
class Plug {
getName() {
return '港版插头'
}
}
class Target {
constructor() {
this.plug = new Plug()
}
getName() {
return this.plug.getName() + ' 适配器转二脚插头'
}
}
let target = new Target()
target.getName() // 港版插头 适配器转二脚插头
7.ES6 module和commonJS的区别
ES6模块
- 不共享全局命名空间
- 模块顶级this的值是undefined
- 模块中的var声明不会添加到window对象
- 异步加载和执行
- 采用export导出,import导入
commonJS模块
- 模块加载是模块系统执行的同步操作
- 使用require()指定依赖
- 导出挂载到module.exports对象上
8.Set和Map
- Map是一组键值对的结构,具有极快的查找速度
- Set和Map类似,也是一组Key的集合,但不存储Value。在Set中不存在重复的Key
9.节流和防抖
节流和防抖分别是什么?
- 函数节流:规定在一个单位时间内,只能触发一次函数,如果这个单位时间内触发多次函数,只有一次生效; 典型的案例就是鼠标不断点击触发,规定在n秒内多次点击只有一次生效
- 函数防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时;典型的案例就是输入搜索:输入结束后n秒才进行搜索请求,n秒内又输入的内容,就重新计时。
function throttle (fn, delay) {
if(typeof fn !== 'function'){
throw new TypeError('fn is not a function')
}
//记录上一次函数触发的时间
let lastTime = Date.now()
return function(...args){
let now = Date.now()
if(now - lastTime >= delay){
fn.call(this, ...args)
lastTime = Date.now()
}
}
}
function debounce(fn, delay) {
if(typeof fn !== 'function'){
throw new TypeError('fn is not a function')
}
let timer = null
return function(...args){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.call(this, ...args)
}, delay)
}
}
10.手写Promise.all和Promise.race
function myPromiseAll(promiseArr){
const resultArr = []
return new Promise((resolve, reject) => {
promiseArr.forEach((promiseItem) => {
Promise.resolve(promiseItem).then((res) => {
resultArr.push(res)
}, (rej) => {
reject(rej)
})
})
if(restArr.length === promiseArr.length){
resolve(resultArr)
}
})
}
function myPromiseRace(promiseArr){
return new Promise((resove, reject) => {
promiseArr.forEach((item) => {
Promise.solve(item).then((res) => {
resolve(res)
})
.catch((err) => {
reject(err)
})
})
}
11.获取url中的参数并转化为对象
const url = "http:www.xxx.net/x/x.html?id=898602B8261890349226&aa=123&bb=456"
const params = url.split("?")[1].split("&")
const obj = {}
params.map(v => obj[v.split("=")[0]] = v.split("=")[1])
console.log(obj) //{id: "898602B8261890349226", aa: "123", bb: "456"}
12.手写发布订阅模式
// 发布订阅模式
class EventEmitter {
constructor() {
// 事件对象,存放订阅的名字和事件
this.events = {};
}
// 订阅事件的方法
on(eventName,callback) {
if (!this.events[eventName]) {
// 注意时数据,一个名字可以订阅多个事件函数
this.events[eventName] = [callback]
} else {
// 存在则push到指定数组的尾部保存
this.events[eventName].push(callback)
}
}
// 触发事件的方法
emit(eventName) {
// 遍历执行所有订阅的事件
this.events[eventName].forEach(cb => cb());
}
// 移除订阅事件
removeListener(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(cb => cb != callback)
}
}
// 只执行一次订阅的事件,然后移除
once(eventName,callback) {
// 绑定的时fn, 执行的时候会触发fn函数
let fn = () => {
callback(); // fn函数中调用原有的callback
this.removeListener(eventName,fn); // 删除fn, 再次执行的时候之后执行一次
}
this.on(eventName,fn)
}
}
// 观察者模式
class Subject {
observers = []
addObserver(observer) {
this.observers.push(observer)
}
removeObserver(observer) {
let index = this.observers.indexOf(observer)
if(index > -1){
this.observers.splice(index, 1)
}
}
notify() {
this.observers.forEach(observer => {
observer.update()
})
}
}
class Observer{
update() {}
subscribeTo(subject) {
subject.addObserver(this)
}
}
13.面向对象三大概念及特点
- 封装
- 继承
- 多态
- 一种面向对象语言需要向开发者提供四种基本能力:
- 封装 - 把相关的信息(无论数据或方法)存储在对象中的能力
- 聚集 - 把一个对象存储在另一个对象内的能力
- 继承 - 由另一个类(或多个类)得来类的属性和方法的能力
- 多态 - 编写能以多种方法运行的函数或方法的能力
14.判断是否是数组
一共四种方法
let arr = []
1. instanceof
arr instanceof Array
2. __proto__
arr.__proto__ === Array.prototype
3. constructor
arr.constructor === Array
4. Object.prototype.toString
Object.prototype.toString.call(arr) === '[object Array]'
5. Array.isArray
Array.isArray(arr)