JavaScript面试知识点

JavaScript规定了几种语言类型
基本类型(null,undefined,string,number,Boolean,symbol(符号,标记))
引用类型(object,Array,Function)

javaScript中的变量在内存中的具体存储形式(理解值类型和引用类型)
变量主要分为两种类型,基本类型和引用类型
基本类型在内存中占据的空间大小是一定的,所以保存在栈内存中
从一个变量向另一个变量复制基本来下的值 会创建这个值得一个副本。
引用类型保存在堆内存中
包含引用类型值得变量实际上包含的并不是对象本身 而是一个指向该对象的指针。
从一个变量向另一个变量复制引用类型的值 复制的其实是指针 因此两个变量最终都指向同一个对象

javascript为什么使用单线程
防止dom渲染冲突问题
描述:javascript主要的任务是处理用户的交互,而用户的交互无非就是响应DOM的增删改,
如果是多线程的话,用户一边对dom进行删除,一边对dom进行修改,渲染就会有冲突
可以使用web worker实现多线程

什么是任务队列
任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。
主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。

同步和异步任务
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入"任务队列"的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行

js运行机制

  • js运行会分为同步任务和异步任务
  • 所有同步任务都在主线程上执行,形成一个执行栈。
  • 主线程之外,还存在一个"任务队列"。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
  • 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
    主线程不断重复上面的第三步。
    主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

js引擎在一次事件循环中,会先执行js线程的主任务,然后会去查找是否有微任务microtask(promise),如果有那就优先执行微任务,如果没有,在去查找宏任务macrotask(setTimeout、setInterval)进行执行。

宏任务 or 微任务
这里需要注意的是new Promise是会进入到主线程中立刻执行,而promise.then则属于微任务
宏任务(macro-task):整体代码script、setTimeOut、setInterval
微任务(mincro-task):promise.then、promise.nextTick(node)

null和undefined的区别
null字面表示的是空值,如何要释放一个空对象的话,将变量设置为null,表示对象已经被清空,垃圾回收机制可以被收回
underfined字面表示的是没有被定义的,主要有以下几种情况
(1)声明了变量,但没有给变量赋值
(2)访问对象中不存在的属性
(3)定义的形参,但没有传实参
(4)使用viod对表达式求值

window.onload和DOMContentLoded区别
DOM完整的解析过程
1、解析HTML结构。
2、加载外部脚本和样式表文件。
3、解析并执行脚本代码。//js之类的
4、DOM树构建完成。//DOMContentLoaded事件会被触发
5、加载图片等外部文件。
6、页面加载完毕。//load事件会被触发

1、当 onload事件触发时,页面上所有的DOM,样式表,脚本,图片,flash都已经加载完成了。
2、当 DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash。

至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型
typeof(一般判断基本类型)(null返回object,function返回function,其他的基本类型,返回正常的结果,其他的引用类型,返回的都是object)
instanceeOf(后面一定是对象类型,检测的是原型)(instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。)
image.png
constructor(类继承中会发生错误)
Object.prototype.toString.call() 适合所有的类型

熟练应用map、reduce、filter等高阶函数解决问题
arr.forEach(callback) 遍历数组,无return 即使有return,也不会返回任何值,并且会影响原来的数组
arr.map(callback) 映射数组(遍历数组),有return 返回一个新数组 。
arr.filter(callback) 过滤数组,返回一个满足要求的数组
arr.every(callback) 依据判断条件,数组的元素是否全满足,若满足则返回ture
arr.some(callback) 依据判断条件,数组的元素是否有一个满足,若有一个满足则返回ture
arr.reduce(callback, initialValue) 迭代数组的所有项,累加器,数组中的每个值(从左到右)合并,最终计算为一个值
callback的参数: value --当前索引的值
        index --索引
        array --原数组(只有forEach和map)
arr.indexOf() 查找某个元素的索引值,若有重复的,则返回第一个查到的索引值若不存在,则返回 -1
arr.lastIndexOf() 和arr.indexOf()的功能一样,不同的是从后往前查找
Array.from() 将伪数组变成数组,就是只要有length的就可以转成数组。
Array.of() 将一组值转换成数组,类似于声明数组
arr.find(callback) 找到第一个符合条件的数组成员
arr.findIndex(callback) 找到第一个符合条件的数组成员的索引值
arr.includes() 判断数中是否包含给定的值
arr.keys() 遍历数组的键
arr.values() 遍历数组键值

