在js中,存在着6种原始值:es6
* boolean * number * string * undefined * null * symbol
注意: 虽然typeof null输出的是object,可是null并非object类型,由于早期的bug;面试
number类型是浮点数类型,因此才会有js精度问题出现。0.1+0.2 !== 0.3。 原始类型存储的都是值,是没有函数能够调用的。
在js中,除了原始类型其余的就是对象类型了。
Q1:对象类型和原始类型的不一样之处?ajax
答:对象类型和原始类型不一样的是,原始类型存贮的是值,对象那个类型存储的是地址(指针)。当建立了一个对象类型的时候,计算机回在内存 中开辟一个空间赖存放值,可是咱们须要找到这个空间,这个控件会拥有一个地址(指针)。当咱们将变量赋值给另一个变量时,复制的是本来变量的地址(指针)。
Q2:函数参数是对象会发生什么?算法
答:函数参数是对象的话,在函数内部可能会改变这个参数对象,生成新对象。
Q1:typeof是否能正确判断类型?编程
答:对于原始类型来讲,typeof除了null均可以正确显示类型,typeof null显示的是object是错误的。对于其余的,除了function会返回function,其余都会返回object。
Q2. instanceof能正确判断对象的原理是什么?数组
答:instanceof内部机制是经过原型链来判断对象类型的。 eg: const Person = function () {} const p1 = new Person() p1 instanceof Person // true var str = 'hello world' str instanceof String // false
1.转Boolean: 在条件判断时,除了undefined,null,0,-0,NaN,'',false,其余全部值都转化为true,包括全部对象。
2.对象转原始类型: 对象在转换类型的时候,会调用内置的[[ToPrimitive]]函数,对于该函数来讲,promise
算法逻辑通常来讲以下: * 若是已是原始类型了,就不须要换砖了 * 调用x.valueof(),若是转换为基础类型,就返回转换的值 * 调用x.toString(),若是转化为基础类型,就返回转换的值 * 若是都没有返回原始类型,就会报错 1)四则运算符: 加法运算符特色: *运算中若是一方为字符串,那么就会把另外一方叶转换为字符串,1 + '1' // '11' *若是一方不是字符串或者数字,那么会将它转化为数字或者字符串:true+ true // 2 ; 4 + [1,2,3] // '41,2,3' 注意: + 'a'是快速将'a'转化成number类型的写法;'a' + + 'b' // 'aNaN' 那么对于除了加法运算符其余运算符来讲,只要一方是数字,那么另外一方就会被转化为数字。反正最终都是要转化为数字来运算。 4 * '3' // 12 4 * [] // 0 4 * [1,2] // NaN 2)比较运算符 大于小于运算都会转化为数字进行运算。 关于 ==: *undefined 等于 null *字符串和数字比较时,字符串转数字 *数字和布尔类型比较时,布尔转数字 *字符串和布尔比较时,二者转数字
this永远指向最后调用它的那个对象。
1.改变this指向:浏览器
* 使用es6箭头函数 * 在函数内部使用 _this = this * 使用 apply,call,bind * new 实例化一个对象
2.箭头函数: 箭头函数的this始终指向函数定义时的this,而非执行时(意味着若是箭头函数被非箭头函数包含,this绑定的就是最近一层非箭头函数的this)。闭包
箭头函数中没有this绑定,必须经过查找做用域链来决定其值。
3.apply,call,bind区别
apply和call只是传入的参数不一样。call接受的是若干个参数列表,而apply接受的是一个包含多个参数的数组。
bind和apply,call区别:并发
bind建立了一个新的函数,须要手动去调用它;
Q1: ==和 === 有什么区别?
答: 对于 == 来讲,若是双方的类型不同的华,就会进行类型转换。 流程: * 首先判断二者类型是否相同 * 判断是否为 undefined == null //true * string转number比较 * boolean转number比较 * object转原始类型比较 可是,=== 的比较只须要判断二者类型和值是否相同。
Q1: 什么是闭包?
答:函数A内部有一个函数B,函数B能够访问到函数A中的变量,那么函数B就是闭包。
Q2:经典面试题:循环中使用闭包解决var定义函数的问题:
答:第一种:使用闭包的方式: for (var i = 1; i <=5 ; i++) { ;(function(j) { setTimeout(function timer() { console.log(j) }, j * 1000) })(i)}
第二种:用let定义的方式: for (let i=1 ; i <=5 ; i++) { setTimeout(function timer() { console.log(i) }, i * 1000) } Q3:闭包的优缺点? 闭包做用(优势):能够读取到函数内部的变量,可让这些变量的值始终保持在内存中,不会被垃圾回收掉。
闭包缺点:由于闭包会使得函数中变量保存在内存中,因此会增大内存使用量,使用不当很容易形成内存泄漏。浪费内存。
清除闭包:退出函数以前,将不使用的局部变量删除。
1.堆和栈:栈(stack)为自动分配的内存空间,它由系统自动释放;而堆(heap)则是动态分配的内存,大小不定也不会自动释放。
2.基本数据类型存放在栈中,数据大小肯定,内存空间大小能够分配,是直接按值存放的,因此能够直接访问。基本类型的比较是值的比较。
3.引用类型存放在堆中,变量实际上存放的是栈1内存的指针,引用类型的值可变。引用类型的比较是引用的比较。因此比较两个引用类型,是看其的引用是否指向同一个对象。
Q1.浅拷贝?
答:浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。
Q2.深拷贝?
答: 深拷贝递归复制了对象的全部层级属性。
Q3.深拷贝,浅拷贝以及赋值的区别?
答:浅拷贝,深拷贝和原数据都不是指向同一对象,而赋值对象和原对象是指向同一对象。 第一层数据为基本类型,改变会使原数据改变,深拷贝以及浅拷贝不会。 原数据中包含子对象,改变子对象会使赋值对象以及浅拷贝对象改变,不会使深拷贝对象改变。
Q4.如何实现浅拷贝?
答:1.能够经过Object.assign来解决。(Object.assign()方法用于将全部可枚举的值从一个或多个源对象复制到目标对象。它将返回目标对象。) eg: let a={ age: 1 } let b=O bject.assign({}, a) a.age=2 console.log(b.age) // 1 2.能够经过展开运算符...来实现浅拷贝 eg: let a={ age: 1} let b={ ...a } a.age=2 console.log(b.age) // 1
Q5.如何实现深拷贝?
答:1.该问题能够经过JSON.parse(JSON.stringify(object))来解决。 eg: let a={age: 1, jobs: { first: 'FE' } } let b=J SON.parse(JSON.stringify(a)) a.jobs.first='native' console.log(b.jobs.first) // FE 可是该方法也是有局限性的: * 会忽略undefined * 会忽略symbol * 不能序列化函数 * 不能解决循环引用的对象
js是基于原型的继承。
1.使用构造函数建立对象后,新对象与构造函数没有关系了,新对象的[[prototype]]属性指向的是构造函数的原型对象。
2.构造函数、原型和实例的关系:
* 构造函数都有一个属性prototype,这个属性是一个对象(Object的实例) * 原型对象里面有个constractor属性,该属性指向原型对象所属的构造函数。当原型对象被修改过,constractor就不必定指向原来的构造函数了。 * 实例对象都有一个_proto_属性,该属性也指向构造函数的原型对象,它是一个非标准属性,不能够用于编程,它用于浏览器本身使用。
3.prototype和_proto_的关系:
* prototype是构造函数的属性; * _proto_是实例对象的属性; * 这二者都会指向同一个对象。 总结:*函数也是对象,对象不必定是函数; * 对象的本质:无序的键值对集合,键值对当中的值能够是任意数据类型的值; *对象就是一个容器,这个容器当中放的是属性和方法。
4.原型链:
原型链就是能够理解为有限的实例对象和原型之间组成的有限链,就是用来实现属性共享和继承的。
5.属性搜索:
* 在访问对象的某个成员的时候在对象中查找是否存在; * 若是当前对象中没有就在构造函数的原型对象中查找; * 若是原型对象中没有就在原型对象的原型上找; * 直到找到Object的原型对象的原型是null为止。 eg: var arr = [] 原型链:arr -> Array.prototype -> Object.prototype -> null
Q1:什么是原型?
答: 原型也是一个对象,而且这个对象中包含了不少函数。原型的constract属性指向构造函数,构造函数又经过prototype属性指回原型,到那时并非全部函数都具备这个属性。
1.原型链继承:将父类的实例做为子类的原型。
eg:Cat.prototype = new Animal() 缺点:子类没法给父类传参,子类要新增属性和方法的话,必需要在new Parent()这样的语句以后执行,不然会被覆盖。
2.构造继承:使用父类的构造函数来加强子类的实例,等因而在子类的构造函数内部执行Parent.call(this)。
eg:function Cat(name) { Animal.call(this); this.name = name || 'Tom'; } var cat = new Cat(); 缺点:没有原型,每次建立一个子类实例都要执行一遍Parent函数。
3.组合式继承:结合原型链继承和构造继承,组合式继承在使用过程当中会被调用两次:一次是建立子类的时候,一次是在子类构造函数的内部。
eg: function Cat(name){ Animal.call(this); this.name = name || 'Tom'; } Cat.prototype = new Animal();
3.class继承:
class继承的核心在于使用extends代表继承自哪一个父类,而且在子类构造函数中必须调用super,由于这段代码能够当作Parent.call(this,value). eg: class Parent { constructor(value) { this.val = value } getValue() { console.log(this.val) } } class Child extends Parent { constructor(value) { super(value) this.val = value } } let child = new Child(1) child.getValue() // 1 child instanceof Parent // true
1.var和let区别:
* let有块级做用域的说法,也就是let声明的变量只能在这个块内部使用,别的地方不能使用。var声明的变量能够在全局使用,var在函数内部声明的变量只能在函数内部使用。 * let没有变量提高,var有变量提高 * let声明的变量存在“暂时性死区”,而var声明的变量没有 * let不容许在同一个快内重复声明一个变量。所以也不能在函数内部重复声明一个变量。 * var声明的变量会挂载到window上,而let声明的对象不会挂载到window上。
const特性:
Q1.什么是提高?
答:就是变量能够在神明以前使用,而且值为undefined。
Q2. 什么是暂时性死区?
答:在let命令声明以前,该变量都素hi不可用的。
Q1.为何要使用模块化?
答:使用模块化能够带来如下好处: * 解决命名冲突; * 提供复用性; * 提升代码可维护性。
Q2.实现模块化的方式?
CommonJS AMD CMD
1.require/exports:
遵循CommonJS/AMD,只能在运行时肯定模块的依赖关系及输入/输出的变量,没法进行静态优化。
2.import/export:
遵循ES6规范,支持编译时静态分析,便于JS引入宏和类型检验,动态绑定。
1. 并发(concurrency)和并行(parallelism)区别: Q1:并发与并行的区别? 答:并发是宏观概念,我分别有任务A和任务B,在一段时间内经过任务间的切换完成了这个任务。 并行是微观概念,假设CPU中存在两个核心,那么我就能够同时完成任务A和任务B。同时完成多个任务的状况能够称之为并行。 2.回调函数(callback): Q1.什么是回调函数? 答:ajax(url,() => { //处理逻辑; }) Q2.回调函数有什么缺点? 答:1)回调函数有一个致命的弱点就是容易写出回调地狱(callback hell)。 eg: ajax(url, () => { // 处理逻辑 ajax(url1, () => { // 处理逻辑 ajax(url2, () => { // 处理逻辑 }) }) }) 回调地狱根本问题: * 嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身; * 嵌套函数一多,就很难处理错误。 2)回调函数不能使用trycatch捕获错误,不能直接return。 Q3.如何解决“地狱回调”? 答:* function拆解 * promise * generater * async/await 3. Generator: 4. promise: Q1:Promise的特色? 答:对象的状态不受外界影响,只有三种状态:pending resolved rejected 状态一旦改变,就不会再变,任什么时候候均可以获得这个结果。 Q2. Promise的优缺点? 答:缺点:* promise没法取消,一旦新建就会当即执行,没法中途取消; * 若是不设置回调函数,promise内部会抛出错误,不会反映到外部; * 当处于pending状态时,没法得知目前进展到哪个阶段(刚刚开始仍是即将完成)。 优势:能够解决回调地狱问题。 Q3. 什么是Promise链? 答:就是在Promise构造函数里面经过then来一步一步使用promise,由于then方法返回的也是一个全新的promise实例。 Q4. Promise构造函数执行和then函数执行有什么区别? 答:then方法每次都返回一个promise实例。 5. Async/Await: Q1.async的特色? 答:一个函数若是加上async,那么该函数就会返回一个promise。async就是将函数返回值使用Promise.resolve()包裹了一下,和then中处理返回值同样,而且await只能配套在async函数内部使用。 Q2.优缺点? 答: 优势:处理then的调用链可以更清晰的写出代码,而且可以优雅的解决回调地狱问题。 缺点:由于await将一部代码改形成了同步代码,若是多个异步代码没有依赖性却使用了await会致使性能上的下降。 Q3.await原理是什么? 答:await内部实现了generator。 实现async/await: async function doIt() { console.time("doIt"); const time1 = 300; const time2 = await step1(time1); const time3 = await step2(time2); const result = await step3(time3); console.log(`result is ${result}`); console.timeEnd("doIt"); } doIt(); 6.经常使用定时器函数: setTimeout() setInterval() requestAnimationFrame() requestAnimationFrame自带函数节流功能,基本能够保证在16.6毫秒内只执行一次,而且该函数的延时效果是精准的。若是有循环定时器的需求,requestAnimationFrame是最好的选择。