各大厂前端面试题合集 — javascript(二)

149 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

ES6中的块级作用域

1、为什么需要块级作用域

ES5只有全局作用域和函数作用域,没有块级作用域,会带来以下问题:

1) 变量提升导致内层变量可能会覆盖外层变量

var i = 5;
function func() {     
    console.log(i);     
    if (true) {         
        var i = 6;     
    }
} 
func(); // undefined

2) 用来计数的循环变量泄露为全局变量

for (var i = 0; i < 10; i++) {
    console.log(i);  // 0 1 2 3 4 5 6 7 8 9 
}   
console.log(i);  // 10

2、ES6的块级作用域

ES6允许块级作用域的任意嵌套。外层作用域无法读取内层作用域的变量。

{{{{     
    {let i = 6;}     
    console.log(i);  // 报错 
}}}}

内层作用域可以定义外层作用域的同名变量。

{{{{     
    let i = 5;     
    {let i = 6;} 
}}}}

块级作用域的出现使得立即执行函数不再需要。

立即执行函数:

(function() {     
    var i = 5; 
})();

块级作用域:

{     
    let i = 5; 
}

3、块级作用域与函数声明

ES5规定,函数只能在顶层作用域和函数作用域之外声明,不能在块级作用域中声明。

if (true) {     
    function func() {} 
} 
try {     
    function func() {} 
} catch { 
}

上面两种函数声明在ES5中都是非法的,但是浏览器没有遵守这一规定,还是支持在块级作用域中声明函数,因此以上两种情况实际都能运行,不会报错;但是,在严格模式下,还是会报错。

'use strict': 
if (true) {     
    function func() {} // 报错 
}

ES6引入了块级作用域,明确允许在块级作用域中声明函数。

'use strict': 
if (true) {     
    function func() {} // 不报错 
}

ES6还规定,在块级作用域中,函数声明的行为类似于let,在块级作用域之外不可引用。

ES6中,函数声明会提升到函数作用域的头部。

function func() {     
    console.log('out'); 
} 

(function() {     
    if (false) {         
        function func() {             
            console.log(‘in’);         
        }     
    }     
    func(); 
})();

在ES5中输出:in

在ES6中输出:out

但是,以上代码在Chrome中运行会报错,实际运行的是以下代码:

function func() {     
    console.log(‘out’); 
} 

(function() {     
    var func = undefined;     
        if (false) {         
            function func() {             
                console.log(‘in’);         
            }    
        }     
        func(); 
})();

考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数,若确实需要,也应该写成函数表达式,而不是函数声明语句。

{     
    let func= function func() {         
        console.log(‘in’);     
    }; 
};

注意:ES6的块级作用域允许声明函数只在使用大括号的情况下成立,如果未使用大括号,会报错。

'use strict': 
if (true) {     
    function func1() {} // 不报错 
} 
if (true)     
    function func2() {} // 报错

ES6的let和const命令都涉及块级作用域

🌟ES6了解什么,和js的区别

1、作用域

     在之前的js中,只有函数作用域和全局作用域,指的是变量只能在函数中或者全局中,块级作用域是不存在的。

     例:在for循环或while等其他循环中,var 定义一个变量,在外部获取是没问题的,在ES6的的语法中 ,let 一个变量,在大括号外是获取不到的。const 是定义一个常量,定义之后是无法更改的

2、模板字符串 ${变量名}

       为动态生成内容,提前定义的字符串模板

var name = "han";

var course = "你好";


//普通写法
console.log("hello," + name + "course is" + course);

//ES6写法 (字符串内回车表示换行)
console.log(`hello,${name},course is ${course}`);

3、箭头函数

      简化一切回调函数/匿名函数自调

4、解构

      当在一个大的对象/数组中选取部分属性/元素放入变量中使用时,解构可以将一个对象/数组,拆散到多个变量中

      如何:数组解构:下标对下标

                对象解构:属性对属性

                参数解构:属性对属性(此时参数位置可以不固定了)

5、散播

      将一个数组打散后分别传入到函数中

      ...arr

6、剩余参数

      除了前面几个明确的参数值外,剩余参数全部收集到数组中保存

function fun(参数1,参数2,参数n,...数组名){};

7、参数增强

      函数定义时,可提前为部分参数变量指定默认值

function fun(参数1,参数2,...,参数n = 默认值){};

8、for of循环

      依次遍历数组/类数组对象中每个元素的值

for(var key of arr/obj){
    //key会依次取出arr/obj中的每个商品
}

9、class:简化了对象

//封装
class Student{
  constructor(sname,sage){
      this.sname = sname;
      this.sage = sage;
  }
  intr(){}
}

//继承
class Father{
    //函数体  
}
class child extends Father{
  constructor(参数列表){
       super(参数列表);//指向父类型构造函数,且自动替换this
  }
}