理解原型设计模式以及JavaScript中的原型规则
无论什么时候,只要创建了一个新函数,就会根据特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象(构造函数原型)。
当你对构造函数new出一个实例时候,这个实例会有一个属性__proto__,它指向的是构造函数的prototype,就是指向所谓的自己原型对象
在默认情况下,所有原型对象都会自动获得一个 constructor (构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针
prototype
People--------------------> People.prototype(构造函数的原型)

xiaoming --------- proto(原型对象)
xiaoming.constructor === People.prototype.constructor
People.prototype.constructor === People
__proto__是神器,有原型链查找功能,当xiaoli身上没有某个属性的时候,系统会沿着__proto__去寻找它的原型对象上有没有这个属性

instanceof的底层实现原理,手动实现一个instanceof
instanceof主要用于判断某个实例是否属于某个类型,也可用于判断某个实例是否是其父类型或者祖先类型的实例。
instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false。

function food(name,color,size) {
        this.name = name;
        this.color = color;
        this.size = size;
     }
var apple = new food('redApple', 'RED', 'small');
console.log(apple instanceof food);
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式
   var O = R.prototype;
   L = L.__proto__;
   while (true) { 
     if (L === null) 
        return false; 
     if (O === L)  // 这里重点:当 O 严格等于 L 时,返回 true 
        return true; 
     L = L.__proto__; 
   } 
}
//此方法用来判断继承
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式
     var O = R;
    L = L.__proto__;
    while (true) { 
      if (L === null) 
        return false; 
      if (O === L.constructor) // 这里重点:当 O 严格等于 L 时,返回 true 
        return true; 
      L = L.__proto__; 
   } 
}

#####实现继承的几种方式以及他们的优缺点
1.原型链继承(核心: 将父类的实例作为子类的原型)

function Cat(){ 
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

缺点:构造函数原型上的属性在所有该构造函数构造的实例上是共享的,即属性没有私有化,原型上属性的改变会作用到所有的实例上。
2.构造继承(核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型))

function Cat(name){
  	Animal.call(this);
  	this.name = name || 'Tom';
}

优缺点:实现了属性的私有化,但是子类无法访问父类原型上的属性。
3.组合继承(核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用)

function Cat(name){
  	Animal.call(this);
  	this.name = name || 'Tom';
}
Cat.prototype = new Animal();

#####描述new一个对象的详细过程,手动实现一个new操作符
(new过程中会新建对象,此对象会继承构造器的原型与原型上的属性,最后它会被作为实例返回这样一个过程。)
• 创建一个空对象,将它的引用赋给 this,继承函数的原型。
• 通过 this 将属性和方法添加至这个对象
• 最后返回 this 指向的新对象,也就是实例(如果没有手动返回其他的对象)

// 构造器函数
let Parent = function (name, age) {
    this.name = name;
    this.age = age;
};
Parent.prototype.sayName = function () {
    console.log(this.name);
};
//自己定义的new方法
let newMethod = function (Parent, ...rest) {
    // 1.以构造器的prototype属性为原型,创建新对象;
    let child = Object.create(Parent.prototype);
    // 2.将this和调用参数传给构造器执行
    Parent.apply(child, rest);
    // 3.返回第一步的对象
    return child;
};
//创建实例,将构造函数Parent与形参作为参数传入
const child = newMethod(Parent, 'echo', 26);
child.sayName() //'echo';

词法作用域和动态作用域
词法作用域是在写代码或者说定义时确定的,而动态作用域是在运行时确定的。
词法作用域关注函数在何处声明,而动态作用域关注函数从何处调用。

JavaScript的作用域和作用域链
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性
作用域链是javascript内部中一种变量、函数查找机制

this的原理以及几种不同使用场景的取值
① 通过函数名()直接调用:this指向window
② 通过对象.函数名()调用的:this指向这个对象
③ 函数作为数组的一个元素,通过数组下标调用的:this指向这个数组
④ 函数作为window内置函数的回调函数调用:this指向window( setInterval setTimeout 等)
⑤ 函数作为构造函数,用new关键字调用时:this指向新new出的对象

闭包的实现原理和作用,可以列举几个开发中闭包的实际应
闭包:函数作为返回值;函数作参数(所有的自由变量查找,是在函数定义的地方,不是在执行的地方)

闭包的概念
①函数嵌套函数
②函数内部可以引用函数外部的参数和变量
③参数和变量不会被垃圾回收机制回收
闭包的作用:访问函数内部的变量,保持函数在环境中一直存在,不会被垃圾回收机制收回
(因为函数内部声明 的变量是局部的,只能在函数内部访问到,但是函数外部的变量是对函数内部可见的,这就是作用域链的特点了。)
闭包的优点:
(1)方便调用上下文中声明的局部变量
(2)逻辑紧密,可以在一个函数中再创建个函数,避免了传参的问题
(3)加强了封装性
(4)变量始终保持在内存中,不会被清除
闭包的缺点
因为使用闭包,可以使函数在执行完后不被销毁,保留在内存中,如果大量使用闭包就会造成内存泄露,内存消耗很大

