最新版js面试题

js打印调试方法有哪些
js中数据类型有哪些
js判断数据类型方法有哪些
js中常用内置对象有哪些
js原型与原型链,获取原型方法,作用域,作用链
js事件模型
js事件流
js中this理解
js闭包理解
js创建对象那几种方式
js创建继承那几种方式
js对dom操作
js中数组与字符串与对象有那些原生方法
js有几种模块规范
js运行机制理解?
js函数传参中arguments对象是什么?
谈谈v8引擎的垃圾回来机制
有哪些操作会操作内存泄漏
箭头函数与普通函数区别
什么是类,什么是模版字符串?
For,foreEach,map性能比较
Window , document对象里面有哪些属性与方法
undefined与null与underclared区别
浏览器和 Node.js 中的事件循环机制有什么区别?
变量提升
事件是如何实现的
es5与es6区别
什么是回调函数
谈谈js实现异步方法有哪些
简单描述一下 Babel 的编译过程?
ES6 Modules 相对于 CommonJS 的优势是什么?

手写js
Bind, apply, call
科里化
使用setTimeout模拟实现setInterval(相反)
订阅与发布
观察者
实现new与instanceof内部机制
实现ajax/promise/async
instanceOf
防抖与节流
版本号排序
虚拟dom转真实dom
列表转树形结构(相反)

1.介绍一下js的数据类型有哪些,值是如何存储的
JavaScript一共有8种数据类型,其中有7种基本数据类型:Undefined、Null、Boolean、Number、String、Symbol(es6新增,表示独一无二的值)和BigInt(es10新增);
1种引用数据类型——Object(Object本质上是由一组无序的名值对组成的)。里面包含 function、Array、Date等。JavaScript不支持任何创建自定义类型的机制,而所有值最终都将是上述 8 种数据类型之一。
原始数据类型:直接存储在栈(stack)中,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。
引用数据类型:同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

  1. && 、 ||和!! 运算符分别能做什么
  • && 叫逻辑与,在其操作数中找到第一个虚值表达式并返回它,如果没有找到任何虚值表达式,则返回最后一个真值表达式。它采用短路来防止不必要的工作。
  • || 叫逻辑或,在其操作数中找到第一个真值表达式并返回它。这也使用了短路来防止不必要的工作。在支持 ES6 默认函数参数之前,它用于初始化函数中的默认参数值。
  • !! 运算符可以将右侧的值强制转换为布尔值,这也是将值转换为布尔值的一种简单方法
  1. js的数据类型的转换
    在 JS 中类型转换只有三种情况,分别是:
  • 转换为布尔值(调用Boolean()方法)
  • 转换为数字(调用Number()、parseInt()和parseFloat()方法)
  • 转换为字符串(调用.toString()或者String()方法)
  • (null和underfined没有.toString方法)

