for...of、for...in一般来说,for...of 循环更适合遍历数组和可迭代对象,而 for...in 循环更适合遍历对象的属性。
- const arr = [1, 2, 3];
-
- // 使用 for...of 循环遍历数组
- for (let item of arr) {
- console.log(item); // 输出:1, 2, 3
- }
-
- // 使用 for...in 循环遍历数组(不建议)
- for (let item in arr) {
- console.log(arr[item]); // 输出:1, 2, 3
- }
- const obj = { a: 1, b: 2, c: 3 };
-
- // 使用 for...of 循环遍历对象(会报错,因为对象不是可迭代对象)
- // for (let item of obj) { ... } // 报错
-
- // 使用 for...in 循环遍历对象
- for (let item in obj) {
- if (obj.hasOwnProperty(item)) {
- console.log(obj[item]); // 输出:1, 2, 3
- }
- }
在 JavaScript 中,作用域是变量和函数的可访问区域或集合。它决定了代码块中变量和其他资源的可见性。作用域可以分为全局作用域、局部作用域(函数作用域)和块级作用域。
全局作用域是在函数或代码块之外声明的变量,它们在整个程序中都是可见的。而局部作用域,也称为函数作用域,是在函数内部声明的变量,只能在函数内部访问。
当在 JavaScript 中使用一个变量时,JavaScript 引擎会首先在当前作用域中查找该变量,如果找不到,它会继续在上层作用域中查找,以此类推,直到找到该变量或到达全局作用域。如果在全局作用域中仍然找不到该变量,它会在非严格模式下在全局范围内隐式声明该变量,或者在严格模式下直接报错。这个过程被称为作用域链。
值得注意的是,JavaScript 采用的是静态作用域,这意味着函数的作用域在函数定义时就确定了。此外,JavaScript 没有块级作用域,由花括号封闭的代码块不会创建新的作用域。
闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
在JavaScript中,闭包是由函数和对应的引用环境组合而成的实体。闭包让我们可以从函数内部访问其外部函数的作用域。每当函数创建,闭包就会被创建。为了使用闭包,我们可以简单的将一个函数定义在另一个函数的内部,然后将其暴露给外部,返回这个函数或者是把它传给另一个函数。内部函数会拥有访问外部函数作用域中变量的能力,即使是外部函数已经执行完毕并销毁。
在JavaScript中,闭包可能会导致内存泄漏,因为当一个函数返回了一个闭包,并且这个闭包引用了外部函数的变量时,即使外部函数已经执行完毕并被销毁,但由于闭包仍然存在,它仍然可以访问外部函数的变量。因此,外部函数的变量不会被垃圾回收机制回收,从而导致内存泄漏。
为了解决这个问题,可以使用以下几种方法:
Object.defineProperty() 是 JavaScript 中的一个方法,用于在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
Object.defineProperty(obj, prop, descriptor)
参数说明:
obj:需要定义属性的对象。prop:需要定义的属性名。descriptor:属性描述符,用于描述属性的特性。属性描述符(descriptor)可以是一个数据描述符或一个存取描述符,但不能同时是两者。数据描述符和存取描述符都具有以下可选键值:
configurable:当且仅当属性的 configurable 键值为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象中被删除。默认为 false。enumerable:当且仅当属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。默认为 false。value:包含了这个属性的数据值。默认为 undefined。writable:当且仅当属性的 writable 键值为 true 时,属性的值,也就是 value,才能被改变。默认为 false。get:一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(属性所在的对象)。默认值为 undefined。set:一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,会调用这个函数。这个方法接受唯一参数,即该属性新的参数值。默认值为 undefined。- let obj = {};
-
- // 定义一个数据描述符
- Object.defineProperty(obj, 'name', {
- value: 'John',
- writable: true,
- enumerable: true,
- configurable: true
- });
-
- console.log(obj.name); // 输出:John
- obj.name = 'David'; // 修改属性值
- console.log(obj.name); // 输出:David
-
- // 定义一个存取描述符
- let _age = 25; // 私有变量
- Object.defineProperty(obj, 'age', {
- get: function () {
- return _age;
- },
- set: function (value) {
- _age = value;
- },
- enumerable: true,
- configurable: true
- });
-
- console.log(obj.age); // 输出:25
- obj.age = 30; // 修改属性值
- console.log(obj.age); // 输出:30
手写apply、call

手写bind

原型(Prototype)和原型链(Prototype Chain)是 JavaScript 中的核心概念,用于实现对象之间的继承关系。
每个 JavaScript 对象都有一个原型(prototype)属性,该属性指向另一个对象,称为原型对象。原型对象本身也有一个原型,通过这种方式形成了原型链。当访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或方法或者到达原型链的末端(null)。
所有的引用类型(函数、对象、数组),都有一个__proto__属性(隐形原型),属性值是一个普通的对象。所有的引用类型(函数、对象、数组),__proto__属性值指向它的构造函数的prototype(显性原型)属性值。
通过这种方式,可以实现对象之间的继承。当一个对象需要访问另一个对象的属性或方法时,如果该属性或方法在当前对象本身上不存在,就会通过原型链查找到原型对象中对应的属性或方法,从而实现了继承。这种继承方式被称为原型继承。
//
JavaScript当中每个对象都有一个特殊的内置属性 [[prototype]],这个特殊的对象可以指向另外一个对象。
那么这个对象有什么用呢?
当我们通过引用对象的属性key来获取一个value时,它会触发 [[Get]]的操作;
这个操作会首先检查该属性是否有对应的属性,如果有的话就使用它;
如果对象中没有改属性,那么会访问对象[[prototype]]内置属性指向的对象上的属性
获取的方式有两种:
方式一:通过对象的 __proto__ 属性可以获取到(但是这个是早期浏览器自己添加的,存在一定的兼容性问 题);
方式二:通过 Object.getPrototypeOf 方法可以获取到;
函数也是一个对象
函数作为对象来说, 它也是有[[prototype]] 隐式原型
console.log(foo.__proto__)
函数它因为是一个函数, 所以它还会多出来一个显示原型属性: prototype
console.log(foo.prototype)
深拷贝是指,拷贝对象的具体内容,深拷贝在计算机中开辟一块新的内存地址用于存放复制的对象。 源数据改变不会影响复制的数据。
在JavaScript中,可以使用以下方式进行深拷贝:
