一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
11、js为什么要进行变量提升,它导致了什么问题?
变量提升的表现是,无论在函数中何处位置声明的变量,好像都被提升到了函数的首部,可以在变量声明前访问到而不会报错。
首先要知道,调用js时,会有两步操作,即解析阶段和执行阶段。
1、在解析阶段,会对函数进行预编译。解析的时候回先创建一个全局执行上下文环境,先把代码中即将执行的变量、函数声明都拿出来,变量先赋值为undefined,函数先声明好可使用。在一个函数执行之前,也会创建也该函数执行上下文环境,跟全局执行上下文类似,不过函数执行上下文会多出this、arguments和函数的参数。
2、在执行阶段,按照代码的顺序依次执行
举个例子也就是:比如你要出门,出门之前要洗漱,穿衣服、打扮等等,这个阶段就是解析阶段。然后你开始打车出行,可能是公交车, 可能地铁,可能出租车等等,这就是执行阶段。也就是解析阶段我是提前准备的,当我真正要去做某-件事的时候就不需要每次都要(1.穿衣打扮在打公交车2.穿衣打扮在打出租车),而是(我已经穿衣打扮好了,1.打公交车2.打出租车),所以就不需要重复的穿衣打扮了。
那为什么会进行变量提升呢?
1、可以提高性能,让函数可以在执行时预先为变量分配栈空间
2、提高js代码的容错性,使一些不规范的代码也可以正常执行
12、es6模块和commonjs模块有什么不同?
commonjs模块输出的是一个值得拷贝,es6模块输出的是值得引用。
commonjs模块是运行时加载,es6模块是编译时输出接口
13、let、const、var的区别?
1、块级作用域:块作用域由{}包括,let和const具有块级作用域,var不存在块级作用域。块级作用域解决了es5中的两个问题:
1、内层变量可能覆盖外层变量
2、用来计数的循环变量泄露为全局变量
2、变量提升:var存在变量提升,let和const不存在变量提升,即变量只能在声明之后使用,否则会报错
3、给全局添加属性:浏览器的全局对象是window,node的全局对象是global。var声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是let和const不会
4、重复声明:var声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的变量。const和let不允许重复声明变量。
5、暂时性死区:在使用let、const命令声明变量之前,该变量都是不可用的,在这语法上,称为暂时性死区。使用var声明的变量不存在暂时性死区。
6、初始值设置:在声明变量的时候,var和let可以不用设置初始值,而const声明变量必须设置初始值。
7、指针指向:let和const都是es6新增的用于创建变量的语法,let、var创建的变量可以更改指针指向(可以重新赋值),但const声明的变量是不允许改变指针的指向。
14、const对象的属性可以修改吗?
const保证的并不是变量的值不能改动,而是变量指向的那个内存地址不能改动。
对于基本数据类型的数据(number、string、boolean),修改不了
但是对于引用数据类型的数据(主要是对象和数组),可以改变内部的属性值
15、如果new一个箭头函数会怎么样?
箭头函数是es6提出来的,没有prototype,this指向,arguments参数,所以不能new一个箭头函数
new操作符的实现步骤如下:
1、创建一个对象
2、将构造函数的作用域赋给新对象(也就是将对象的__proto__属性指向构造函数的prototype属性)
3、指向构造函数中的代码,构造函数中的this指向该对象(也就是为这个对象添加属性和方法)
4、返回新的对象。
所以,上面的第二、三步,箭头函数都是没有办法执行的。
16、箭头函数与普通函数的区别?
1、箭头函数没有自己的this
2、箭头函数继承来的this指向永远不会改变
3、call()、apply()、bind()等方法不能改变箭头函数中的this指向
4、箭头函数不能作为构造函数使用
5、箭头函数没有自己的arguments
6、箭头函数没有prototype
7、箭头函数不能用作generator函数,不能使用yeild关键字,普通函数可以
17、箭头函数的this指向哪里?
箭头函数没有属于自己的this,它所谓的this是捕获其所在上下文的this值,作为自己的this值,并且由于没有属于自己的this,所以是不会被new调用的,这个所谓的this也不会被改变。
18、对于原型、原型链的理解?
先明确两个概念:
1、prototype:任意函数对象的属性(包含构造函数)
2、__proto__:任意对象都具备的属性
基于上面的两个概念,我这边举个例子,比如:const person = new Person(),在通过person.slice()执行slice方法,会执行以下步骤:
1、先检索自身有没有slice方法
2、如果自身没有,person实例会通过person.__proto__去原型对象上找是否有slice方法
3、如果没有找到,会通过person.__proto__.__proto__去父级找,如果父级没有,会一直递归到上一级寻找,如果最根级是object,发现object也没有,就会返回一个null,说明没有找到。
js分为函数对象和普通对象,每个对象都有__proto__属性,但是只有函数对象才有prototype属性
Object、Function都是js内置的函数, 类似的还有我们常用到的Array、RegExp、Date、Boolean、Number、String
属性__proto__是一个对象,它有两个属性,constructor和__proto__;
原型对象prototype有一个默认的constructor属性,用于记录实例是由哪个构造函数创建;
1. Person.prototype.constructor == Person // **准则1:原型对象(即Person.prototype)的constructor指向构造函数本身**
2. person01.__proto__ == Person.prototype // **准则2:实例(即person01)的__proto__和原型对象指向同一个地方**
19、对于promise的理解?
promise核心特性:解决回调地狱问题和-次决议
首先我们来谈下解决异步的方式有: callBack (回调)和promise,这俩种都可以。其实promise是基于callBack进化的产物,promise具备 callBack不具备的能力,比如:
1. promise- -般用class实现, return返回的值是- -个新的promise实例, 这样就可以实现链式调用,解决回调地狱问题
2.以前我们用callBack来解决异步,会有这么-一种情况,打个比方:我们使用lodash库,用到里面map的方法,结果某-天lodash库作者不小心在遍历的时候手误多写 了俩个对外的callBack ()的的方法,我们在外面使用的时候,每次循环就会触发俩次,可是如果我们是在这个里面执行接口数据请求|埋点数据统计,这些操作会影响到业务正常使用,控制权不可控。基于callBack这种方式,promise提出一 次决议的概念, 也就是promise源码里面一 般用class来实现promise, 类里面有pendding, resolve, reject这三种状态, 规范只能从pendding转成resolve/reject状态, 如果判断是非pendding状态,直接就return掉了 ,所以我们在promise里面就算一个地方同时reslove-干次,最终在外面.then接收结果的时候, 只会收到第一次reslove返回的数据。
还有一-点就是promise源码里面对于每个外部传入进来的函数,源码里面都用了try catch包裹着,所以如果外部的函数如果就算执行的时候执行栈挂掉了,也可以通过try catch里面的catch捕获,最后通过以reject往外抛出来,使得程序可以正常运行。
20、Promise.all和promise.race的区别和使用场景?
●Promise.all : Promise. all可以将多个Promise 实例包装成- 一个新的Promise实例。
Promise.all获得的成功结果的数组里面的数据顺序和Promise.al接收到的数组顺序是一致的, 这样当遇到发送多个请求并根据请求顺序获取和使用数据的场景,就可以使用Promiseall来解决。
●Promise.race
Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p31])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
经典运用的场景:当要做-件事,超过多长时间就不做了(也就是处理超时业务逻辑的时候) .可以用这个方法来解决:
Promise. race([promise1, timeOutPromise(5000)]). then(res=>{})