堆栈溢出和内存泄漏的原理,如何防止
1、内存泄露:是指申请的内存执行完后没有及时的清理或者销毁,占用空闲内存,内存泄露过多的话,就会导致后面的进程申请不到内存。因此内存泄露会导致内部内存溢出
2、堆栈溢出:是指内存空间已经被申请完,没有足够的内存提供了
js一半常用的垃圾回收方法是标记清除
标记清除法:在一个变量进入执行环境后就给它添加一个标记:进入环境,进入环境的变量不会被释放,因为只要执行流进入响应的环境,就可能用到他们。当变量离开环境后,则将其标记为“离开环境”
3、常见的内存泄露的原因
全局变量引起的内存泄露
闭包
没有被清除的计时器
4、解决方法
减少不必要的全局变量
减少闭包的使用(因为闭包会导致内存泄露)
避免死循环的发生

设计模式
1 、单例模式( 调用static 可以使用this吗)
要求一个类只有一个实例化对象存在
这个实例化对象必须提供一个全局对外访问方式
这个实例化对象应当是私有的,不能被外界直接访问或者更改
每个实例只允许被实例化一次
(1)类中定义一个私有化属性 。 static private interface = 类名
(2)定义一个静态方法 static getinterface(){ if(!类名.interface){new 类名} return 类名 + 属性(interface)
(3) constructor 是一个私有化
使用场景

  • 引用第三方库(多次引用只会使用一个库引用,如 jQuery)
  • 弹窗(登录框,信息提升框)
  • 购物车 (一个用户只有一个购物车)
  • 全局态管理 store (Vuex / Redux)

2、工厂模式
工厂模式是创建对象的常用设计模式,为了不暴露创建对象的具体逻辑,将逻辑封装在一个函数中,这个函数就称为一个工厂。本质上是一个负责生产对象实例的工厂

let  factory = function (role) {
function User(obj) {
    this.name = obj.name;
    this.role = obj.role;
}
switch(role) {
    case 'superman':
    return new User({ name: '平台用户', role: ['主页', '登录页'] })
    break;
    case 'man':
    return new User({ name: '游客', role: ['登录页']})
    break;
    default:
    throw new Error('参数错误')
}
}
let superman = factory('superman');
let man = factory('man');

3、策略模式:
策略模式的本意将算法的使用与算法的实现分离开来,避免多重判断调用哪些算法
适用场景:

  • 多重条件语句判断,执行对应的算法场景
  • 表单校验(validator)

4、代理模式:
代理模式是为其他对象提供一种代理,也就是当其他对象直接访问该对象时,如果开销较大,就可以通过这个代理层控制对该对象的访问。常见的使用场景为懒加载,合并http请求和缓存。

(function(){
    // 目标对象,是真正被代理的对象
    function Subject(){}
    Subject.prototype.request = function(){};

    function Proxy(realSubject){
        this.realSubject = realSubject;
    }
    Proxy.prototype.request = function(){
        this.realSubject.request();
    };
}());

5、观察者模式
一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知

js兼容性问题
1、event对象
IE是把event事件对象作为全局对象window的一个属性;可以使用event或window.event来访问;
FireFox和Chrome等主流浏览器是通过把【事件对象】作为【事件响应函数】的【参数】进入传入的;

兼容性的写法示例:
domElement.onclick = function( e ){
      e = e || window.event;//或(||)书写顺序有讲究,不能随意换
}

2、事件源(获取事件作用的元素)
非IE event.target IE event.srcElement
3、添加事件监听器的函数
标准(IE8以上):obj.addEventListener(事件名称,事件函数,是否捕获);
1.有捕获
2.事件名称没有on
3.this触发该事件的对象
IE:obj.attachEvent(事件名称,事件函数);
1.没有捕获
2.事件名称有on
3.this指向window
4、取消事件的默认行为

function stopDefault(e) {
  if ( e && e.preventDefault ) {
    	e.preventDefault();
  } else {
   	 window.event.returnValue = false;  // IE中阻止函数器默认动作的方式
  }
  	return false;
}

5、阻止事件冒泡
FF、Chrome:event.stopPropagation();
IE:event.cancelBubble
6、ajax请求
IE: new ActiveXObject()
FF、Chrome:new XMLHttpRequest()

浏览器的组成
用户界面 浏览器引擎 渲染引擎(内核) 网络 UI 后端 JS引擎 数据存储

浏览器内核
1、IE浏览器内核:Trident内核,也是俗称的IE内核;
2、Chrome浏览器内核:以前是Webkit内核,现在是Blink内核;
3、Firefox浏览器内核:Gecko内核,俗称Firefox内核;
4、Safari浏览器内核:Webkit内核;
5、Opera浏览器内核:最初是自己的Presto内核,后来是Webkit,现在是Blink内核;
6、360浏览器、猎豹浏览器内核:IE+Chrome双内核;
7、搜狗、遨游、QQ浏览器内核:Trident(兼容模式)+Webkit(高速模式);
8、百度浏览器、世界之窗内核:IE内核;
9、2345浏览器内核:以前是IE内核,现在也是IE+Chrome双内核;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值