new操作符的实现原理
new操作符是用来创建对象的,它的实现原理如下:
(1)首先创建了一个新的空对象
(2)设置原型,将对象的原型设置为函数的 prototype 对象。
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
(4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
function myNew(fun,...args){
// let obj=Object.create(fun.prototype)第一个参数是新创建对象的原型对象。
let obj={}
obj.__proto__=fun.prototype
let res=fun.call(obj,...args)
if(res &&(typeof res ==="object" || typeof res ==="function")){
return res
}
return obj
}
function Person(name,age){
this.name = name;
this.age = age;
}
console.log(myNew(Person,'张三',20));
map和Object的区别
| map | object |
|---|---|
| 键可以是任意类型,包括原始类型和对象引用 | 键只能是字符串或者 Symbol 类型 |
| 键值对有序 | 键值对无序 |
方法:size 属性用于获取元素个数forEach() 方法用于遍历元素 | for in 循环 |
| 适合于需要存储大量动态数据的情况 | 适合于保存固定且已知的属性 |
Map使用
在 JavaScript 中,Map 是一种用来存储键值对的数据结构,你可以使用以下方法来使用 Map:
-
创建一个新的
Map对象:const myMap = new Map(); -
添加键值对到
Map中:myMap.set('key1', 'value1'); myMap.set('key2', 'value2'); -
从
Map中获取值:console.log(myMap.get('key1')); // 输出: value1 -
检查
Map中是否包含某个键:console.log(myMap.has('key1')); // 输出: true -
删除
Map中的键值对:myMap.delete('key2'); -
获取
Map的大小(包含的键值对数量):console.log(myMap.size); // 输出: 1 -
遍历
Map中的键值对:myMap.forEach((value, key) => { console.log(key + ' = ' + value); });
Map 还有许多其他的方法和特性,可以根据你的具体需求来使用。需要注意的是,Map 的键可以是任意类型,包括对象、数字、字符串等。
Object使用
在 JavaScript 中,Object 是一种非常常用的数据结构,用于存储键值对。以下是一些使用 Object 的常见方法:
-
创建一个新的空对象:
const myObject = {}; -
添加属性到对象中:
myObject.key1 = 'value1'; myObject['key2'] = 'value2'; -
从对象中获取属性的值:
console.log(myObject.key1); // 输出: value1 console.log(myObject['key2']); // 输出: value2 -
检查对象中是否包含某个属性:
console.log('key1' in myObject); // 输出: true -
删除对象中的属性:
delete myObject.key2; -
获取对象的所有属性名:
const keys = Object.keys(myObject); console.log(keys); // 输出: ['key1'] -
获取对象的所有属性值:
const values = Object.values(myObject); console.log(values); // 输出: ['value1']
Object 还有许多其他的方法和特性,比如Object.entries() 用于获取对象的键值对数组,Object.assign() 用于对象的合并等。需要根据具体的需求来选择使用哪种方法。
map和weakMap的区别
| Map | weakMap |
|---|---|
键是强引用类型,只要键存在于 Map 中,它所引用的对象就不会被垃圾回收 | 键是弱引用类型,键引用的对象如果没有其他引用存在,会被垃圾回收 |
| 键可以是任意类型,包括原始类型和对象引用 | 对象 |
Map 提供了迭代方法 (例如 keys()、values() 和 entries()),可以直接迭代 Map 的键值对 | WeakMap 由于键是弱引用,没有提供直接访问键的迭代方法。 |
Map 提供了 size 属性,可以获取其存储的键值对数量 | 无 |
由于这些区别,Map 更适用于存储需要长期保存的键值对,而 WeakMap 更适用于存储临时的、不需要长期保存并且键的生命周期由其他的对象决定的键值对。因为 WeakMap 的键是弱引用,所以无法遍历键、无法获取大小,也无法清空整个 WeakMap。
ES6模块与CommonJS模块有什么异同?
ES6模块和CommonJS模块是两种不同的模块系统,它们在很多方面有异同:
异同点:
-
语法差异:
-
ES6模块使用
import和export关键字来导入和导出模块, -
CommonJS模块使用
require()和module.exports来导入和导出模块
-
-
导入导出方式:
- ES6模块支持静态导入,模块的导入在代码静态分析阶段就能确定。
- CommonJS模块的导入是动态的,导入语句的执行发生在运行时。
-
模块加载时机:
- ES6模块是在编译时加载,因此模块的依赖关系在代码静态分析阶段就能确定。
- CommonJS模块是在运行时加载,模块的依赖关系只能在运行时确定。
-
默认导出和命名导出:
- ES6模块可以同时支持默认导出和命名导出。
- CommonJS模块不直接支持默认导出,但可以通过
module.exports来实现类似默认导出的功能。
-
浏览器兼容性:
- ES6模块是原生支持的,可以直接在支持ES6模块的现代浏览器中使用。
- CommonJS模块是Node.js环境中使用的模块系统,需要借助工具(如webpack或者Browserify)才能在浏览器中使用。
object.assign和扩展运算法是深拷贝还是浅拷贝,两者区别
-
Object.assign()方法接收的第一个参数作为目标对象,后面的所有参数作为源对象。然后把所有的源对象合并到目标对象中。它会修改了一个对象,因此会触发 ES6 setter。
-
扩展操作符(…)使用它时,数组或对象中的每一个值都会被拷贝到一个新的数组或对象中。它不复制继承的属性或类的属性,但是它会复制ES6的 symbols 属性。
Object.assign和扩展运算符都是浅拷贝。
浅拷贝是指只拷贝对象的第一层属性,对于对象的内部嵌套对象或数组,则会共享引用,而不是创建新的引用。
示例代码:
// 浅拷贝示例
const obj1 = { a: 1, b: { c: 2 } };
// 使用Object.assign进行浅拷贝
const obj2 = Object.assign({}, obj1);
obj2.b.c = 3;
console.log(obj1); // 输出: { a: 1, b: { c: 3 } }
// 使用扩展运算符进行浅拷贝
const obj3 = { ...obj1 };
obj3.b.c = 4;
console.log(obj1); // 输出: { a: 1, b: { c: 4 } }
可以看到,浅拷贝只会复制对象的第一层属性,对于嵌套的对象或数组,仍然是共享同一个引用。因此,修改拷贝后的对象的嵌套属性会影响原始对象。
defienProperty与proxy有何作用,区别是什么?
Object.defineProperty和Proxy都是用来实现对对象的监视和控制的机制
Object.defineProperty的作用和区别:
- 添加或删除对象的属性时,Vue 检测不到。因为添加或删除的对象没有在初始化进行响应式处理,只能通过
$set来调用Object.defineProperty()处理。 - 无法监控到数组下标和长度的变化。
- 只能对已有的属性进行操作,无法监视对象的整体操作,比如属性的读取、删除等行为。
示例:
const obj = { name: 'Alice' };
Object.defineProperty(obj, 'name', { writable: false }); // 将name属性设为不可写
Proxy的作用和区别:
- 可以监控整个对象的操作。
- 通过Proxy可以代理整个对象,可以监听同级结构下的所有属性变化,并在代理过程中定义对应的操作方法(如get、set、deleteProperty等),从而实现对对象的行为进行拦截和自定义处理。
- Proxy 可以监听数组的变化
示例:
const handler = {
get: function(target, prop) {
console.log(`Reading property ${prop}`);
return target[prop];
}
};
const proxy = new Proxy({ name: 'Alice' }, handler);
console.log(proxy.name); // 会触发get拦截器,打印"Reading property name"并返回"Alice"
扩展运算符的作用及使用场景
可以在某些情况下方便地将一个数组或对象展开成多个独立的值,或将多个值组合成一个数组或对象
(1)对象扩展运算符
对象的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。
(2)数组扩展运算符
数组的扩展运算符可以将一个数组转为用逗号分隔的参数序列,且每次只能展开一层数组。
- 合并数组:扩展运算符可以将一个数组中的元素展开,并插入到另一个数组中,实现数组的合并。
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const mergedArr = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
- 复制数组:可以使用扩展运算符来复制一个数组。
const originalArr = [1, 2, 3];
const copyArr = [...originalArr]; // 复制了originalArr数组
- 传递函数参数:在调用函数时,可以使用扩展运算符传递数组元素作为函数的参数。
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
const result = sum(...numbers); // result为6
- 合并对象:可以使用扩展运算符快速合并对象的属性。
const obj1 = { foo: 'bar' };
const obj2 = { baz: 'qux' };
const mergedObj = { ...obj1, ...obj2 }; // { foo: 'bar', baz: 'qux' }
- 字符串处理:可以将字符串分解为字符数组。
const str = 'hello';
const chars = [...str]; // ['h', 'e', 'l', 'l', 'o']
11.14
1.遍历器(Iterator)
-
它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
-
Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令
for...of循环,Iterator 接口主要供for...of消费。 -
Iterator 的遍历过程是这样的。
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
2.for...in 和for... of区别
-
for…in 获取的是对象的键名,for…of 遍历获取的是对象的键值;
-
for… in 会遍历对象的整个原型链,而 for … of 只遍历当前对象;
-
for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值;
-
总结: for...in 循环主要是为了遍历对象而生,不适用于遍历数组;for...of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象。
Generator
-
是Es6提供的一种异步编程解决方案,就是一个函数
-
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态
-
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,
function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
-
总结一下,调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的
next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。 -
next传入的参数, 第二次传入为第一次yield返回结果
Set
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
Set 实例的属性和方法
Set 结构的实例有以下属性。
Set.prototype.constructor:构造函数,默认就是Set函数。Set.prototype.size:返回Set实例的成员总数。
Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。
Set.prototype.add(value):添加某个值,返回 Set 结构本身。Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。Set.prototype.clear():清除所有成员,没有返回值。
遍历操作
Set 结构的实例有四个遍历方法,可以用于遍历成员。
Set.prototype.keys():返回键名的遍历器Set.prototype.values():返回键值的遍历器Set.prototype.entries():返回键值对的遍历器Set.prototype.forEach():使用回调函数遍历每个成员
Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
原因
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
实例的属性和操作方法
Map 结构的实例有以下属性和操作方法。
(1)size 属性
size属性返回 Map 结构的成员总数。
const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
(2)Map.prototype.set(key, value)
set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
const m = new Map();
m.set('edition', 6) // 键是字符串
m.set(262, 'standard') // 键是数值
m.set(undefined, 'nah') // 键是 undefined
set方法返回的是当前的Map对象,因此可以采用链式写法。
let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');
(3)Map.prototype.get(key)
get方法读取key对应的键值,如果找不到key,返回undefined。
const m = new Map();
const hello = function() {console.log('hello');};
m.set(hello, 'Hello ES6!') // 键是函数
m.get(hello) // Hello ES6!
(4)Map.prototype.has(key)
has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
const m = new Map();
m.set('edition', 6);
m.set(262, 'standard');
m.set(undefined, 'nah');
m.has('edition') // true
m.has('years') // false
m.has(262) // true
m.has(undefined) // true
(5)Map.prototype.delete(key)
delete()方法删除某个键,返回true。如果删除失败,返回false。
const m = new Map();
m.set(undefined, 'nah');
m.has(undefined) // true
m.delete(undefined)
m.has(undefined) // false
(6)Map.prototype.clear()
clear()方法清除所有成员,没有返回值。
let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
map.clear()
map.size // 0
遍历方法
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
Map.prototype.keys():返回键名的遍历器。Map.prototype.values():返回键值的遍历器。Map.prototype.entries():返回所有成员的遍历器。Map.prototype.forEach():遍历 Map 的所有成员。
Super的作用
在面向对象编程中,特别是在使用 JavaScript 中的类时,关键字 super 扮演着重要的角色。super 关键字主要用于两个方面:
- 在子类的构造函数中调用父类的构造函数:在子类的构造函数中,我们使用
super来调用父类的构造函数,实现对父类属性的初始化。这是super的最常见用法。示例如下:
class Animal {
constructor(name) {
this.name = name;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类的构造函数
this.breed = breed;
}
}
在这个例子中,Dog 类继承自 Animal 类,并在其构造函数中通过 super 调用了父类 Animal 的构造函数,传递了 name 参数进行初始化。
- 在子类方法中调用父类的同名方法:当子类和父类具有同名的方法时,子类中的方法可以通过
super调用父类的同名方法,实现对父类方法的扩展或覆写。示例如下:
class Animal {
makeSound() {
console.log("Generic animal sound");
}
}
class Dog extends Animal {
makeSound() {
super.makeSound(); // 调用父类的方法
console.log("Bark");
}
}
在这个例子中,Dog 类覆写了父类 Animal 的 makeSound 方法,在方法内部使用 super.makeSound() 调用了父类的同名方法,并在其后添加了狗的特有声音。
总之,super 关键字在类继承中起着连接子类和父类的重要作用,它使得子类可以轻松地调用父类的构造函数或者同名方法,实现了类的继承和方法的覆写,使得代码更加灵活和可维护。
11.15
1.async 和defer 的区别
defer 和 async属性都是去异步加载外部的JS脚本文件,它们都不会阻塞页面的解析
- 执行顺序:
多个带async属性的标签,不能保证加载的顺序;
多个带defer属性的标签,按照加载顺序执行;
- 脚本是否并行执行:
async 属性,表示后续文档的加载和执行与js脚本的加载和执行是并行进行的
defer 属性,后续文档的加载和js脚本的加载是并行的(仅是加载过程),js脚本需要等到文档解析完成之后才执行
2.Object.defineProperty
Object.defineProperty() 是 JavaScript 中的一个内置方法,用于定义或修改对象的属性。
该方法有三个参数:
- 对象:要定义或修改属性的对象。
- 属性名称:字符串类型,表示要定义或修改的属性的名称。
- 属性描述符:一个对象,用来描述要定义或修改的属性的特性。
属性描述符对象可以包含以下属性:
- configurable:用于指定属性是否可以被删除或修改特性,默认为 false。
- enumerable:用于指定属性是否可以被枚举,默认为 false。
- value:用于指定属性的值,默认为 undefined。
- writable:用于指定属性的值是否可以被修改,默认为 false。
- get:一个函数,用于获取属性的值。
- set:一个函数,用于设置属性的值。
使用 Object.defineProperty() 方法,可以在创建对象时或者在对象创建后动态地定义和修改属性。该方法通过属性描述符对象提供了灵活的属性定义和修改功能,可以控制属性的特性(如可写性、可枚举性等)以及属性的访问和赋值行为。
3.Proxy
Proxy 是 JavaScript 中的一个内置对象,用于创建代理对象,可以理解为在对象之前设置一个“拦截”,当该对象被访问的时候,都必须经过这层拦截。意味着你可以在这层拦截中进行各种操作。比如你可以在这层拦截中对原对象进行处理,返回你想返回的数据结构。 Proxy 的基本语法如下:
const proxy = new Proxy(target, handler);
- target:表示被代理的目标对象。
- handler:一个对象,用于定义拦截目标对象操作的各种行为。
handler 可以包含多个拦截操作的方法,例如:
- get:拦截对目标对象属性的访问。
- set:拦截对目标对象属性的设置。
- apply:拦截对目标对象的函数调用。
- deleteProperty:拦截对目标对象属性的删除操作。
- has:拦截对目标对象 in 操作符的判断。
4.Reflect
Reflect 是 JavaScript 的内置对象之一,它提供了一组静态方法,用于操作对象。它的设计目标是提供一种统一、更易于使用的方式来执行对象操作,以替代一些传统的操作符或函数。
5.Promise
- Promise 是 JavaScript 中用于处理异步操作的对象,它提供了一种更加结构化,更易于管理异步操作的方式
- 状态:Promise 对象最初处于未决状态(Pending),随后可能变为已完成状态(Fulfilled)或已拒绝状态(Rejected)
- 处理程序:通过使用 Promise 的 then 方法,可以指定异步操作完成时要执行的操作;也可以使用 catch 方法来捕获异步操作失败时的错误。
- 链式调用:Promise 实例可以通过链式调用 then 方法,使得多个异步操作按顺序执行,并且更加清晰和易于管理。