js基础面试题

2021-01-06  本文已影响0人  Grit_1024

(掌握)什么是“use strict”,好处和坏处

use ‘strict’: "严格模式"是一种在JavaScript代码运行时自动实行更严格解析和错误处理的方法。

优点:

  1. 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;

  2. 消除代码运行的一些不安全之处,保证代码运行的安全;

  3. 提高编译器效率,增加运行速度;

  4. 为未来新版本的Javascript做好铺垫。

缺点:

现在网站的 JS 都会进行压缩,一些文件用了严格模式,而另一些没有。这时这些本来是严格模式的文件,被 merge 后,这个字符串("use strict")就到了文件的中间,不仅没有指示严格模式,反而在压缩后浪费了字节。

(掌握)console.log(0.1 + 0.2)

0.1+0.2的结果不是0.3,而是0.3000000000000000004,JS中两个数字相加时是以二进制形式进行的,当十进制小数的二进制表示的有限数字超过52位时,在JS里是不能精确储存的,这个时候就存在舍入误差。

(掌握)数组pop(), push(), unshift(), shift()的区别

(掌握)==和===

  1. ==:只是比较值
  2. ===:既要比较数据类型还要比较值

掌握)事件冒泡和事件捕获到底有何区别?

  1. 事件冒泡:从下至上。当给父子元素的同一事件绑定方法的时候,触发子元素身上的事件,执行完毕之后,也会触发父级元素相同的事件。

注意: addEventListener中有三个属性,第三个属性是布尔值。false为事件冒泡,true为事件捕获

  1. 事件捕获:从上至下到指定元素。当触发子元素身上的事件时,先触发父元素,然后在传递给子元素

(掌握)JS数据类型

在ES5的时候,我们认知的数据类型确实是 6种:Number、String、Boolean、undefined、object、Null。

ES6 中新增了一种 Symbol 。这种类型的对象永不相等,即始创建的时候传入相同的值,可以解决属性名冲突的问题,做为标记。

(掌握)什么是typescript

  • 1.它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。
  • 2.TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以不加改变的在TypeScript下工作。TypeScript是为大型应用之开发而设计,而编译时它产生 JavaScript 以确保兼容性。

(掌握)什么是模块化编程?

每个模块内部,module变量代表当前模块。

这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。

(掌握)简述javascript原型、原型链?有什么特点

原型:每一个构造函数都有一个prototype属性指向一个对象,这个对象就是构造函数实例的原型

原型链:每一个实例都有一个proto属性执行原型对象,来获取原型对象上的属性和方法,原型对象也有一个__proto

属性指向另外一个原型对象,以此类推,直到原型链的最终端null为止,这个串成链的过程就是原型链

特点:实现继承 一个对象可以拿到另一个对象上的属性和方法

构造函数都有一个prototype属性指向原型对象

原型对象都有一个consttuctor属性指向构造函数

构造函数new实例化实例对象

实例对象上有__proto属性指向原型

(掌握)解释javascript中的作用域和变量声明提升

作用域是指程序源代码中定义变量的区域。作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。

变量声明提升:

foo;  // undefined
var foo = function () {
    console.log('foo1');
}

foo();  // foo1,foo赋值

可以想象成:所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端。

(掌握)谈谈this对象的理解,call()和apply()的区别

call和apply的区别在于传入参数的不同; 第一个参数都是,指定函数体内this的指向;

第二个参数开始不同,apply是传入带下标的集合,数组或者类数组,apply把它传给函数作为参数,call从第二个开始传入的参数是不固定的,都会传给函数作为参数。

call比apply的性能要好,平常可以多用call。call传入参数的格式正是内部所需要的格式。

(掌握)js 的typeof返回有哪些数据类型?

string,number,Boolean,undefined,object,function, symbol(es6)

(掌握)什么是闭包?为什么要用它?

1)什么是闭包

函数执行后返回结果是一个内部函数,并被外部变量所引用,如果内部函数持有被执行函数作用域的变量,即形成了闭包。

可以在内部函数访问到外部函数作用域。使用闭包,一可以读取函数中的变量,二可以将函数中的变量存储在内存中,保护变量不被污染。而正因闭包会把函数中的变量值存储在内存中,会对内存有消耗,所以不能滥用闭包,否则会影响网页性能,造成内存泄漏。当不需要使用闭包时,要及时释放内存,可将内层函数对象的变量赋值为null。

**2)闭包原理 **

函数执行分成两个阶段(预编译阶段和执行阶段)。

