阅读 170

web架构师 web前端面试100问之JavaScript(ES6/ES7)

1、什么是原型、原型链,有什么作用?

答: 原型:每一个对象都与另一个对象相关联,那个关联的对象就称为原型。
例如:函数Person有一个属性prototype,指向一个对象,对于普通函数来说没多意义,对于构造函数就有作用了,当使用new操作符时,会把Person.prototype(原型对象)赋值给实例的__proto__(原型实例)属性。

JS有一个原型查找机制,把原来定义在实例上的方法,放到原型对象上去,通过构造函数的new操作,会把原型对象赋值给实例的__proto__属性,那么当使用返回的实例去调用某一个方法的时候,如果实例本身上没有,就去自动去实例的__proto__上去查找,这样达到方法的复用,减少内存开销。

原型链:每一个对象,都有一个原型对象与之关联,这个原型对象它也是一个普通对象,这个普通对象也有自己的原型对象,这样层层递进,就形成了一个链条,这个链条就是原型链。通过原型链可以实现JS的继承,把父类的原型对象赋值给子类的原型,这样子类实例就可以访问父类原型上的方法了。

1 Person.prototype.constructor == Person2 3 Person.prototype.__proto__.constructor  == Object 4 5 Person.prototype.__proto__ == Object.prototype6          7 Object.prototype.__proto__ == null
复制代码

2、如何实现继承(ES5/ES6)?

10 function Anima(name){ //父类
11                  this.name = name;
12              }
13
14              Anima.prototype.sayName = function(){
15                    console.log(this.name)
16              }
17
18              function Person(name,age){ //子类
19                  Anima.call(this,name);
20                  this.age = age;
21              }
22
23              Person.prototype = Object.create(Anima.prototype, {
24                constructor: {
25                  value: Person,
26                  enumerable: false
27                }
28              })
29
30
31            var p = new Persion("haha",12);
32
            Extends //ES6
          
12345678910111213141516171819202122232425
复制代码

点击获取 web前端全栈架构视频课程

3.什么是作用域以及作用域链?

答:作用域是指程序源代码中定义变量的区域,限定一个变量可访问的范围的,作用域的本质是对象。在JS采用的词法作用域,在书写代码的时候(定义函数的时候),作用域就已经确定好了。 在ES6环境下,包含3个作用域,全局globel,函数作用域,快级作用域( {} ) (eval)

作用域链:由多级作用域对象,逐级引用的链式结构。本质为执行上下文的scope属性,存储所有的变量,包括局部与全局,控制变量的使用顺序。

var b = 10; //1:输出?2:改造代码输出10或20
(function b(){
    b = 20;
    console.log(b); 
})();

var b = 10;
(function b(b) { 在这个函数b是一个常量,在函数b内部是可以使用的,但是不能修改,如果加上use strict 严格模式就会报错。
 window.b = 20;
 console.log(b) //输出10
})(b)
1234567891011

var a = 10;
(function () {
    console.log(a)
    a = 5
    console.log(window.a)
    var a = 20;
    console.log(a)
})()
12345678
复制代码

4.什么是闭包,闭包的好处和坏处分别是?

答:当函数可以记住并访问外部作用域时,就产生了闭包,那个外部作用域就称为闭包。

形成的原因:外层函数的作用域对象无法释放。
作用:保护一个变量,重用一个变量。坏处:使用不当,会造成内存泄漏。

大白话来解释:函数A和函数B,当内部函数B引用了A 的局部变量时,函数A 称为闭包
原因是:JS是词法作用域,B的作用域链上有对A执行环境的引用(这个执行环境用函数来表示),A的执行环境AO就不会回收。

5、什么是this,this的常用方式有哪些?如何改变this的指向?

答:JS在运行过程中会产生执行上下文环境(context),context记录了包含函数在哪里被调用,作用域scope,this等信息。this就是context的其中一个属性,会在函数的执行过程中使用。This是在运行时候绑定的,它指代的上下文对象取决于函数调用的各种条件。
this提供了一种优雅的方式来隐式的传递一个对象的引用,所以在函数中使用this可以更加方便的复用函数。

1、全局使用 this === window 很少使用
2、函数当中 在全局调用这个fn() this === window
3、在方法当中使用 this === 调用当前这个函数的所在的对象啊
4、构造函数this执向的是 new 创建出来的实例对象啊
5、DOM事件处理函数中的this,指向当前的DOM节点
6、通过 bind,call,apply 操作符来显示的设置 this的指向
bind:绑定函数里面的this,返回新函数,
call,apply:绑定并执行这个函数,前者传参是“,”隔开,后者是数组
7、ES6的箭头函数 箭头函数没有自己的this,父作用域的this

