1. JS变量提升?
JavaScript的变量提升是指在代码执行前,变量和函数的声明会被提升到作用域的顶部。这意味着你可以在声明之前使用这些变量和函数。 变量提升:
console.log(a); // undefined
var a = 'hello';
//console.log(a1); // 这里就会报错
//const a1 = 'hello1';
函数提升:
b(); // world
function b() {
console.log('world');
}
只有声明会被提升,而不是初始化或赋值。变量提升将变量的声明移动到作用域的顶部,但不会将变量的赋值操作提升。
2. JS执行过程?
- 解析(Parsing)阶段:在这个阶段,JavaScript 引擎会对代码进行解析,将代码转换为可执行的语法树。这个过程包括词法分析和语法分析,其中词法分析将代码分解为词法单元(tokens),语法分析将词法单元转换为语法树。
- 变量提升(Variable Hoisting)阶段:在解析阶段之后,JavaScript 引擎会对变量和函数的声明进行提升。这意味着变量和函数的声明会被移动到作用域的顶部,可以在声明之前使用。
- 执行阶段:在执行阶段,JavaScript 引擎按照语法树执行代码,并进行变量赋值、函数调用等操作。代码的执行顺序通常是从上到下,但也受到控制流语句(如条件语句和循环语句)的影响。
在执行阶段,JavaScript 引擎会创建执行环境(Execution Context)来管理代码的执行。每个执行环境都有一个与之关联的变量环境(Variable Environment),用于存储变量和函数的声明,以及一个用于存储当前执行位置的词法环境(Lexical Environment)。
3. let, const, var 的区别?
4. 箭头函数与普通函数的区别?
箭头函数: 箭头函数的定义
普通函数: 函数定义
区别: 1. 语法的区别 ``` // 箭头函数 const arrowFunc = (param1, param2) => { // 函数体 };
// 普通函数(函数声明)
function regularFunc(param1, param2) {
// 函数体
}
// 普通函数(函数表达式)
const regularFunc = function(param1, param2) {
// 函数体
};
```
2. this 绑定
箭头函数没有自己的this绑定,箭头函数内部的this始终指向定义函数时的上下文,而不是调用时的上下文。
普通函数的this值是在运行时确定的
```
const obj = {
name: "John",
arrowFunc: () => {
console.log(this.name); // undefined
},
regularFunc: function() {
console.log(this.name); // "John"
}
};
obj.arrowFunc(); // 箭头函数中的 this 指向全局对象(undefined)
obj.regularFunc(); // 普通函数中的 this 指向 obj 对象
```
3. arguments对象
箭头函数没有自己的 arguments 对象。在箭头函数中可以使用剩余语法(...args)来代替 arguments
```
const arrowFunc = (...args) => {
console.log(args); // [1, 2, 3]
};
const regularFunc = function() {
console.log(arguments); // { '0': 1, '1': 2, '2': 3 }
};
arrowFunc(1, 2, 3);
regularFunc(1, 2, 3);
```
4. 不能作为构造函数
箭头函数不能使用 new 关键字来创建对象的实例。
普通函数可以作为构造函数来使用,并且使用 new 关键字时会创建一个新的对象实例。
5. new操作符的实现原理?
1. 首先创建一个新对象。
2. 设置原型,将这个新对象的__proto__原型指向构造函数的原型对象prototype。
p1.__ptoto__ = Person.prototype
3. 让构造函数的this指向这个新对象,执行构造函数的代码,就是为新对象添加属性。
4. 判断函数的返回值,如果是基本类型的就返回创建的对象,如果是引用类型的就返回引用类型的对象。
实现一个new
```
function myNew (func, ...args) {
const obj = Object.create(func.prototype) // Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
let result = func.apply(obj, args)
return result instanceof Object ? result : obj
}
```
使用
```
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function() {
console.log(`name是${this.name} ,age = ${this.age}`);
};
const john = myNew(Person, 'John', 25);
john.sayHello();
```
参考 https://juejin.cn/post/6991483397495324703
6. Set 和 Map ?
集合Set:是一种无序的集合,类似于数组,它可以存储各种类型的唯一值。Set中不允许有重复的值, Set使用和常用方法(添加值、删除值、检查值是否存在和迭代值):
const set = new Set();
set.add(1); // 添加
set.add(2); // 添加
set.add(3); // 添加
set.add(1); // 添加重复的值会被忽略
console.log(set.size); // 3
console.log(set.has(1)); // true
console.log(set.has(4)); // false
set.delete(3); // 删除3
for(let item of set) {
console.log(item)
}
字典Map:是一组键值对的结构,具有极快的查找速度。其中每个键都是唯一的。 Map使用:
const map = new Map();
map.set('k1', 'key1');
map.set('k2', 'key2');
map.set('k3', 'key3');
map.set('k2', 'keykey2');
console.log(map);
console.log(map.size); // 3
console.log(map.get('k1')); // key1
map.delete('k3');
console.log(map.size); // 2
console.log(map.has('k1')); // true
for(let [key, value] of map) {
console.log(key, value);
}
console.log(map.keys());
console.log(map.values());
7. WeakSet和WeakMap?
WeakSet对象允许你将若保持对象存储在一个集合中。
WeakSet使用和方法(add、delete、has):
const arr = [[1,2], [3,4]];
const weakset = new WeakSet(arr);
console.log(weakset)
const ws = new WeakSet();
const foo = {a:1};
const bar = {b:2};
ws.add(foo);
ws.add(bar);
console.log(ws);
console.log(ws.has(foo));
ws.delete(bar);
console.log(ws);
WeakSet 与 Set 的区别:
a. WeakSet只能存储对象引用,不能存放值,Set都可以存储。 b. WeakSet对象中存储的对象值都是被弱引用的,Set存储对象类型的数据是强引用的。Set中添加对象后,即使该对象在其他地方没有引用,它也不会被垃圾回收,直到从Set中删除它。 而在WeakSet中添加的对象,如果在其他地方没有强引用,那么它会在垃圾回收时被清理。 c. Set是可迭代的,可以使用 for..of..循环或 forEach方法遍历其中的值,它还提供了 size 属性来获取集合的大小。WeakSet 不可迭代,也没有 size 属性获取集合大小。
WeakMap使用和方法:
WeakMap和 Map 结构类似,也是用于生成键值对的集合。其中键是弱引用对象,而值可以是任意类型。
const wm1 = new WeakMap();
const key = {foo: 1};
wm1.set(key, 1); // key是引用类型的
console.log(wm1.get(key));
// wm1.set('k1', 'key1'); // 会报错,因为键只能是引用类型
const k1 = [1,2];
const wm2 = new WeakMap([[k1, 'foo']]);
console.log(wm2)
console.log(wm2.get(k1)); // foo
console.log(wm2.has(k1)); // true
WeakMap和Map的区别:
a. 键的引用处理方式:Map使用键的强引用,WeakMap使用键的弱引用。 b. 键的类型限制: Map可以使用任何类型的值做键,WeakMap只能使用对象作为键。 c. 迭代和大小:Map是可迭代的,WeakMap是不可迭代的。
8. 强引用和弱引用是什么?
在js中,引用是指对内存中的对象的访问。强引用和弱引用是两种不同的引用方式,它们决定了对象在垃圾回收过程中的生命周期。
强引用:
强引用是最常见的引用方式,当一个对象被强引用时,即使在程序中没有其他引用指向该对象,它也不会被垃圾回收。只有当没有任何强引用指向对象的时,对象才认为是不可访问的,从而被垃圾回器回收内存。 默认情况下,变量和对象属性都是强引用。 比如:
const obj = { name: 'Object' }; // 强引用
在上面的示例中,obj是一个强引用,即使没有其他引用指向这个对象,它仍然在内存中。
弱引用:
弱引用是一种不会阻止对象被垃圾回收的引用方式。当一个对象只被弱引用所引用时,如果没有其他强引用指向该对象,它会被垃圾回收。 弱引用通常用于解决一些特定问题,如缓存、临时数据等。
const weakMap = new WeakMap();
const obj = { name: 'Object' };
weakMap.set(obj, 'Value'); // 弱引用
在上面的示例中,WeakMap使用弱引用将 obj和Value关联起来。如果没有其他强引用指向obj,那么在垃圾回收时,obj以及关联的数据将被自动清理。
9. for...in和for...of 遍历?
for...in
for...in 是用于遍历对象的循环语句。variable是当前属性的名称(也就是key)
// 在迭代中variable 是对象的key
for (variable in object) {
// 循环体
}
var obj = { a: 1, b: 2, c: 3 }
for (let key in obj) {
console.log(key); // 'a', 'b', 'c'
}
注意: a. for...in 循环遍历的顺序不一定是属性被定义的顺序,也不一定是从小到大的顺序。对象属性的顺序在规范中并没有明确定义,所以遍历顺序可能会因 JavaScript 引擎的实现而有所不同。
for...of
for...of 是一种遍历可迭代对象(包括:数组、字符串、Set和Map等)的循环语句。它提供了一种简介的方式来遍历对象的元素,而不需要使用索引或者键。
// variable 是当前迭代元素的值
for (variable of iterable) {
// 循环体
}
const arr = [1, 2, 3];
for (const element of arr) {
console.log(element);
}
注意 a. for...of循环遍历的顺序与元素可迭代对象中的顺序一致。对于数组和字符串,它们的顺序是根据索引或字符串的顺序来确定的。
10. for...in 和 for...of的区别?
a. 使用对象:
for...in用于遍历可枚举属性,包括自身属性和继承的属性。比如 object对象
for...of用于遍历可迭代对象,比如数组,字符串等。
b. 迭代内容:
for...in迭代对象的属性名。
for...of迭代对象的元素值。
c. 遍历顺序:
for...in遍历对象时,顺序不一定是属性被定义的顺序,也不一定是从小到大的顺序。所遍历的顺序由 JavaScript引擎的实现而有所不同。
for...of遍历顺序与元素在可迭代对象中的顺序一致。对于数组和字符串,顺序是根据索引或字符串的顺序来确定的。对于 Set和Map,顺序是插入的顺序。
d. 对象的继承属性:
for...in会遍历对象的自身属性和继承的属性。
for...of只能遍历对象自身属性,不包括继承属性。
11. use strict是什么意思 ? 使用它区别是什么?
"use strict"是一种在JS中启用严格模型的指令。
通过在脚本文件或者函数开头添加该指令,可以告诉JS引擎以严格模式来解析和执行代码。严格模式引入了一些限制和改变。
主要区别和改变:
- 变量声明:在严格模式下,变量必须通过 var、let 或 const 关键字进行声明,否则会抛出 ReferenceError。
- 静默失败:在严格模式下,一些容易被忽略或潜在造成错误的操作将会抛出错误,例如对只读属性的赋值、删除不可删除属性、对未声明变量的赋值等。这样可以帮助开发者及时发现代码中的问题。
- 全局变量显示声明:在严格模式下,全局作用域中的变量和函数必须显式声明(使用 var、let 或 const),否则将不会创建对全局对象的属性。
- this值:在严格模式下,函数中的 this 值不会被自动转换为全局对象(如 window),而是保持为 undefined。这样可以避免在意外的上下文中使用 this 导致错误。
// 'use strict'
function fn() {
console.log(this);
}
fn()
- 删除变量:在严格模式下,无法删除通过 var 声明的变量,删除操作会抛出错误。
- 重复的参数名: 在严格模式下,函数定义中重复的参数名将会抛出错误。
- eval限制:在严格模式下,使用 eval 函数执行的代码将在独立的作用域中运行,不会影响到当前作用域的变量和函数。
12. ES6模块与CommonJS模块介绍?有什么异同?
ES6模块也称为 ECMAScript模块,和 CommonJS模块都是JavaScript两种不同的模块系统,用于组织和管理代码结构。 ES6模块:
- 语法: ES6使用 import 和 export 关键字来导入和导出模块。
- 文件级别的导入和导出:可以在一个模块总
- 值引用:ES6模块的导入和导出是属于值的引用,即每个导入的变量都是绑定到被导出的值上,它们是只读的,无法修改值、
- 静态加载:ES6模块在编译时进行静态分析,模块的依赖关系在编译阶段就确定下来。
- 顶层作用域:ES6模块在模块顶层有自己的作用域,每个模块都是独立的,不会污染全局作用域。
- 动态导入:ES6模块支持动态导入,可以在运行时根据条件或需要动态加载模块。 CommonJS模块
- 语法:使用 require()函数来导入模块。使用 module.exports或exports对象导出模块。
- 运行时加载:CommonJS模块在运行时进行加载和解析。
- 值拷贝:CommonJS模块的导出和导入都是基于值的拷贝,即每个导入的变量都是对导出值的拷贝,可以修改导出的值。
- 同步执行:CommonJS模块在模块内部是同步执行的,模块中的变量和函数在模块内部是共享的,可能会对全局作用域造成影响。
- 循环依赖处理:模块在处理循环依赖时会返回一个未完成的模块对象,导入的值是部分完成的,可能会导致循环依赖问题。
13. 扩展运算符的作用及使用场景?
扩展运算符:在JavaScript中扩展运算符是一种语法,使用三个连续的点(...)来展开可迭代对象(如:数组、字符串、类数组对象)或对象字面量。
使用场景:
// 数组
const array1 = [1, 2, 3];
const array2 = [4, 5, 6];
console.log([...array1, ...array2]); // [1, 2, 3, 4, 5, 6]
// 获取数组中的最大值/最小值
const numbers = [9, 4, 5, 1];
console.log(Math.min(...numbers)); // 1
console.log(Math.max(...numbers)); // 9
// 对象字面量扩展
const obj1 = { x: 1, y: 2 };
const obj2 = { z: 3, ...obj1 };
console.log(obj2); // {z: 3, x: 1, y: 2}
// 字符展开
const str = "hello";
console.log([...str]); // ['h', 'e', 'l', 'l', 'o']
// 函数参数
function foo() {
const args = [...arguments];
console.log(args);
}
foo(1, 2);
14. 对对象与数组的解构的理解?
解构是 ES6 提供的一种新的提取数据的模式,这种模式能够从对象或数组里有针对性地拿到想要的数值。
- 数组解构:
const [a, b, c] = [1, 2, 3]
- 对象的解构:
const obj = {
name: 'Bob',
age: 24
}
const { name, age } = obj
15. 如何提取高度嵌套的对象里的指定属性?
- 用递归的方法一层一层的去查找。
- 用解构的方法查找。
const school = {
classes: {
students: {
name: 'xiaoming',
age: 16,
}
}
}
// 在解构出来的变量名右侧,通过冒号 + {目标属性} 这种形式
const { classes: { students: { name } } } = school;
name // 'xiaoming'