利用了函数作用域链的特性,一个函数内部定义的函数会将包含外部函数的活动对象添加到它的作用域链中,函数执行完毕,其执行作用域链销毁,但因内部函数的作用域链仍然在引用这个活动对象,所以其活动对象不会被销毁,直到内部函数被烧毁后才被销毁。

3)优点

  1. 可以从内部函数访问外部函数的作用域中的变量,且访问到的变量长期驻扎在内存中,可供之后使用
  2. 避免变量污染全局
  3. 把变量存到独立的作用域,作为私有成员存在

**4)缺点 **

  1. 对内存消耗有负面影响。因内部函数保存了对外部变量的引用,导致无法被垃圾回收,增大内存使用量,所以使用不当会导致内存泄漏
  2. 对处理速度具有负面影响。闭包的层级决定了引用的外部变量在查找时经过的作用域链长度
  3. 可能获取到意外的值(captured value)

4)应用场景

应用场景一: 典型应用是模块封装,在各模块规范(ES6)出现之前,都是用这样的方式防止变量污染全局。

var Yideng = (function () {
    // 这样声明为模块私有变量,外界无法直接访问
    var foo = 0;

    function Yideng() {}
    Yideng.prototype.bar = function bar() {
        return foo;
    };
    return Yideng;
}());

应用场景二: 在循环中创建闭包,防止取到意外的值。

如下代码,无论哪个元素触发事件,都会弹出 3。因为函数执行后引用的 i 是同一个,而 i 在循环结束后就是 3

for (var i = 0; i < 3; i++) {
    document.getElementById('id' + i).onfocus = function() {
      alert(i);
    };
}
//可用闭包解决
function makeCallback(num) {
  return function() {
    alert(num);
  };
}
for (var i = 0; i < 3; i++) {
    document.getElementById('id' + i).onfocus = makeCallback(i);
}

(掌握)简述js继承的方式

  1. 混入式继承:把父类的所有方法都拷贝到子类上
  2. 原型式继承:只继承父类原型上的属性和方法
  3. 原型链继承:继承父类构造函数里边的属性和方法,也继承父类原型上的属性和方法 缺点--不能向父类传参数
  4. 借用构造函数继承:可以父类传递参数 缺点--继承不了父类原型对象的方法
  5. 组合继承:借用构造函数继承+原型链继承

(掌握)给String添加一个trim()方法,去除开头和结尾的空格符号

String.prototype.trim = function (str) {
  return str.replace(/(^\s*)|(\s*$)/g, '');
}

(掌握)深拷贝和浅拷贝的区别

浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,

深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存.

浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。

深复制:在计算机中开辟一块新的内存地址用于存放复制的对象。

(掌握)如何实现深拷贝

常用:使用JSON.parse(JSON.stringify(obj))

原理是把一个对象序列化成为一个JSON字符串,将对象的内容转换成字符串的形式再保存在磁盘上,再用JSON.parse()反序列化将JSON字符串变成一个新的对象

缺点是: 会忽略undefined、symbol、funciton

实现:递归+判断类型

一个简单的代码

// 数字 字符串 function是不需要拷贝的
function deepClone(value) {  
    if (value == null) return value;  
    if (typeof value !== 'object') return value;
    if (value instanceof RegExp) return new RegExp(value);  
    if (value instanceof Date) return new Date(value);  
    // 我要判断 value 是对象还是数组 如果是对象 就产生对象 是数组就产生数组  
    let obj = new value.constructor;  
    for(let key in value){    
        obj[key] = deepClone(value[key]); // 看一看当前的值是不是一个对象  
    }  
    return obj;
}

(掌握)javascript 的垃圾回收机制讲一下

定义:指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。

JavaScript 在创建对象(对象、字符串等)时会为它们分配内存,不再使用对时会“自动”释放内存,这个过程称为垃圾收集。

内存生命周期中的每一个阶段:

分配内存 —  内存是由操作系统分配的,它允许您的程序使用它。在低级语言(例如 C 语言)中,这是一个开发人员需要自己处理的显式执行的操作。然而,在高级语言中,系统会自动为你分配内在。 使用内存 — 这是程序实际使用之前分配的内存,在代码中使用分配的变量时,就会发生读和写操作。 释放内存 — 释放所有不再使用的内存,使之成为自由内存,并可以被重利用。与分配内存操作一样,这一操作在低级语言中也是需要显式地执行。

四种常见的内存泄漏:全局变量,未清除的定时器,闭包,以及 dom 的引用

  1. 全局变量 不用 var 声明的变量,相当于挂载到 window 对象上。如:b=1; 解决:使用严格模式
  2. 被遗忘的定时器和回调函数
  3. 闭包
  4. 没有清理的 DOM 元素引用

介绍下 promise 的特性、优缺点

(掌握)请介绍一下XMLhttprequest对象

Ajax的核心是JavaScript对象XmlHttpRequest。该对象在Internet Explorer 5中首次引入,它是一种支持异步请求的技术。简而言之,XmlHttpRequest使您可以使用JavaScript向服务器提出请求并处理响应,而不阻塞用户。通过XMLHttpRequest对象,Web开发人员可以在页面加载以后进行页面的局部更新。

(掌握)请描述一下 cookiessessionStoragelocalStorage 的区别?

  1. cookie是浏览器自动携带在请求里发送给服务端去验证的

sessionStorage和localStorage不会自动携带

  1. cookie体积相对sessionStorage localStorage小
  2. 后端可以设置cookie之后,前端修改不了的;
  3. cookie可以设置过期时间
  4. cookie是用来做状态保持的,因为http请求时无状态的
  5. cookie是用户第一次访问服务器服务器颁发给浏览器的,第二次请求就会携带
  6. sessionStorage 存储在内存中 关闭浏览器数据会消失
  7. localStorage 关闭浏览器数据不会消失 需要手动去清除

(掌握)浏览器缓存策略

1)浏览器缓存策略

浏览器每次发起请求时,先在本地缓存中查找结果以及缓存标识,根据缓存标识来判断是否使用本地缓存。如果缓存有效,则使 用本地缓存;否则,则向服务器发起请求并携带缓存标识。根据是否需向服务器发起HTTP请求,将缓存过程划分为两个部分: 强制缓存和协商缓存,强缓优先于协商缓存。

HTTP缓存都是从第二次请求开始的:

[ img

(opens new window)](https://camo.githubusercontent.com/df822872ee2a8aef44c665f8fffd13c4cc4eb637bd8706ce4899e8eb72d2a431/687474703a2f2f696d672d7374617469632e796964656e6778756574616e672e636f6d2f77786170702f69737375652d696d672f7169642d382e706e67)

2)强缓存

3)强缓存-expires

4)强缓存-cache-control

5)协商缓存

6)协商缓存-协商缓存-Last-Modified/If-Modified-since

7)协商缓存-Etag/If-None-match

(掌握)简述同源策略与跨域

同源策略是一种约定,它是浏览器最核心的也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能会受到影响。

当协议,主机,和端口号有一个不同时,就是跨域。

(掌握)跨域解决方案

1、(后端)服务器配置CORS(跨域资源共享)

2) (后端)node.js或nginx,反向代理,把跨域改造成同域

3)(前端)将JSON升级成JSONP,在JSON的基础上,利用<script>标签可以跨域的特性,加上头设置

(掌握)从浏览器地址栏输入URL到显示页面的步骤

1\. 浏览器根据请求的URL交给DNS域名解析,找到真实IP,向服务器发起请求;
2\. 服务器交给后台处理完成后返回数据,浏览器接收文件(html,js,css,图像等);
3\. 浏览器对加载到的资源(html,js,css等)进行语法解析,建立相应的内部数据结构(如HTML的DOM);
4\. 载入解析到的资源文件,渲染页面,完成。

(掌握)JavaScript 中的作用域(scope)是指什么?

在 JavaScript 中,每个函数都有自己的作用域。作用域基本上是变量以及如何通过名称访问这些变量的规则的集合。只有函数中的代码才能访问函数作用域内的变量。

同一个作用域中的变量名必须是唯一的。一个作用域可以嵌套在另一个作用域内。如果一个作用域嵌套在另一个作用域内,最内部作用域内的代码可以访问另一个作用域的变量。

(掌握)解释 JavaScript 中的 null 和 undefined

null表示一个空的对象,什么也没有
undefined 表示声明为赋值 
undefined是从null派生出来的 
null == undefined //true 
null === undefined //false
typeof null // 'object'
typeof undefined // 'undefined'

(掌握)浏览器的事件循环

**1)为什么会有Event Loop **

JavaScript的任务分为两种同步异步,它们的处理方式也各自不同,同步任务是直接放在主线程上排队依次执行,异步任务会放在任务队列中,若有多个异步任务则需要在任务队列中排队等待,任务队列类似于缓冲区,任务下一步会被移到调用栈然后主线程执行调用栈的任务。