var x = 3;
  var foo = {
     x: 2,
     baz: {
      x: 1,
      bar: function() {
         return this.x;
        }
      }
   }

  var go = foo.baz.bar;
  go()?
foo.baz.bar()?


//综合面试题
function Foo() {
    Foo.a = function() {
        console.log(1)
    }
    this.a = function() {
        console.log(2)
    }
}
Foo.prototype.a = function() {
    console.log(3)
}
Foo.a = function() {
    console.log(4)
}
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();

12345678910111213141516171819202122232425262728293031323334353637
复制代码

6.手写bind,call,apply函数

答案:以上都是Function原型上的方法。

Function.prototype.myCall = function(context,...args){//绑定并执行
                //执行函数
              var fn = this;
              context.fn = fn;
              context.fn(...args);
              delete context.fn
          }

          Function.prototype.myBind = function(context){//绑定返回新的执行函数
            //判断调用者是不是函数
            if(typeof this != 'function'){
                throw new Error("Error")
            }
            //截取传递的参数
            let args = [...arguments].slice(1);
            var _this = this;//保存一下当前的调用者  
            return function F() {
              return _this.apply(context,args.concat([...arguments]))
            }
          } 
1234567891011121314151617181920
复制代码

7.什么是深、浅拷贝,请写出代码

答:浅拷贝,就是复制一个对象,当对象的属性值没有引用类型的时候。
Object.assign 迭代(for…in for…of object.enteries) 扩展运算符等。

反之如果对象中还有引用类型,连着引用类型一并拷贝称为深拷贝。
JSON对象的方法(会忽略到值为函数和null的属性),递归。

function deepCopy(obj){
              //判断对象的类型
            var newObj =  Array.isArray(obj)?[]:{};
            if(obj && typeof obj == "object"){
                  //迭代
                  for(var key in obj){
                        if(typeof obj[key] == 'obj'){
                          newObj[key] = deepCopy(obj[key])
                        }else{
                          newObj[key]  = obj[key]
                        }     
                  }
            }
            return newObj
    }
123456789101112131415
复制代码

8、什么是Ajax,如何封装一个Ajax?Get请求与 Post请求的区别?

答:Ajax的全称是异步的js与xml技术,通过它与后台服务器进行数据交换,可以使网页实现异步更新,言外之意是在不重新加载整个页面的情况下,对网页进行局部更新。

     1.nex xmlhttprequset对象
     2.open(method,url,false)
     3.绑定redaystatechange事件
     4.调用send方法,如果是post请求,可以传递参数
复制代码

前端的请求方式除了常用Get和Post,还有update,delete,put等(restful api设计)
GET 请求可被缓存,保留在浏览器历史记录中 ,请求的参数是直接跟在URL上,因此不应传递敏感数据。
GET 请求有长度限制(2048字符),IE和Safari浏览器限制2k;Opera限制4k;Firefox,Chrome限制8k 。
GET 请求通常只应当用于从后台获取数据。

POST 请求不会被缓存,不会保留在浏览器历史记录中
POST 请求对数据长度没有要求。
POST 请求通常用于往后台提交数据。

9.说一些ES6、ES7新特性。

答: let,count快级作用域;解构,从对象和数组中提取值;箭头函数;字符串模版;扩展运算符…;对象的简写;module;promise(async);class;对原生对象的扩展(新增加了很多方法) ;for-of (Object.keys,values,entries等);Symbal();
不常用的proxy,reflect,generate函数

10、什么是Promise对象,如何使用?

答:Promise是解决异步回调的ES语法的标准,通常用来解决异步嵌套和多异步同时完成回调等问题。
Promise是一个构造函数,相当于一个容器,把异步代码包裹在里面,promise有三个状态(pending(进行中)、fulfilled(已成功)和rejected(已失败))初始化为pending,当异步请求成功后调用resolve函数,状态从pending—>fulfilled,失败的时候调用reject,状态从pending—>rejected。状态不可逆。
缺点:书写麻烦,不能实现异步代码,同步执行的需求(配合async函数使用即可)

11、什么是跨域,解决跨域常用的方式有哪些?

答:跨域是浏览器端行为,根据同源策略,当请求的协议、域名、端口只有一个不同,就会跨域,跨域是浏览器为了安全存在的机制,浏览器会把跨域请求的数据去掉,同行报错。
在实际开发难免会出现跨域的情况,解决方案通常有
1.JSONP技术,利用了script的src属性没有跨域限制
2.CORS,当在相应头信息中添加access-control-allow-origain属性,浏览器读取到就会允许返回数据。后台配置,或者下一个浏览器插件即可。
3.后台代理(Node)
4.Iframe域的提升(很少)

12、什么是函数的防抖、节流,并手写基本代码

答:防抖和节流都是为了提升运行效率,减少函数的执行次数。
防抖:把多次函数执行,合并成一次执行

 function debounce(fn,delay){ //fn真正执行的函数,delay间隔时间
                var timer = null;
                 return function(){
                    var args = arguments;
                    var that = this;
                    if(timer) clearTimeout(timer)
                    timer = setTimeout(function(){
                        fn.apply(that,[...arguments])
                    },delay)
                 }

       }
123456789101112
复制代码

节流 :规定一个单位时间,在单位时间内触发一个事件回调,触发时超过间隔时间则执行,否则不执行。

function throttle(fn,gapTime){
          let _lastTime = null;
          return function(){
            var that = this;
            var args = arguments;
            let _nowTime = + new Date();
            if(_nowTime-_lastTime > gapTime || !_lastTime){
               fn.apply(that,args);
               _lastTime = _nowTime;
            }
          }
     }
123456789101112
复制代码

13、什么事件循环(Event Loop)?

答:js是非阻塞单线程语言,js在执行过程中会产生执行环境,执行环境会被顺序的加入到执行栈,当遇到异步任务,会添加到task队列当中,执行同步栈,当同步栈执行完以后,event loop 就会从异步task队列当中提取要执行的代码放到执行栈中。这个一个过程称为事件循环。所以JS的异步还是同步过程。

 console.log('script start')

          setTimeout(function() {
            console.log('setTimeout')
          }, 0)

          new Promise(resolve => {
            console.log('Promise')
            resolve()
          })
            .then(function() {
              console.log('promise1')
            })
            .then(function() {
              console.log('promise2')
            })

          console.log('script end')
          //输出结果?
12345678910111213141516171819
复制代码

15、前端安全你有了解吗?什么是XSS攻击和CSRF 跨站请求伪造?怎么预防?

答:前端的安全问题,在工作过程中会注意这些问题。
1、XSS 代码注入问题,通常不信任用户的输入,转义输入输出内容()encodeURIComponent),括号 尖括号等。利用用户对站点的信任

   2、CSRF 跨站请求伪造 
            是一种挟制用户在当前已登录的web应用中执行非本意的攻击
            大白话:利用用户登录态发起恶意请求
            网站对用户的信任
1234
复制代码

添加验证码(体验稍微差一些),不让第三方访问cookie 对cookie设置 samesite, 请求验证 加 token ,

3、密码问题:对密码进行加密(MD5等)

16.如何处理精度丢失问题?

答:产生的原因:JS使用Number类型表示一个数字,使用64位。
在这里插入图片描述

计算机先把10进制转位2进制,0.1和0.2转成二进制的时候还会无限循环,由于有51位位数的限制,多余的会被裁掉,所以在进制之间转换就会丢掉精度。
通常可以把小数乘以倍数转成正整数,计算完后再转回去。
parseFloat((0.1 + 0.2).toFixed(10))

16.前端常用请求数据的方式有哪些,并说出各自的区别。

答:前端进行网络请求常用的form,Ajax,fetch
常用的库文件Jquery或者axios等。

  Form提交数据页面会刷新,使用体验不佳。
  Ajax是一个综合技术,可以实现异步更新页面内容。使用起来方便,通常使用JQ的封装,会额外的JS加载代价。
  Fetch是新的API,比较底层。目前浏览器兼容性问题多一些,比如老版本不会携带cookie,不能直接传递JS对象作为参数,不支持JSONP等
123
复制代码

17.如何检测一个对象一定是数组?

答:1、Object.prototype.toString.call(),前提是toString方式没有被重写。
2、instanceof 内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。
[] instanceof Object; // true
3、Array.isArray() ES5新增的方法,兼容性稍微差一些。

18.JS加载会阻塞页面渲染吗?会的话该怎么解决呢?

答:会
defer:并行下载,在页面解析完后执行,会按照script的顺序执行。(常用)
async:异步下载代码,下载完毕后离职执行代码,不会按照页面的script顺序。

20.常见前端性能优化有哪些?
答:
1:加载优化

     CDN,文件压缩混淆合并,按需加载(异步组件),前端缓存,浏览器缓存(cache-control,exprices) 
     
     DNS预先解析,服务器渲染 nuxt,next(SEO),图片懒加载
复制代码

2:脚本优化

     减少合并DOM操作(createDocumentFragment),CSS3代替js动画,缓存变量,用requestAnimationFrame代理setTimeout,减少reflow与repain,事件代理,开启GPU渲染tranfrom:translateX(0)
1
复制代码

3:图片优化

     雪碧图(减少请求),使用SVG和iconfont(字体图片)代替图片,图片压缩
1
复制代码

更多前端面试相关和性能优化前端自动化测试视频教程请私信或点击获取。

文章分类
前端
文章标签