前端面试题——JS部分——持续总结

JS

1、JS中this指向问题?

彻底理解JS中的this指向
JS中的this原理
首先记住几点:

1)this永远指向一个对象
2)this的指向完全取决于函数调用的位置,只有函数执行后才能确定this到底指向谁。
3)this指的是函数运行时所在的环境

下面总结几种常见情况:
1)如果一个函数中有this,但他没有被上一级对象调用,那么this此时指向window。

function a() {
    var user = "好好学习,天天向上";
    console.log(this.user); //undefined
    console.log(this); //Window
}
a();//相当于 window.a();

var b = function {
    var user = "好好学习,天天向上";
    console.log(this.user); //undefined
    console.log(this); //Window
}
window.b();//相当于 b();

2)如果一个函数中有this,他被上一级对象调用,那么this此时指向的就是上一级对象

var o = {
    user:"好好学习,天天向上",
    fn:function(){
        console.log(this.user);  //好好学习,天天向上
    }
}
o.fn();//相当于window.o.fn()

this指向的是调用他的上一级对象o。

3)如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

var o = {
    a:10,
    b:{
        fn:function(){
            console.log(this.a); //undefined
        }
    }
}
o.b.fn();

尽管对象b没有属性a,但是this还是指向调用它的上一级对象b,并不是指向o。
同时不要忘记前面几句话:this的指向取决于函数调用的位置
对比下面的例子:

var o = {
    a:10,
    b:{
        a:12,
        fn:function(){
            console.log(this.a); //undefined
            console.log(this); //window
        }
    }
}
var j = o.b.fn;
j();//函数在此处执行

这里的this指向window。 this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子中虽然函数fn是被对象b所引用,但是在将 fn 赋值给变量j的时候并没有执行所以最终指向的是window。

4)构造函数中的new改变this指向

function Fn(){
    this.user = "好好学习,天天向上";
}
var a = new Fn();
console.log(a.user); //好好学习,天天向上

这里this指向的是对象a。这里之所以对象a可以点出函数Fn里面的user是因为new关键字可以改变this的指向,将这个this指向对象a,为什么我说a是对象,因为用了new关键字就是创建一个对象实例,我们这里用变量a创建了一个Fn的实例(相当于复制了一份Fn到对象a里面),此时仅仅只是创建,并没有执行,而调用这个函数Fn的是对象a,那么this指向的自然是对象a,那么为什么对象a中会有user,因为你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份。

2、哪些方法可以改变this指向

call、apply、bind方法总结
1)call()
call(): 第一个参数表示要把this指向的新目标,第二个之后的参数其实相当于传参,参数以逗号隔开(性能较apply略好)
用法: a.call(b,1,2); 表示要把a函数的this指向修改为b的this指向,并且运行a函数,传进去的参数是(1,2)

var a = {
    user:"好好学习,天天向上",
    fn:function(b,c){
        console.log(this.user); //好好学习,天天向上
        console.log(b+c);//3
    }
}
var b = a.fn;//a.fn();立即执行可以
b.call(a,1,2); //b();不可以

2)apply()
 apply(): 第一个参数同上,第二个参数接受一个数组,里面也是传参,只是以数组的方式,相当于arguments
 用法: a.apply(b,[1,2]); 表示要把a函数的this指向修改为b的this指向,并且运行a函数,传进去的参数是(1,2)注意 :即使只有一个参数的话,也要是数组的形式。

var a = {
    user:"好好学习,天天向上",
    fn:function(e,ee){
        console.log(this.user); //好好学习,天天向上
        console.log(e+ee); //11
    }
}
var b = a.fn;
b.apply(a,[10,1]);

3)bind()
bind和前两者不同,bind返回的是一个修改后的函数。同样bind也可以有多个参数,并且参数可以执行的时候再次添加,但是要注意的是,参数是按照形参的顺序进行的。

var a = {
    user:"好好学习,天天向上",
    fn:function(e,d,f){
        console.log(this.user); //好好学习,天天向上
        console.log(e,d,f); //10 1 2
    }
}
var b = a.fn;
var c = b.bind(a,10);//bind返回的是一个函数,下面是打印结果
console.log(c); //function() { [native code] }
c(1,2);//执行时可以继续添加参数1,2

