资讯专栏INFORMATION COLUMN

2019面试笔记

nidaye / 809人阅读

摘要:使用构造函数创建对象后,新对象与构造函数没有关系了,新对象的属性指向的是构造函数的原型对象。构造继承使用父类的构造函数来增强子类的实例,等于是在子类的构造函数内部执行。

一.js原始类型:

在js中,存在着6种原始值:

* boolean 
* number 
* string 
* undefined 
* null 
* symbol 

注意: 虽然typeof null输出的是object,但是null并不是object类型,因为早期的bug;

number类型是浮点数类型,所以才会有js精度问题出现。0.1+0.2 !== 0.3。 原始类型存储的都是值,是没有函数可以调用的。 

symbol类型: symbol类型的对象永远不相等,即便创建他们的时候传入了相同的值,可以借助此特性解决属性名的冲突问题。
for...in..拿不到symbol类型的属性值,而且要用[]的方式来读取symbol类型的属性值,用点的方式读不到。

二.对象(Object)类型:

在js中,除了原始类型其他的就是对象类型了。
Q1:对象类型和原始类型的不同之处?

答:对象类型和原始类型不同的是,原始类型存贮的是值,对象那个类型存储的是地址(指针)。当创建了一个对象类型的时候,计算机回在内存
中开辟一个空间赖存放值,但是我们需要找到这个空间,这个控件会拥有一个地址(指针)。当我们将变量赋值给另外一个变量时,复制的是原本变量的地址(指针)。 

Q2:函数参数是对象会发生什么?

答:函数参数是对象的话,在函数内部可能会改变这个参数对象,生成新对象。
三.typeof VS instanceof

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]]函数,对于该函数来说,

算法逻辑一般来说如下:
  * 如果已经是原始类型了,就不需要换砖了
  * 调用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

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创建了一个新的函数,需要手动去调用它; 
六.new的过程:

创建一个空对象obj;

将新创建的空对象的隐式原型指向其构造函数的显示原型;

使用call改变this的指向;

如果无返回值或者返回一个非对象值,则将obj返回作为新对象;如果返回值时一个新对象的华那么直接返回该对象。

七. == VS ===

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属性指回原型,到那时并不是所有函数都具有这个属性。
十一:js继承:

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
十二:var、let及const区别:

1.var和let区别:

* let有块级作用域的说法,也就是let声明的变量只能在这个块内部使用,别的地方不能使用。var声明的变量可以在全局使用,var在函数内部声明的变量只能在函数内部使用。
* let没有变量提升,var有变量提升
* let声明的变量存在“暂时性死区”,而var声明的变量没有
* let不允许在同一个快内重复声明一个变量。因此也不能在函数内部重复声明一个变量。
* var声明的变量会挂载到window上,而let声明的对象不会挂载到window上。

const特性:

const声明一个只读的常量。一旦声明,常量的值就不能改变;

const声明的变量必须初始化,不然会报错;

const声明的变量也存在暂时性死区;

const声明的变量只在所声明的块级作用域内有效;

const声明的常量也与let一样不可重复声明。

Q1.什么是提升?

答:就是变量可以在神明之前使用,并且值为undefined。

Q2. 什么是暂时性死区?

答:在let命令声明之前,该变量都素hi不可用的。
十三.模块化:

Q1.为什么要使用模块化?

答:使用模块化可以带来以下好处:
  * 解决命名冲突;
  * 提供复用性;
  * 提高代码可维护性。

Q2.实现模块化的方式?

CommonJS  AMD  CMD

1.require/exports:

遵循CommonJS/AMD,只能在运行时确定模块的依赖关系及输入/输出的变量,无法进行静态优化。

2.import/export:

遵循ES6规范,支持编译时静态分析,便于JS引入宏和类型检验,动态绑定。
十四、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是最好的选择。
        


文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102454.html

相关文章

  • 【前端刷题笔记02】字节跳动2019面试

    摘要:为什么状态需要经过最大报文段生存时间才能返回到状态虽然按道理,四个报文都发送完毕,我们可以直接进入状态了,但是我们必须假象网络是不可靠的,有可以最后一个丢失。所以状态就是用来重发可能丢失的报文。 1、TCP的三次握手和四次挥手 1.1 三次握手: 客户端请求 -> 服务器响应 -> 客户端确认收到响应,建立连接(保证网络正常) showImg(https://segmentfault....

    amc 评论0 收藏0
  • 【前端刷题笔记01】 - 字节跳动2019春招面试

    摘要:某个请求任务耗时严重,不会影响到其它连接的正常执行如何实现长连接 1、sleep函数该如何实现的 sleep函数,将程序执行挂起一段时间,阻塞程序的运行 ES6方案: function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function test() { con...

    马忠志 评论0 收藏0
  • 2019-我的前端面试

    摘要:先说下我面试情况,我一共面试了家公司。篇在我面试的众多公司里,只有同城的面问到相关问题,其他公司压根没问。我自己回答的是自己开发组件面临的问题。完全不用担心对方到时候打电话核对的问题。 2019的5月9号,离发工资还有1天的时候,我的领导亲切把我叫到办公室跟我说:阿郭,我们公司要倒闭了,钱是没有的啦,为了不耽误你,你赶紧出去找工作吧。听到这话,我虎躯一震,这已经是第2个月没工资了。 公...

    iKcamp 评论0 收藏0
  • Android工程师转型Java后端开发之路,自己选的路,跪着也要走下去!

    本文是公众号读者jianfeng投稿的面试经验恭喜该同学成功转型目录:毅然转型,没头苍蝇制定目标,系统学习面试经历毅然转岗,没头苍蝇首先,介绍一下我的背景。本人坐标广州,2016年毕业于一个普通二本大学,曾经在某机构培训过Android。2018年初的时候已经在两家小公司工作干了两年的android开发,然后会一些Tomcat、Servlet之类的技术,当时的年薪大概也就15万这样子。由于个人发展...

    番茄西红柿 评论0 收藏0

发表评论

0条评论

nidaye

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<