10、promise

      代替异步调用中的回调函数,解决回调地狱的问题,用.then调用

//定义函数时
function prev(){
    var promise = new Promise(function(open,errHandler){
        //prev的逻辑
        //当prev执行完
        //如果执行正确
        open();
        //否则
        errHandler();
    });
    return promise;
}

//调用时
prev().then(next).catch(function(msg){
    //错误代码
    console.error(msg);
});

🌟cookie、localStorage、sessionStorage的区别

共同点:都是保存在浏览器端,并且是同源的

Cookie:

cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下,存储的大小很小只有4K左右。 (key:可以在浏览器和服务器端来回传递,存储容量小,只有大约4K左右)

sessionStorage:

仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持,localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。(key:本身就是一个回话过程,关闭浏览器后消失,session为一个回话,当页面不同即使是同一页面打开两次,也被视为同一次回话)

localStorage:

localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。(key:同源窗口都会共享,并且不会失效,不管窗口或者浏览器关闭与否都会始终生效)

区别

Cookie和session都可用来存储用户信息,cookie存放于客户端,session存放于服务器端,因为cookie存放于客户端有可能被窃取,

cookie一般用来存放不敏感的信息,比如用户设置的网站主题,

敏感的信息用session存储,比如用户的登陆信息,session可以存放于文件,数据库,内存中都可以,

cookie可以服务器端响应的时候设置,也可以客户端通过JS设置cookie会在请求时在http首部发送给客户端,cookie一般在客户端有大小限制,一般为4K,

1、生命周期:

Cookie:可设置失效时间,否则默认为关闭浏览器后失效

Localstorage:除非被手动清除,否则永久保存

Sessionstorage:仅在当前网页会话下有效,关闭页面或浏览器后就会被清除

2、存放数据:

Cookie:4k左右

Localstorage和sessionstorage:可以保存5M的信息

3、http请求:

Cookie:每次都会携带在http头中,如果使用cookie保存过多数据会带来性能问题

其他两个:仅在客户端即浏览器中保存,不参与和服务器的通信

4、易用性:

Cookie:需要程序员自己封装,原生的cookie接口不友好

其他两个:即可采用原生接口,亦可再次封装

5、应用场景:

从安全性来说,因为每次http请求都会携带cookie信息,这样子浪费了带宽,所以cookie应该尽可能的少用,此外cookie还需要指定作用域,不可以跨域调用,限制很多,但是用户识别用户登陆来说,cookie还是比storage好用,其他情况下可以用storage,localstorage可以用来在页面传递参数,sessionstorage可以用来保存一些临时的数据,防止用户刷新页面后丢失了一些参数

🌟箭头函数和普通函数的区别

1、语法更加简洁、清晰

2、箭头函数不会创建自己的this(重要!!深入理解!!)

     箭头函数不会创建自己的this,所以它没有自己的this,它只会从自己的作用域链的上一层继承this。即箭头函数中的this的指向在它被定义的时候就已经确定了,之后永远不会改变

var id = 'GLOBAL'var obj = {      
    id: 'OBJ',      
    a: function(){         
        console.log(this.id);      
    },      
    b: () => {          
        console.log(this.id);      
    }  
};  

obj.a(); // 'OBJ'  
obj.b(); // 'GLOBAL'

上面这个例子,对象obj的方法a使用普通函数定义的,普通函数作为对象的方法调用时,this指向它所属的对象。所以,this.id就是obj.id,所以输出'OBJ'。 但是方法b是使用箭头函数定义的,箭头函数中的this实际是继承的它定义时所处的全局执行环境中的this,所以指向Window对象,所以输出'GLOBAL'。(这里要注意,定义对象的大括号{}是无法形成一个单独的执行环境的,它依旧是处于全局执行环境中!!)

3、箭头函数继承而来的this指向永远不变(重要!!深入理解!!)

4、call() / apply() / bind() 无法改变箭头函数中this的指向

5、箭头函数不能作为构造函数使用

    构造函数new做了什么:

  • js内部首先会生成一个对象;

  • 再把函数中的this指向该对象

  • 然后执行构造函数中的语句

  • 最终返回该对象实例

    但是!!因为箭头函数没有自己的this,它的this是继承了外层执行环境的this,且this指向永远不会随在哪里调用、被谁调用而改变,所以箭头函数不能作为构造函数使用,或者说构造函数不能定义成箭头函数,否则new调用时会报错!

6、箭头函数没有自己的arguments

    在箭头函数中访问arguments实际上获得的是外层局部(函数)执行环境中的值。

    可以在箭头函数中使用rest参数代替arguments对象,来访问箭头函数的参数列表!!

7、箭头函数没有原型prototype

let sayHi = () => { 

    console.log('Hello World !') 

}; 

console.log(sayHi.prototype); // undefined

8、箭头函数不能用作Generator函数,不能使用yeild关键字