总结:apply()、call()和bind()除了传递参数形式不同,调用方式也不同:call、apply都是立即调用。bind 方法不会立即执行,而是返回一个改变了上下文 this 后的函数,便于稍后调用。而原函数 中的 this 并没有被改变,依旧指向原来该指向的地方。

3、JS数据类型

1)JS的数据类型有6种:
undefined、null、number、boolean、string和object
分为两类:
原始类型(即基本数据类型)和对象类型(即引用数据类型);
2)基本数据类型:undefined、null、number、boolean、string;
3)引用数据类型:array、function、data;

4、null和undefined的区别

null 代表 “空值”,代表一个空对象。一个特殊的对象值
undefined:代表变量未赋值
1)定义变量但未分配值时,输出undefined

	var a;
    console.log(a);//undefined

2)变量可以赋值为 null,但不能赋值为undefined

  	var a = null;//正确
    console.log(a);//null
	var a = undefind;//错误
    console.log(a);//undefind is not defined

3)null的类型是object,undefined是undefined

console.log(typeof undefined);    //"undefined"  
console.log(typeof null);       //"object"  
console.log(null===undefined);    //false   "==="表示绝对相等,null和undefined类型是不一样的,所以输出“false”
console.log(null==undefined);    //true  因为两者都默认转换成了false

4)null和undefined转换成number数据类型
null 默认转成 0
undefined 默认转成 NaN

Number(undefined)  // NaN
Number(undefined + 10)  //NaN
Number(null)  // 0
Number(10 + null)  // 10

5、JS作用域和作用域链

深入理解作用域和作用域链
ES6之前只有全局作用域函数作用域,ES6新增了块级作用域
1)全局作用域: 页面一打开就会形成一个全局的作用域,在代码中任何地方都能访问到。一般有以下几种情况:

  • 最外层函数 和在最外层函数外面定义的变量拥有全局作用域
//11最外层函数外面定义的变量
var outVariable = "我是最外层变量"; //最外层变量
//22最外层函数
function outFun() { 
    var inVariable = "内层变量";
    function innerFun() { //内层函数
        console.log(inVariable);
    }
    innerFun();
}
console.log(outVariable); //我是最外层变量
outFun(); //内层变量
console.log(inVariable); //inVariable is not defined
innerFun(); //innerFun is not defined
  • 所有末定义直接赋值的变量自动声明为拥有全局作用域
function outFun2() {
    variable = "未定义直接赋值的变量";
    var inVariable2 = "内层变量2";
}
outFun2();//要先执行这个函数,否则根本不知道里面是啥
console.log(variable); //未定义直接赋值的变量
console.log(inVariable2); //inVariable2 is not defined
  • 所有window对象的属性拥有全局作用域

2)函数作用域:在固定的代码片段才能被访问,在函数内部声明的变量
作用域是分层的,内层作用域可以访问外层作用域的变量,反之则不行。 我们看个例子,用泡泡来比喻作用域可能好理解一点:
在这里插入图片描述
最后输出的结果为 2, 4, 12

  • 泡泡1是全局作用域,有标识符foo;
  • 泡泡2是作用域foo,有标识符a,bar,b;
  • 泡泡3是作用域bar,仅有标识符c。

3)块级作用域
块级作用域可通过新增命令letconst声明,所声明的变量在指定块的作用域外无法被访问

块级作用域在如下情况被创建:

  • 在一个函数内部
  • 在一个代码块(由一对花括号包裹)内部

let 声明的语法与 var 的语法一致。你基本上可以用 let 来代替 var 进行变量声明,但会将变量的作用域限制在当前代码块中。
循环中绑定块级作用域妙用:

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

上面代码中,变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。

如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。

作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
变量取值:到 创建 这个变量 的函数的作用域中取值

4)作用域链
一般情况下,变量取值到 创建 这个变量 的函数的作用域中取值。
但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。

6、let、const和var有什么区别?

1)var声明的变量会挂载在window上,而let和const声明的变量不会
2)var声明变量存在变量提升,let和const不存在变量提升,也就是只能在声明位置以后使用。
3)let和const声明形成块作用域
4)同一作用域下let和const不能声明同名变量,而var可以,后声明的同名变量会覆盖之前声明的变量。
5)在变量声明时,var 和 let 可以不用设置初始值。而const声明变量必须设置初始值。不能使用null占位;声明后不能再修改 ;如果声明的是复合类型数据,可以修改其属性
6)let创建的变量是可以更改指针指向(可以重新赋值)。但const声明的变量是不允许改变指针的指向。