console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log(‘str’ instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
// console.log(undefined instanceof Undefined);
// console.log(null instanceof Null);
可以看出直接的字面量值判断数据类型,instanceof可以精准判断引用数据类型(Array,Function,Object),而基本数据类型不能被instanceof精准判断。

MDN:instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。其意思就是判断对象是否是某一数据类型(如Array)的实例
(3)constructor
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log((‘str’).constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true

这里有一个坑,如果我创建一个对象,更改它的原型,constructor就会变得不可靠了
function Fn(){};
Fn.prototype=new Array();
var f=new Fn();
console.log(f.constructor === Fn); // false
console.log(f.constructor===Array); // true

(4)Object.prototype.toString.call()
使用 Object 对象的原型方法 toString ,使用 call 进行狸猫换太子,借用Object的 toString 方法
var a = Object.prototype.toString;
console.log(a.call(2));
console.log(a.call(true));
console.log(a.call(‘str’));
console.log(a.call([]));
console.log(a.call(function(){}));
console.log(a.call({}));
console.log(a.call(undefined));
console.log(a.call(null));

  1. 介绍 js 有哪些内置对象?
    (1)值属性,这些全局属性返回一个简单值,这些值没有自己的属性和方法。
    例如 Infinity、NaN、undefined、null 字面量
    (2)函数属性,全局函数可以直接调用,不需要在调用时指定所属对象,执行结束后会将结果直接返回给调用者。
    例如 eval()、parseFloat()、parseInt() 等
    (3)基本对象,基本对象是定义或使用其他对象的基础。基本对象包括一般对象、函数对象和错误对象。
    例如 Object、Function、Boolean、Symbol、Error 等
    (4)数字和日期对象,用来表示数字、日期和执行数学计算的对象。
    例如 Number、Math、Date
    (5)字符串,用来表示和操作字符串的对象。
    例如 String、RegExp
    (6)可索引的集合对象,这些对象表示按照索引值来排序的数据集合,包括数组和类型数组,以及类数组结构的对象。例如 Array
    (7)使用键的集合对象,这些集合对象在存储数据时会使用到键,支持按照插入顺序来迭代元素。
    例如 Map、Set、WeakMap、WeakSet
    (8)矢量集合,SIMD 矢量集合中的数据会被组织为一个数据序列。
    例如 SIMD 等
    (9)结构化数据,这些对象用来表示和操作结构化的缓冲区数据,或使用 JSON 编码的数据。
    例如 JSON 等
    (10)控制抽象对象
    例如 Promise、Generator 等
    (11)反射
    例如 Reflect、Proxy
    (12)国际化,为了支持多语言处理而加入 ECMAScript 的对象。
    例如 Intl、Intl.Collator 等
    (13)WebAssembly
    (14)其他
    例如 arguments

  2. undefined 与 undeclared 的区别?
    已在作用域中声明但还没有赋值的变量,是 undefined。相反,还没有在作用域中声明过的变量,是 undeclared 的。

对于 undeclared 变量的引用,浏览器会报引用错误,如 ReferenceError: b is not defined 。但是我们可以使用 typ
eof 的安全防范机制来避免报错,因为对于 undeclared(或者 not defined )变量,typeof 会返回 “undefined”。

  1. null 和 undefined 的区别?
    首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。
    undefined 代表的含义是未定义,
    null 代表的含义是空对象(其实不是真的对象,请看下面的注意!)。一般变量声明了但还没有定义的时候会返回 undefined,null
    主要用于赋值给一些可能会返回对象的变量,作为初始化。

  2. {}和[]的valueOf和toString的结果是什么?
    {} 的 valueOf 结果为 {} ,toString 的结果为 “[object Object]”
    [] 的 valueOf 结果为 [] ,toString 的结果为 “”

  3. Javascript 的作用域和作用域链
    作用域: 作用域是定义变量的区域,它有一套访问变量的规则,这套规则来管理浏览器引擎如何在当前作用域以及嵌套的作用域中根据变量(标识符)进行变量查找。
    作用域链: 作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,我们可以访问到外层环境的变量和
    函数。

  4. Javascript 的作用域和作用域链
    作用域: 作用域是定义变量的区域,它有一套访问变量的规则,这套规则来管理浏览器引擎如何在当前作用域以及嵌套的作用域中根据变量(标识符)进行变量查找。
    作用域链: 作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,我们可以访问到外层环境的变量和
    函数。

  5. javascript 创建对象的几种方式?
    我们一般使用字面量的形式直接创建对象,但是这种创建方式对于创建大量相似对象的时候,会产生大量的重复代码。但 js
    和一般的面向对象的语言不同,在 ES6 之前它没有类的概念。但是我们可以使用函数来进行模拟,从而产生出可复用的对象
    创建方式,我了解到的方式有这么几种:

(1)第一种是工厂模式,工厂模式的主要工作原理是用函数来封装创建对象的细节,从而通过调用函数来达到复用的目的。但是它有一个很大的问题就是创建出来的对象无法和某个类型联系起来,它只是简单的封装了复用代码,而没有建立起对象和类型间的关系。

(2)第二种是构造函数模式。js 中每一个函数都可以作为构造函数,只要一个函数是通过 new 来调用的,那么我们就可以把它称为构造函数。执行构造函数首先会创建一个对象,然后将对象的原型指向构造函数的 prototype 属性,然后将执行上下文中的 this 指向这个对象,最后再执行整个函数,如果返回值不是对象,则返回新建的对象。因为 this 的值指向了新建的对象,因此我们可以使用 this 给对象赋值。构造函数模式相对于工厂模式的优点是,所创建的对象和构造函数建立起了联系,因此我们可以通过原型来识别对象的类型。但是构造函数存在一个缺点就是,造成了不必要的函数对象的创建,因为在 js 中函数也是一个对象,因此如果对象属性中如果包含函数的话,那么每次我们都会新建一个函数对象,浪费了不必要的内存空间,因为函数是所有的实例都可以通用的。

(3)第三种模式是原型模式,因为每一个函数都有一个 prototype 属性,这个属性是一个对象,它包含了通过构造函数创建的所有实例都能共享的属性和方法。因此我们可以使用原型对象来添加公用属性和方法,从而实现代码的复用。这种方式相对于构造函数模式来说,解决了函数对象的复用问题。但是这种模式也存在一些问题,一个是没有办法通过传入参数来初始化值,另一个是如果存在一个引用类型如 Array 这样的值,那么所有的实例将共享一个对象,一个实例对引用类型值的改变会影响所有的实例。

(4)第四种模式是组合使用构造函数模式和原型模式,这是创建自定义类型的最常见方式。因为构造函数模式和原型模式分开使用都存在一些问题,因此我们可以组合使用这两种模式,通过构造函数来初始化对象的属性,通过原型对象来实现函数方法的复用。这种方法很好的解决了两种模式单独使用时的缺点,但是有一点不足的就是,因为使用了两种不同的模式,所以对于代码的封装性不够好。

(5)第五种模式是动态原型模式,这一种模式将原型方法赋值的创建过程移动到了构造函数的内部,通过对属性是否存在的判断,可以实现仅在第一次调用函数时对原型对象赋值一次的效果。这一种方式很好地对上面的混合模式进行了封装。

(6)第六种模式是寄生构造函数模式,这一种模式和工厂模式的实现基本相同,我对这个模式的理解是,它主要是基于一个已有的类型,在实例化时对实例化的对象进行扩展。这样既不用修改原来的构造函数,也达到了扩展对象的目的。它的一个缺点和工厂模式一样,无法实现对象的识别。

  1. JavaScript 继承的几种实现方式?
    我了解的 js 中实现继承的几种方式有:

(1)第一种是以原型链的方式来实现继承,但是这种实现方式存在的缺点是,在包含有引用类型的数据时,会被所有的实例对象所共享,容易造成修改的混乱。还有就是在创建子类型的时候不能向超类型传递参数。

(2)第二种方式是使用借用构造函数的方式,这种方式是通过在子类型的函数中调用超类型的构造函数来实现的,这一种方法解决了不能向超类型传递参数的缺点,但是它存在的一个问题就是无法实现函数方法的复用,并且超类型原型定义的方法子类型也没有办法访问到。

(3)第三种方式是组合继承,组合继承是将原型链和借用构造函数组合起来使用的一种方式。通过借用构造函数的方式来实现类型的属性的继承,通过将子类型的原型设置为超类型的实例来实现方法的继承。这种方式解决了上面的两种模式单独使用时的问题,但是由于我们是以超类型的实例来作为子类型的原型,所以调用了两次超类的构造函数,造成了子类型的原型中多了很多不必要的属性。

(4)第四种方式是原型式继承,原型式继承的主要思路就是基于已有的对象来创建新的对象,实现的原理是,向函数中传入一个对象,然后返回一个以这个对象为原型的对象。这种继承的思路主要不是为了实现创造一种新的类型,只是对某个对象实现一种简单继承,ES5 中定义的 Object.create() 方法就是原型式继承的实现。缺点与原型链方式相同。

(5)第五种方式是寄生式继承,寄生式继承的思路是创建一个用于封装继承过程的函数,通过传入一个对象,然后复制一个对象的副本,然后对象进行扩展,最后返回这个对象。这个扩展的过程就可以理解是一种继承。这种继承的优点就是对一个简单对象实现继承,如果这个对象不是我们的自定义类型时。缺点是没有办法实现函数的复用。

(6)第六种方式是寄生式组合继承,组合继承的缺点就是使用超类型的实例做为子类型的原型,导致添加了不必要的原型属性。寄生式组合继承的方式是使用超类型的原型的副本来作为子类型的原型,这样就避免了创建不必要的属性。

  1. 寄生式组合继承的实现?
    function Person(name) {
    this.name = name;
    }
    Person.prototype.sayName = function() {
    console.log("My name is " + this.name + “.”);
    };
    function Student(name, grade) {
    Person.call(this, name);
    this.grade = grade;
    }
    Student.prototype = Object.create(Person.prototype);
    Student.prototype.constructor = Student;
    Student.prototype.sayMyGrade = function() {
    console.log("My grade is " + this.grade + “.”);
    };

  2. 谈谈你对this、call、apply和bind的理解
    在浏览器里,在全局范围内this 指向window对象;
    在函数中,this永远指向最后调用他的那个对象;
    构造函数中,this指向new出来的那个新的对象;
    call、apply、bind中的this被强绑定在指定的那个对象上;
    箭头函数中this比较特殊,箭头函数this为父作用域的this,不是调用时的this.要知道前四种方式,都是调用时确定,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下来;
    apply、call、bind都是js给函数内置的一些API,调用他们可以为函数指定this的执行,同时也可以传参。

  3. 原型、原型链
    原型: 对象中固有的__proto__属性,该属性指向对象的prototype原型属性。
    原型链: 当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是Object.prototype所以这就是我们新建的对象为什么能够使用toString()等方法的原因。
    特点: JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。

  4. 闭包
    闭包是指有权访问另一个函数作用域中的变量的函数 ——《JavaScript高级程序设计》
    当函数可以记住并访问所在的词法作用域时,就产生了闭包,
    即使函数是在当前词法作用域之外执行 ——《你不知道的JavaScript》
    闭包用途:
    能够访问函数定义时所在的词法作用域(阻止其被回收)
    私有化变量
    模拟块级作用域
    创建模块
    闭包缺点:会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏
    function a(){
    var n = 0;
    function add(){
    n++;
    console.log(n);
    }
    return add;
    }
    var a1 = a(); //注意,函数名只是一个标识(指向函数的指针),而()才是执行函数;
    a1(); //1
    a1(); //2 第二次调用n变量还在内存中

  5. 什么是 DOM 和 BOM?
    DOM 指的是文档对象模型,它指的是把文档当做一个对象来对待,这个对象主要定义了处理网页内容的方法和接口。
    BOM 指的是浏览器对象模型,它指的是把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的法和接口。BOM
    的核心是 window,而 window 对象具有双重角色,它既是通过 js 访问浏览器窗口的一个接口,又是一个 Global(全局)
    对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象的一个属性或者方法存在。window 对象含有 locati
    on 对象、navigator 对象、screen 对象等子对象,并且 DOM 的最根本的对象 document 对象也是 BOM 的 window 对
    象的子对象。

  6. new关键字
    首先创建了一个新的空对象
    设置原型,将对象的原型设置为函数的prototype对象。
    让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
    判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
    function create(Con, …args) {
    let obj = {}
    Object.setPrototypeOf(obj, Con.prototype)
    let result = Con.apply(obj, args)
    return result instanceof Object ? result : obj
    }

  7. 三种事件模型是什么?
    事件 是用户操作网页时发生的交互动作或者网页本身的一些操作,现代浏览器一共有三种事件模型。
    DOM0级模型: ,这种模型不会传播,所以没有事件流的概念,但是现在有的浏览器支持以冒泡的方式实现,它可以在网页中直接定义监听函数,也可以通过 js属性来指定监听函数。这种方式是所有浏览器都兼容的。
    IE 事件模型: 在该事件模型中,一次事件共有两个过程,事件处理阶段,和事件冒泡阶段。事件处理阶段会首先执行目标元素绑定的监听事件。然后是事件冒泡阶段,冒泡指的是事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过 attachEvent 来添加监听函数,可以添加多个监听函数,会按顺序依次执行。
    DOM2 级事件模型: 在该事件模型中,一次事件共有三个过程,第一个过程是事件捕获阶段。捕获指的是事件从 document 一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。后面两个阶段和 IE 事件模型的两个阶段相同。这种事件模型,事件绑定的函数是 addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行。

  8. 事件委托是什么?
    事件委托 本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,并且父节点可以通过事件对象获取到
    目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件代理。
    使用事件代理我们可以不必要为每一个子元素都绑定一个监听事件,这样减少了内存上的消耗。并且使用事件代理我们还可以实现事件的动态绑定,比如说新增了一个子节点,我们并不需要单独地为它添加一个监听事件,它所发生的事件会交给父元素中的监听函数来处理。

  9. 什么是事件传播?
    当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。在“当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。
    事件传播有三个阶段:
    捕获阶段–事件从 window 开始,然后向下到每个元素,直到到达目标元素事件或event.target。
    目标阶段–事件已达到目标元素。
    冒泡阶段–事件从目标元素冒泡,然后上升到每个元素,直到到达 window。

  10. 什么是事件捕获?
    当事件发生在 DOM 元素上时,该事件并不完全发生在那个元素上。在捕获阶段,事件从window开始,一直到触发事件的元素。window----> document----> html----> body ---->目标元素
    function addEvent(el, event, callback, isCapture = false) {
    if (!el || !event || !callback || typeof callback !== ‘function’) return;
    if (typeof el === ‘string’) {
    el = document.querySelector(el);
    };
    el.addEventListener(event, callback, isCapture);
    }
    addEvent(document, ‘DOMContentLoaded’, () => {
    const child = document.querySelector(’.child’);
    const parent = document.querySelector(’.parent’);
    const grandparent = document.querySelector(’.grandparent’);
    addEvent(child, ‘click’, function (e) {
    console.log(‘child’);
    });
    addEvent(parent, ‘click’, function (e) {
    console.log(‘parent’);
    });
    addEvent(grandparent, ‘click’, function (e) {
    console.log(‘grandparent’);
    });
    addEvent(document, ‘click’, function (e) {
    console.log(‘document’);
    });
    addEvent(‘html’, ‘click’, function (e) {
    console.log(‘html’);
    })
    addEvent(window, ‘click’, function (e) {
    console.log(‘window’);
    })
    });

  11. 什么是事件冒泡?
    事件冒泡刚好与事件捕获相反,当前元素---->body ----> html---->document ---->window。当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。在冒泡阶段,事件冒泡,或者事件发生在它的父代,祖父母,祖父母的父代,直到到达window为止。
    function addEvent(el, event, callback, isCapture = false) {
    if (!el || !event || !callback || typeof callback !== ‘function’) return;
    if (typeof el === ‘string’) {
    el = document.querySelector(el);
    };
    el.addEventListener(event, callback, isCapture);
    }
    addEvent(document, ‘DOMContentLoaded’, () => {
    const child = document.querySelector(’.child’);
    const parent = document.querySelector(’.parent’);
    const grandparent = document.querySelector(’.grandparent’);
    addEvent(child, ‘click’, function (e) {
    console.log(‘child’);
    });
    addEvent(parent, ‘click’, function (e) {
    console.log(‘parent’);
    });
    addEvent(grandparent, ‘click’, function (e) {
    console.log(‘grandparent’);
    });
    addEvent(document, ‘click’, function (e) {
    console.log(‘document’);
    });
    addEvent(‘html’, ‘click’, function (e) {
    console.log(‘html’);
    })
    addEvent(window, ‘click’, function (e) {
    console.log(‘window’);
    })
    });

  12. DOM 操作——怎样添加、移除、移动、复制、创建和查找节点?
    (1)创建新节点
    createDocumentFragment() //创建一个DOM片段
    createElement() //创建一个具体的元素
    createTextNode() //创建一个文本节点
    (2)添加、移除、替换、插入
    appendChild(node)
    removeChild(node)
    replaceChild(new,old)
    insertBefore(new,old)
    (3)查找
    getElementById();
    getElementsByName();
    getElementsByTagName();
    getElementsByClassName();
    querySelector();
    querySelectorAll();
    (4)属性操作
    getAttribute(key);
    setAttribute(key, value);
    hasAttribute(key);
    removeAttribute(key);

  13. 常用的正则表达式
    (1)匹配 16 进制颜色值
    var color = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;
    (2)匹配日期,如 yyyy-mm-dd 格式
    var date = /1{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) / ; ( 3 ) 匹 配 q q 号 v a r q q = / [ 1 − 9 ] [ 0 − 9 ] 4 , 10 /; (3)匹配 qq 号 var qq = /^[1-9][0-9]{4,10} /3qqvarqq=/[19][09]4,10/g;
    (4)手机号码正则
    var phone = /^1[34578]\d{9}$/g;
    (5)用户名正则
    var username = /2[a-zA-Z0-9_$]{4,16}KaTeX parse error: Undefined control sequence: \- at position 41: … /^([A-Za-z0-9_\̲-̲\.])+\@([A-Za-z…/;
    (7)身份证号(18位)正则
    var cP = /3\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]KaTeX parse error: Undefined control sequence: \/ at position 44: …tps?|ftp|file):\̲/̲\/)?([\da-z\.-]…/;
    (9)ipv4地址正则
    var ipP = /^(?😦?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?) / ; ( 10 ) / / 车 牌 号 正 则 v a r c P a t t e r n = / [ 京 津 沪 渝 冀 豫 云 辽 黑 湘 皖 鲁 新 苏 浙 赣 鄂 桂 甘 晋 蒙 陕 吉 闽 贵 粤 青 藏 川 宁 琼 使 领 A − Z ] 1 [ A − Z ] 1 [ A − Z 0 − 9 ] 4 [ A − Z 0 − 9 挂 学 警 港 澳 ] 1 /; (10)//车牌号正则 var cPattern = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1} /;(10)//varcPattern=/[使AZ]1[AZ]1[AZ09]4[AZ09]1/;

  14. Ajax 是什么? 如何创建一个 Ajax?
    我对 ajax 的理解是,它是一种异步通信的方法,通过直接由 js 脚本向服务器发起 http 通信,然后根据服务器返回的数据,更新网页的相应部分,而不用刷新整个页面的一种方法。
    (原生):
    //1:创建Ajax对象
    var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject(‘Microsoft.XMLHTTP’);// 兼容IE6及以下版本
    //2:配置 Ajax请求地址
    xhr.open(‘get’,‘index.xml’,true);
    //3:发送请求
    xhr.send(null); // 严谨写法
    //4:监听请求,接受响应
    xhr.onreadysatechange=function(){
    if(xhr.readySate4&&xhr.status200 || xhr.status==304 )
    console.log(xhr.responsetXML)
    }
    jQuery写法
    $.ajax({
    type:‘post’,
    url:’’,
    async:ture,//async 异步 sync 同步
    data:data,//针对post请求
    dataType:‘jsonp’,
    success:function (msg) {
    },
    error:function (error) {
    }
    })
    promise 封装实现:
    function getJSON(url) {
    // 创建一个 promise 对象
    let promise = new Promise(function(resolve, reject) {
    let xhr = new XMLHttpRequest();

    // 新建一个 http 请求
    xhr.open(“GET”, url, true);

    // 设置状态的监听函数
    xhr.onreadystatechange = function() {
    if (this.readyState !== 4) return;
    // 当请求成功或失败时,改变 promise 的状态
    if (this.status === 200) {
    resolve(this.response);
    } else {
    reject(new Error(this.statusText));
    }
    };
    // 设置错误监听函数
    xhr.onerror = function() {
    reject(new Error(this.statusText));
    };
    // 设置响应的数据类型
    xhr.responseType = “json”;
    // 设置请求头信息
    xhr.setRequestHeader(“Accept”, “application/json”);
    // 发送 http 请求
    xhr.send(null);
    });
    return promise;
    }

  15. 谈谈你对模块化开发的理解?
    我对模块的理解是,一个模块是实现一个特定功能的一组方法。在最开始的时候,js 只实现一些简单的功能,所以并没有模块的概念
    ,但随着程序越来越复杂,代码的模块化开发变得越来越重要。
    由于函数具有独立作用域的特点,最原始的写法是使用函数来作为模块,几个函数作为一个模块,但是这种方式容易造成全局变量的污
    染,并且模块间没有联系。
    后面提出了对象写法,通过将函数作为一个对象的方法来实现,这样解决了直接使用函数作为模块

  16. js 的几种模块规范?
    js 中现在比较成熟的有四种模块加载方案:
    第一种是 CommonJS 方案,它通过 require 来引入模块,通过 module.exports 定义模块的输出接口。这种模块加载方案是服务器端的解决方案,它是以同步的方式来引入模块的,因为在服务端文件都存储在本地磁盘,所以读取非常快,所以以同步的方式加载没有问题。但如果是在浏览器端,由于模块的加载是使用网络请求,因此使用异步加载的方式更加合适。
    第二种是 AMD 方案,这种方案采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js 实现了 AMD 规范。
    第三种是 CMD 方案,这种方案和 AMD 方案都是为了解决异步模块加载的问题,sea.js 实现了 CMD 规范。它和require.js的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。
    第四种方案是 ES6 提出的方案,使用 import 和 export 的形式来导入导出模块。

  17. AMD 和 CMD 规范的区别?
    它们之间的主要区别有两个方面。
    第一个方面是在模块定义时对依赖的处理不同。AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块。而 CMD 推崇就近依赖,只有在用到某个模块的时候再去 require。
    第二个方面是对依赖模块的执行时机处理不同。首先 AMD 和 CMD 对于模块的加载方式都是异步加载,不过它们的区别在于

  18. ES6 模块与 CommonJS 模块、AMD、CMD 的差异。
    1.CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。CommonJS 模块输出的是值的
    ,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令 import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
    2.CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。CommonJS 模块就是对象,即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

  19. requireJS的核心原理是什么?
    require.js 的核心原理是通过动态创建 script 脚本来异步引入模块,然后对每个脚本的 load 事件进行监听,如果每个脚本都加载完成了,再调用回调函数
    面试中该如何回答呢?
    下面是我个人推荐的回答:

  20. 事件循环
    微任务包括了 promise 的回调、node 中的 process.nextTick 、对 Dom 变化监听的 MutationObserver。
    宏任务包括了 script 脚本的执行、setTimeout ,setInterval ,setImmediate 一类的定时事件,还有如 I/O 操作、UI 渲
    染等。
    首先js 是单线程运行的,在代码执行的时候,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。
    在执行同步代码的时候,如果遇到了异步事件,js 引擎并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务
    当同步事件执行完毕后,再将异步事件对应的回调加入到与当前执行栈中不同的另一个任务队列中等待执行。
    任务队列可以分为宏任务对列和微任务对列,当当前执行栈中的事件执行完毕后,js 引擎首先会判断微任务对列中是否有任务可以执行,如果有就将微任务队首的事件压入栈中执行。
    当微任务对列中的任务都执行完成后再去判断宏任务对列中的任务。
    setTimeout(function() {
    console.log(1)
    }, 0);
    new Promise(function(resolve, reject) {
    console.log(2);
    resolve()
    }).then(function() {
    console.log(3)
    });
    process.nextTick(function () {
    console.log(4)
    })
    console.log(5)
    此结果是25431

  21. arguments 的对象是什么?
    arguments对象是函数中传递的参数值的集合。它是一个类似数组的对象,因为它有一个length属性,我们可以使用数组索引表示法arguments[1]来访问单个值,但它没有数组中的内置方法,如:forEach、reduce、filter和map。
    我们可以使用Array.prototype.slice将arguments对象转换成一个数组。

  22. 简单介绍一下 V8 引擎的垃圾回收机制
    v8 的垃圾回收机制基于分代回收机制,这个机制又基于世代假说,这个假说有两个特点,一是新生的对象容易早死,另一个是不死的对象会活得更久。基于这个假说,v8 引擎将内存分为了新生代和老生代。
    新创建的对象或者只经历过一次的垃圾回收的对象被称为新生代。经历过多次垃圾回收的对象被称为老生代。
    新生代被分为 From 和 To 两个空间,To 一般是闲置的。当 From 空间满了的时候会执行 Scavenge 算法进行垃圾回收。当我们执行垃圾回收算法的时候应用逻辑将会停止,等垃圾回收结束后再继续执行。这个算法分为三步:
    (1)首先检查 From 空间的存活对象,如果对象存活则判断对象是否满足晋升到老生代的条件,如果满足条件则晋升到老生代。如果不满足条件则移动 To 空间。
    (2)如果对象不存活,则释放对象的空间。
    (3)最后将 From 空间和 To 空间角色进行交换。
    新生代对象晋升到老生代有两个条件:
    (1)第一个是判断是对象否已经经过一次 Scavenge 回收。若经历过,则将对象从 From 空间复制到老生代中;若没有经历,则复制到 To 空间。
    (2)第二个是 To 空间的内存使用占比是否超过限制。当对象从 From 空间复制到 To 空间时,若 To 空间使用超过 25%,则对象直接晋升到老生代中。设置 25% 的原因主要是因为算法结束后,两个空间结束后会交换位置,如果 To 空间的内存太小,会影响后续的内存分配。
    老生代采用了标记清除法和标记压缩法。标记清除法首先会对内存中存活的对象进行标记,标记结束后清除掉那些没有标记的对象。由于标记清除后会造成很多的内存碎片,不便于后面的内存分配。所以了解决内存碎片的问题引入了标记压缩法。
    由于在进行垃圾回收的时候会暂停应用的逻辑,对于新生代方法由于内存小,每次停顿的时间不会太长,但对于老生代来说每次垃圾回收的时间长,停顿会造成很大的影响。 为了解决这个问题 V8 引入了增量标记的方法,将一次停顿进行的过程分为了多步,每次执行完一小步就让运行逻辑执行一会,就这样交替运行。

  23. 哪些操作会造成内存泄漏?
    1.意外的全局变量
    2.被遗忘的计时器或回调函数
    3.脱离 DOM 的引用
    4.闭包
    第一种情况是我们由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
    第二种情况是我们设置了setInterval定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
    第三种情况是我们获取一个DOM元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回收。
    第四种情况是不合理的使用闭包,从而导致某些变量一直被留在内存当中。

  24. ECMAScript 2015(ES6)有哪些新特性?
    块作用域

    箭头函数
    模板字符串
    加强的对象字面
    对象解构
    Promise
    模块
    Symbol
    代理(proxy)Set
    函数默认参数
    rest 和展开

  25. 什么是类?
    类(class)是在 JS 中编写构造函数的新方法。它是使用构造函数的语法糖,在底层中使用仍然是原型和基于原型的继承。
    //ES5 Version
    function Person(firstName, lastName, age, address){
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
    this.address = address;
    }
    Person.self = function(){
    return this;
    }
    Person.prototype.toString = function(){
    return “[object Person]”;
    }
    Person.prototype.getFullName = function (){
    return this.firstName + " " + this.lastName;
    }
    //ES6 Version
    class Person {
    constructor(firstName, lastName, age, address){
    this.lastName = lastName;
    this.firstName = firstName;
    this.age = age;
    this.address = address;
    }
    static self() {
    return this;
    }
    toString(){
    return “[object Person]”;
    }
    getFullName(){
    return ${this.firstName} ${this.lastName};
    }
    }

  26. 什么是Proxy?
    Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”,即对编程语言进行编程。
    Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

  27. 写一个通用的事件侦听器函数
    const EventUtils = {
    // 视能力分别使用dom0||dom2||IE方式 来绑定事件
    // 添加事件
    addEvent: function(element, type, handler) {
    if (element.addEventListener) {
    element.addEventListener(type, handler, false);
    } else if (element.attachEvent) {
    element.attachEvent(“on” + type, handler);
    } else {
    element[“on” + type] = handler;
    }
    },
    // 移除事件
    removeEvent: function(element, type, handler) {
    if (element.removeEventListener) {
    element.removeEventListener(type, handler, false);
    } else if (element.detachEvent) {
    element.detachEvent(“on” + type, handler);
    } else {
    element[“on” + type] = null;
    }
    },
    // 获取事件目标
    getTarget: function(event) {
    return event.target || event.srcElement;
    },
    // 获取 event 对象的引用,取到事件的所有信息,确保随时能使用 event
    getEvent: function(event) {
    return event || window.event;
    },
    // 阻止事件(主要是事件冒泡,因为 IE 不支持事件捕获)
    stopPropagation: function(event) {
    if (event.stopPropagation) {
    event.stopPropagation();
    } else {
    event.cancelBubble = true;
    }
    },
    // 取消事件的默认行为
    preventDefault: function(event) {
    if (event.preventDefault) {
    event.preventDefault();
    } else {
    event.returnValue = false;
    }
    }
    };

  28. 什么是高阶函数?
    高阶函数只是将函数作为参数或返回值的函数。
    function higherOrderFunction(param,callback){
    return callback(param);
    }

  29. 什么是回调函数?回调函数有什么缺点
    回调函数是一段可执行的代码段,它作为一个参数传递给其他的代码,其作用是在需要的时候方便调用这段(回调函数)代码。
    在JavaScript中函数也是对象的一种,同样对象可以作为参数传递给函数,因此函数也可以作为参数传递给另外一个函数,这个作为参数的函数就是回调函数。
    const btnAdd = document.getElementById(‘btnAdd’);
    btnAdd.addEventListener(‘click’, function clickCallback(e) {
    // do something useless
    });
    回调函数有一个致命的弱点,就是容易写出回调地狱(Callback hell)。假设多个事件存在依赖性:
    setTimeout(() => {
    console.log(1)
    setTimeout(() => {
    console.log(2)
    setTimeout(() => {
    console.log(3)
    },3000)
    },2000)

  30. instanceof的原理是什么,如何实现
    instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。
    实现 instanceof:
    首先获取类型的原型
    然后获得对象的原型
    然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null
    function myInstanceof(left, right) {
    let prototype = right.prototype
    left = left.proto
    while (true) {
    if (left === null || left === undefined)
    return false
    if (prototype === left)
    return true
    left = left.proto
    }
    }


  1. 0-9 ↩︎

  2. a-zA-Z$ ↩︎

  3. 1-9 ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值