调用栈:调用栈是一个栈结构,函数调用会形成一个栈帧,帧中包含了当前执行函数的参数和局部变量等上下文信息,函数执行完后,它的执行上下文会从栈中弹出。

JavaScript是单线程的,单线程是指 js引擎中解析和执行js代码的线程只有一个(主线程),每次只能做一件事情,然而ajax请求中,主线程在等待响应的过程中回去做其他事情,浏览器先在事件表注册ajax的回调函数,响应回来后回调函数被添加到任务队列中等待执行,不会造成线程阻塞,所以说js处理ajax请求的方式是异步的。

综上所述,检查调用栈是否为空以及讲某个任务添加到调用栈中的个过程就是event loop,这就是JavaScript实现异步的核心。

2)浏览器中的 Event Loop

Micro-Task 与 Macro-Task

浏览器端事件循环中的异步队列有两种:macro(宏任务)队列和 micro(微任务)队列。

常见的 macro-task:setTimeoutsetIntervalscript(整体代码)I/O 操作UI 渲染等。

常见的 micro-task: new Promise().then(回调)MutationObserve等。

requestAnimationFrame

requestAnimationFrame也属于异步执行的方法,但该方法既不属于宏任务,也不属于微任务。按照MDN中的定义:

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

requestAnimationFrame是GUI渲染之前执行,但在Micro-Task之后,不过requestAnimationFrame不一定会在当前帧必须执行,由浏览器根据当前的策略自行决定在哪一帧执行。

event loop过程

[[图片上传失败...(image-d272f-1621776892401)]

(opens new window)](https://raw.githubusercontent.com/XQY279/blog/master/image/event-loop.jpg)

  1. 检查macrotask队列是否为空,非空则到2,为空则到3
  2. 执行macrotask中的一个任务
  3. 继续检查microtask队列是否为空,若有则到4,否则到5
  4. 取出microtask中的任务执行,执行完成返回到步骤3
  5. 执行视图更新

当某个宏任务执行完后,会查看是否有微任务队列。如果有,先执行微任务队列中的所有任务,如果没有,会读取宏任务队列中排在最前的任务,执行宏任务的过程中,遇到微任务,依次加入微任务队列。栈空后,再次读取微任务队列里的任务,依次类推。

(掌握)解释事件冒泡以及如何阻止它?

事件冒泡是指嵌套最深的元素触发一个事件,然后这个事件顺着嵌套顺序在父元素上触发。

防止事件冒泡的一种方法是使用 event.cancelBubble 或 event.stopPropagation()(低于 IE 9)。

(掌握)什么是防抖和节流?有什么区别?如何实现?

/** 防抖:
 * 应用场景:当用户进行了某个行为(例如点击)之后。不希望每次行为都会触发方法,而是行为做出后,一段时间内没有再次重复行为,
 * 才给用户响应
 * 实现原理 : 每次触发事件时设置一个延时调用方法,并且取消之前的延时调用方法。(每次触发事件时都取消之前的延时调用方法)
 *  @params fun 传入的防抖函数(callback) delay 等待时间
 *  */
const debounce = (fun, delay = 500) => {
    let timer = null //设定一个定时器
    return function (...args) {
        clearTimeout(timer);
        timer = setTimeout(() => {
            fun.apply(this, args)
        }, delay)
    }
}
/** 节流
 *  应用场景:用户进行高频事件触发(滚动),但在限制在n秒内只会执行一次。
 *  实现原理: 每次触发时间的时候,判断当前是否存在等待执行的延时函数
 * @params fun 传入的防抖函数(callback) delay 等待时间
 * */

const throttle = (fun, delay = 1000) => {
    let flag = true;
    return function (...args) {
        if (!flag) return;
        flag = false
        setTimeout(() => {
            fun.apply(this, args)
            flag = true
        }, delay)
    }
}

区别:节流不管事件触发多频繁保证在一定时间内一定会执行一次函数。防抖是只在最后一次事件触发后才会执行一次函数

(掌握)JSONP 的原理是什么?

尽管浏览器有同源策略,但是 <script> 标签的 src 属性不会被同源策略所约束,可以获取任意服务器上的脚本并执行。jsonp 通过插入 script 标签的方式来实现跨域,参数只能通过 url 传入,仅能支持 get 请求。

(掌握)异步加载JS的方式有哪些?

(掌握)常见web安全及防护原理

上一篇 下一篇

猜你喜欢

热点阅读