变量提升
JavaScript代码在执行时会经历3个主要的步骤(比起那些编译过程只有三个步骤的语言的编译器,JavaScript引擎要复杂得多。例如,在语法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。)
分析一下代码:
console.log(a);
// ƒ a() {
// console.log(111)
// }
function a() {
console.log(111)
}
var a = 1
console.log(a)
// 1
console.log(b)
// undefined
var b = function() {
console.log(111)
}
var b = 2
// 未捕获异常
console.log(a);
// Uncaught ReferenceError: a is not defined
var b = a
// Uncaught ReferenceError: a is not defined
console.log(b)
变量提升的优先级
第一阶段:对所有函数声明进行提升(忽略表达式和箭头函数),引用类型的赋值(函数的声明,函数的提升代表着可执行,因为提升之后保持着引用)分为三步:
开辟堆空间
存储内容
将地址赋值给变量
- 第二阶段:对所有的变量进行提升,全部赋值为 undefined(如果已经存在,不赋值为undefined)。然后依次顺序执行代码。
PS:使用 let 和 const 时,我们不能在声明之前使用变量,这叫做暂时性死区。
var、let、const
var 存在变量提升,而 let、const 则不会。
var 声明的变量会挂载到 window 上,而其他两者不会。
let 和 const 的作用基本一致,后者声明的变量不能再次赋值(注意:但是能改变值)
Proxy
Proxy 代理
代理 proxy 是 ES6 新提出的。代理(proxy)使我们通过代理控制对另一个对象的访问。proxy 和 getter 以及 setter 的区别是,getter和 setter 仅仅控制的是单个对象属性,而 proxy 代理的是对象交互的通用处理,包括对象的方法。
用法
- target:要进行代理的对象。
- handler:是一个特定的对象,定义了特定行为时触发的函数。
var proxy = new Proxy(target, handler);
get 和 set 的参数
const obj = {name:'xiaolu'}
const representtive = new Proxy(obj, {
get: (target, key) =>{
return key in target ? target[key]:"不存在该值“
},
set: (target, key, value)=>{
target[key] = value;
}
})
ES6/7 的异步编程
主要是解决回调地狱问题
面试官:说说 Generator 是如何使用的?以及各个阶段的状态是如何变化的?
Generator
function* WeaponGenerator(){
yield "1";
yield "2";
yield "3";
}
let weapon = WeaponGenerator();
console.log(weapon.next());
console.log(weapon.next());
console.log(weapon.next());
使用迭代器控制生成器。
- 通过调用生成器返回一个迭代器对象,用来控制生成器的执行。
- 调用迭代器的 next 方法向生成器请求一个值。
- 请求的结果返回一个对象,对象中包含一个value值和 done布尔值,告诉我们生成器是否还会生成值。
Promise
面试官:说说 Promise 的原理?你是如何理解 Promise 的?
promise是为了解决回调地狱的问题。Promise 是异步编程的一种解决方案。
promise的状态
- pending
- resolved
- rejected
then()和catch()
- 如果异步操作抛出错误,状态就会变为Rejected,就会调用catch方法指定的回调函数,处理这个错误。
- then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。
- 在resolve()后面抛出的错误会被忽略(如果前面已经是执行了resolve,那么后面throw 出来的error不会被catch到)
- catch方法返回的还是一个Promise对象,因此后面还可以接着调用then方法。
在异步函数中抛出的错误不会被catch捕获到(例如使用了setTimeout之类的,抛出来的错误不会被catch到) - 有多个catch连写,如果在catch中继续throw出异常,那么后面的catch就会一直执行,如果不throw异常,则不会执行(catch其实是then的语法糖)
async 和 await
ES7 的 Async/Await 也是基于 promise 来实现的,可以理解成 async 函数会隐式地返回一个 Promise , await 后面的执行代码放到 then 方法中
模块化
模块化解决了命名冲突问题,可以提高代码的复用率,提高代码的可维护性。
函数
立即执行函数
// module.js文件
(function(window) {
let name = 'xiaolu'
// 暴露的接口来访问数据
function a() {
console.log(`name:${name}`)
}
//暴露接口
window.myModule = { a }
})(window)
Static
static关键字多用于静态方法和静态属性。
不同于普通在类中定义的会被实例继承的方法。在方法前加上static关键字。就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
静态属性
静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。
// 老写法
class Foo {
// ...
}
Foo.prop = 1;
// 新写法
class Foo {
static prop = 1;
}
exports和module.exports的区别
- module.exports 默认值为{}
- exports 是 module.exports 的引用
- exports 默认指向 module.exports 的内存空间
- require() 返回的是 module.exports 而不是 exports
- 若对 exports 重新赋值,则断开了 exports 对 module.exports 的指向
commonjs和esmodule的区别
- commonJs是被加载的时候运行,esModule是编译的时候运行
- commonJs输出的是值的浅拷贝,esModule输出值的引用
- commentJs具有缓存。在第一次被加载时,会完整运行整个文件并输出一个对象,拷贝(浅拷贝)在内存中。下次加载文件时,直接从内存中取值
import 和 require的区别
esmodule加载commonjs模块
module.exports 等同于 export default 可以用 import 引入
commonjs 模块加载 es6 模块
CommonJS 模块加载 ES6 模块,不能使用require命令,而要使用import()函数。
参考链接
- https://blog.csdn.net/weixin_34406796/article/details/91374453