7、JS中的new做了哪几件事?

1)在函数内部创建了一个空对象
2)通过this指向把函数的方法和属性给新创建的对象
3)返回这个创建的对象

8、跨域的相关知识?

跨域相关
解决跨域的几种方式
同源策略 :所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源。
跨域 :当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
JSONP 是一种无需考虑跨域问题即可传送 JSON 数据的方法。
JSONP 不使用 XMLHttpRequest 对象。
JSONP 使用

9、原型和原型链

原型和原型链
上面原文解释的很好。
下面只是提取主要的。
在这里插入图片描述

1)原型:
构造函数的.prototype属性指向原型对象;原型对象的constructor属性指向构造函数。

原型对象的作用:存放实例对象的公有属性和公有方法。
如果把所有的属性和方法都存放在构造函数中,那每创建一个实例,就要重复创建一次新的属性和方法。 这样很浪费内存,但如果把这些公有属性和方法放在原型对象里就会好很多。

2)原型链
如果某个对象查找属性,自己和原型对象上都没有,那就会继续往原型对象的原型对象上去找,这个例子里就是Object.prototype,这里就是查找的终点站了,在这里找不到,就没有更上一层了(null里面啥也没有),直接返回undefined。

可以看出,整个查找过程都是顺着__proto__属性,一步一步往上查找,形成了像链条一样的结构,这个结构,就是原型链。所以,原型链也叫作隐式原型链。
3)总结:

  • 构造函数是使用了new关键字的函数,用来创建对象,所有函数都是Function()的实例。
  • 原型对象是用来存放实例对象的公有属性和公有方法的一个公共对象,所有原型对象都是Object()的实例。
  • 原型链又叫隐式原型链,是由__proto__属性串联起来,原型链的尽头是Object.prototype

10、cookie、 sessionstorage localstorage的异同点?

三者对比
相同点:三者都是将服务器端数据保存到客户端(浏览器端),只是存放在本地的内存大小、生命周期有所不同。
不同点
1)存储大小不同:cookie数据大小不能超过4K,而sessionstorage、localstorage可以达到5M或更大。
2)数据有效期不同:
cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。;
sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;
localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;除非手动删除。API不支持设置过期时间。
3)作用域不同:sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。
4)数据与服务器间的交互方式:
cookie的数据会自动传递到服务器端,服务器端也可以写cookie到客户端;
localStorage和sessionStorage不会把数据自动传到服务器端,仅在本地存储。
拓展
1)session和cookie有什么区别 ?
session是存储服务器端,cookie是存储在客户端,所以session的安全性比cookie高。
获取session里的信息是通过存放在会话cookie里的session id获取的。而session是存放在服务器的内存中里,所以session里的数据不断增加会造成服务器的负担,所以会把很重要的信息存储在session中,而把一些次要东西存储在客户端的cookie里。
cookie中设置了httponly后js脚本将无法获取cookie信息,这样可以防范xss攻击

11、宏任务和微任务?

1)宏任务:当前调用栈中执行的代码,如:主代码块、定时器、事件触发等。
2)微任务:异步任务最开始执行的,它可以是同步任务,也可以是执行宏任务时产生的。微任务可以在实时性和效率性之间做一个有效平衡。当前宏任务执行完,在下一个宏任务执行之前需要执行的任务,如:promise.then process.nextTick、Async / Await、object.serve。
3)宏任务的事件放在callback 队列中,由事件触发线程维护;微任务的事件放在微任务队列中,由JS引擎线程维护。
4)运行机制
在执行栈中执行一个宏任务;
执行过程中若遇到微任务,将微任务添加到微任务队列中;
当宏任务执行完毕,立即执行微任务队列中的微任务;
当微任务队列中的任务执行完毕,检查渲染,GUI线程接管渲染;
当渲染完毕,JS线程接管,开启下一次事件循环,执行下一次宏任务。

12、BOM和DOM的区别?

1)BOM是浏览器对象模型,与浏览器关系密切;DOM是文档对象模型,这里的文档指的是网页,HTML文档。
2)BOM没有相关标准,DOM是W3C标准。
3)BOM的最根本对象是window,DOM的最根本对象是document(window.document)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值