1、JS的作用域和作用域链
- 作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,我们可以访问到外层环境的变量和函数。
- 作用域链的本质上是一个指向变量对象的指针列表。变量对象是一个包含了执行环境中所有变量和函数的对象。作用域链的前端始终都是当前执行上下文的变量对象。全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最后一个对象。
- 当我们查找一个变量时,如果当前执行环境中没有找到,我们可以沿着作用域链向后查找。
2、函数表达式和函数申明
函数声明存在变量提升,会被提升到作用域最前面。
函数表达式,不会被提升,仍然按照从上往下读取。
函数的4种类型:函数声明、箭头函数、函数表达式、函数对象
// 1️⃣:函数声明
async function fn() {
await f()
}
// 2️⃣:函数表达式
const fn = async function () {
await f()
};
// 3️⃣:箭头函数
const fn = async () => {
await f()
};
// 4️⃣:async函数定义在对象中
const obj = {
async fn() {
await f()
}
}
3、原型,原型链
参考阅读: 深入理解JavaScript5:强大的原型和原型链
当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是 Object.prototype 所以这就是我们新建的对象为什么能够使用 toString() 等方法的原因。
4、this指向及apply call bind 的区别
this
,函数执行的上下文,可以通过 apply , call , bind 改变 this
的指向。对于匿名函数或者直接调用的函数来说,this指向全局上下文(浏览 器为window,NodeJS为 global ),剩下的函数调用,那就是谁调用它, this
就指向谁。当然还有es6的箭头函数,箭头函数的指向取决于该箭头函 数声明的位置,在哪里声明, this 就指向哪里。
5、变量提升
推荐阅读:深入理解JavaScript系列:变量对象Variable Object
当执行 JS 代码时,会生成执行环境,只要代码不是写在函数中的,就是在全局执行环境中,函数中的代码会产生函数执行环境。在生成执行环境时,会有两个阶段。第一个阶段是创建的阶段,JS 解释器会找出需要提升的变量和函数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存入内存中,变量只声明并且赋值为 undefined ,所以在第二个阶段,也就是代码执行阶段,我们可以直接提前使用
6、JS的继承
原型链继承、借用构造函数继承、组合继承(组合原型链继承和借用构造函数继承)(常用)、原型式继承、寄生组合式继承(常用)
推荐阅读:JS继承的6种方式
7、new操作符具体干了什么呢
- 1、创建了一个空的js对象(即{})
- 2、设置原型,将对象的原型设置为函数的 prototype 对象
- 3、将空对象作为构造函数的上下文(改变this指向)
- 4、判断构造函数的返回值类型,如果是值类型,则返回新对象。如果是引用类型,就返回这个引用类型的对象
/* create函数要接受不定量的参数,第一个参数是构造函数(也就是new操作符的目标函数),
其余参数被构造函数使用。
new Create() 是一种js语法糖。我们可以用函数调用的方式模拟实现 */
function create(Con,...args){
//1、创建一个空的对象
let obj = {}; // let obj = Object.create({});
//2、将空对象的原型prototype指向构造函数的原型
Object.setPrototypeOf(obj,Con.prototype); // obj.__proto__ = Con.prototype
//3、改变构造函数的上下文(this),并将剩余的参数传入
let result = Con.apply(obj,args);
//4、在构造函数有返回值的情况进行判断
return result instanceof Object?result:obj;}
这里需要注意对构造函数返回值的判断
在new的时候,会对构造函数的返回值做一些判断:
1、如果返回值是基础数据类型,则忽略返回值;
2、如果返回值是引用数据类型,则使用return 的返回,也就是new操作符无效
8、闭包的概念及应用和缺陷
推荐阅读:深入理解JavaScript系列:闭包(Closures)
9 、箭头函数
箭头函数以及和普通函数的区别
10、事件循环 eventLoop 宏任务 微任务
JavaScript事件循环与实践应用
11、Promise async await Promis.all 和 Promic.race的区别
Promise对象与宏任务、微任务
12、ES6语法
阮一峰 ECMAScript 6入门
13、global对象、window对象、document对象
14、什么是堆?什么是栈?它们之间有什么区别和联系?
- 堆和栈的概念存在于数据结构中和操作系统内存中。
- 在数据结构中,栈中数据的存取方式为先进后出。而堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。完全
- 二叉树是堆的一种实现方式。 在操作系统中,内存被分为栈区和堆区。
- 栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
- 堆区内存一般由程序员分配释放,若程序员不释放,程序结束时可能由垃圾回收机制回收。
15、如何将浮点数点左边的数每三位添加一个逗号
function format(number) {
return number && number.replace(/(?!^)(?=(\d{3})+\.)/g, ",");
}
16、JavaScript 常用函数图解
17、 prototype、__proto__与constructor
- 我们需要牢记两点:
①__proto__和constructor属性是对象
所独有的;
② prototype属性是函数
所独有的,因为函数也是一种对象,所以函数也拥有__proto__和constructor属性。 - __proto__属性的作用就是当访问一个
对象
的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,然后返回undefined,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。 - prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.proto === Foo.prototype
- constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function
18、import动态引入
/** moduleA.ts */
export default function testA() {
console.log('this is A');
const name = 'testA';
return name;
}
/** moduleB.ts */
export default function testB() {
const name = 'testB';
console.log('this is B');
return name;
}
/**main.vue*/
import testA from './moduleA';
onMounted(() => {
testA();
// 动态引入moduleB
import('./moduleB').then(module => {
module.default();
console.log('log内容testb', module.default.name);
});
console.log('log内容testA', testA.name);
});
/**输出
this is A
log内容testA testA
this is B
log内容testb testB
*/