(ES6)前端八股文修炼Day2

时间:2024-03-25 07:07:48

在这里插入图片描述

1. let const var 的区别

  • var:

var 是在 ES5 中引入的声明变量的关键字。
具有函数作用域,而不是块作用域,这意味着使用 var 声明的变量在函数内部是可见的。
变量可以被重复声明,而且变量的值可以在声明前使用,这可能会导致一些意外的行为。
使用 var声明的变量可以被提升(hoisted),即变量的声明会被提升到作用域的顶部。

  • let:

let 是在 ES6(ES2015)中引入的声明变量的关键字。
具有块级作用域,只在声明它的块(如 {})内部有效,不会污染外部作用域。
变量不能被重复声明,如果尝试重复声明会导致语法错误。
let 声明的变量不会被提升,不存在变量提升的情况。

  • const:

const 也是在 ES6 中引入的声明变量的关键字。
具有块级作用域,类似于 let,不能重复声明,不存在变量提升。
声明一个常量,值在声明后不能被修改。
声明时必须进行初始化,而且初始化后就不能再修改常量的值。

  • 示例比较:
var x = 10;
if (true) {
    var x = 20;
    console.log(x); // 输出: 20
}
console.log(x); // 输出: 20

// 使用 let
let y = 10;
if (true) {
    let y = 20;
    console.log(y); // 输出: 20
}
console.log(y); // 输出: 10

// 使用 const
const z = 10;
// z = 20; // 会导致错误,常量值无法修改

在实际开发中,推荐优先使用 const,除非变量的值需要被修改才使用 let。避免使用 var,因为它存在一些不利于代码维护和调试的特性。希望这个解释对你有帮助!如果有任何其他问题,请随时告诉我。

2.const 对象的属性可以修改吗?

在 JavaScript 中,使用 const 声明的变量是常量,这意味着变量的引用不可变,但并不意味着变量所引用的对象是不可变的。对于对象(包括数组)来说,使用 const 声明的变量可以修改对象的属性,但不能重新赋值一个新的对象给这个变量。

下面是一个示例来说明这一点:

const person = {
    name: 'Alice',
    age: 30
};

// 修改对象属性是允许的
person.age = 31;
console.log(person); // 输出: { name: 'Alice', age: 31 }
// 重新赋值一个新对象会导致错误
// person = { name: 'Bob', age: 25 }; // 这行会导致错误

在上面的示例中,虽然 person 是用 const 声明的,我们仍然可以修改 person 对象的属性,如更改 age 的值。但是,尝试将一个新对象赋值给 person 会导致错误,因为常量变量的引用是不可变的。

3.new一个箭头函数的会怎么样?箭头函数与普通函数的区别?箭头函数的this指向哪⾥?

在 JavaScript 中,箭头函数和普通函数有一些重要的区别,其中包括在使用 new 关键字时的行为以及 this 的指向:

  1. 使用 new 关键字:

    • 普通函数可以使用 new 关键字来创建一个新的对象实例,这时候函数内部的 this 会指向新创建的实例。
    • 箭头函数不能用 new 关键字来创建实例,因为箭头函数没有自己的 this,它会继承外层作用域的 this
  2. 箭头函数与普通函数的区别:

    • 箭头函数没有自己的 thisargumentssupernew.target,它们都由外围最近一层非箭头函数决定。
    • 箭头函数不能用作构造函数,不能使用 new 关键字。
    • 箭头函数没有原型(prototype)属性。
  3. 箭头函数的 this 指向:

    • 箭头函数的 this 指向在函数定义时确定,取决于箭头函数外围作用域的 this
    • 普通函数的 this 则在函数被调用时才确定,取决于函数的调用方式(比如作为方法调用、函数调用、构造函数调用等)。

下面是一个示例来说明箭头函数的 this 指向和普通函数的区别:

function NormalFunction() {
    this.value = 42;
    setTimeout(function() {
        console.log(this.value); // 输出 undefined,普通函数的 this 指向全局对象或 undefined
    }, 1000);
}

const ArrowFunction = () => {
    this.value = 42;
    setTimeout(() => {
        console.log(this.value); // 输出 42,箭头函数的 this 指向外围作用域的 this
    }, 1000);
};

new NormalFunction(); // 输出 undefined
new ArrowFunction(); // 输出 42

在上面的示例中,setTimeout 内部的箭头函数能够访问到外围作用域的 this,而普通函数的 this 则不同,因为普通函数内部的 this 取决于调用方式。

4. 扩展运算符的作用及使用场景

扩展运算符(Spread Operator)是 ES6 中引入的一个功能强大的语法,用三个点(...)表示。它可以将一个可迭代对象(比如数组、对象等)拆分为独立的元素,或将多个参数展开为单独的参数。扩展运算符的作用和使用场景有以下几个方面:

  1. 在数组中的使用:

    • 将数组展开为独立的元素:

      const arr1 = [1, 2, 3];
      const arr2 = [4, 5, ...arr1, 6, 7];
      console.log(arr2); // 输出: [4, 5, 1, 2, 3, 6, 7]
      
    • 复制数组:

      const originalArray = [1, 2, 3];
      const copyArray = [...originalArray];
      
    • 合并数组:

      const arr1 = [1, 2];
      const arr2 = [3, 4];
      const combinedArray = [...arr1, ...arr2];
      
  2. 在函数调用中的使用:

    • 将数组作为参数传递给函数:
      const numbers = [1, 2, 3];
      function sum(a, b, c) {
          return a + b + c;
      }
      console.log(sum(...numbers)); // 输出: 6
      
  3. 在对象中的使用:

    • 浅拷贝对象:

      const obj1 = { a: 1, b: 2 };
      const obj2 = { ...obj1 }; // 浅拷贝 obj1 到 obj2
      
    • 合并对象:

      const obj1 = { a: 1 };
      const obj2 = { b: 2 };
      const mergedObj = { ...obj1, ...obj2 };
      
  4. 在函数参数中的使用:

    • 用于传递不定数量的参数:
      function sum(...args) {
          return args.reduce((acc, val) => acc + val, 0);
      }
      console.log(sum(1, 2, 3)); // 输出: 6
      

扩展运算符为 JavaScript 的数据处理提供了更灵活和方便的方式,可以简化代码并提高可读性。它在处理数组、对象、函数参数等方面都有很好的应用场景。希望这个解释对你有帮助!如果有任何其他问题,请随时告诉我。

5. Proxy

Proxy 是 ES6 中引入的一个非常强大且灵活的功能,它可以用来拦截并定义对象上的基本操作,从而可以实现各种功能,例如:

  1. 属性访问控制:通过 getset 拦截器,可以控制对对象属性的访问和赋值操作,实现属性的隐藏、验证等功能。

  2. 函数调用控制:通过 apply 拦截器,可以控制对函数的调用操作,可以在函数调用前后执行额外逻辑。

  3. 数组操作的控制:可以通过拦截数组的操作,例如 pushpopsplice 等,实现对数组的监控、验证等功能。

  4. 动态扩展属性:可以通过 get 拦截器动态生成属性,实现虚拟属性或计算属性。

  5. 数据验证:可以在 set 拦截器中对属性值进行验证,确保数据的合法性。

  6. 数据绑定:可以实现数据的双向绑定,当对象数据发生变化时自动更新相关内容。

  7. 缓存:可以利用 Proxy 实现缓存功能,避免重复计算,提高程序性能。

  8. 日志记录:可以在拦截器中记录对象操作的历史,用于调试和监控。

  9. 实现观察者模式:可以在属性访问和赋值时触发相应操作,实现观察者模式。

  10. 实现权限控制:可以根据需要拦截对象的操作,实现权限控制和安全性增强。

总的来说,Proxy 提供了一种对对象进行拦截和自定义操作的机制,可以实现很多复杂的功能和行为定制。
下面我将简要介绍 Proxy 如何实现观察者模式:

  1. 创建观察者模式的实现:

    • 首先,需要创建一个被观察者对象和一个存储观察者的数组。

    • 使用 Proxy 对被观察者对象进行封装,设置一个 observers 数组来存储观察者。

    • 通过 set 拦截器,在被观察者对象的属性发生变化时,通知所有观察者。

  2. 示例代码:

    // 创建一个被观察者对象
    const subject = new Proxy({ value: 0, observers: [] }, {
        set(target, key, value, receiver) {
            const result = Reflect.set(target, key, value, receiver);
            if (key === 'value') {
                target.observers.forEach(observer => observer());
            }
            return result;
        }
    });
    
    // 创建观察者
    const observer1 = () => console.log('Observer 1 updated: ', subject.value);
    const observer2 = () => console.log('Observer 2 updated: ', subject.value);
    
    // 添加观察者
    subject.observers.push(observer1, observer2);
    
    // 修改被观察者的值,触发通知
    subject.value = 1; // Observer 1 updated: 1, Observer 2 updated: 1
    

在上面的示例中,我们创建了一个简单的观察者模式,使用 Proxy 对被观察者对象进行拦截,在属性值发生变化时通知所有观察者。这样,观察者模式就得以实现。

6.提取高度嵌套对象里的指定属性

要提取高度嵌套对象里的指定属性,可以使用递归和对象解构来实现。下面是一个示例,演示如何提取高度嵌套对象中的指定属性:

// 定义一个高度嵌套的对象
const nestedObject = {
    level1: {
        level2: {
            level3: {
                key: 'value'
            }
        }
    }
};

// 递归函数用于提取指定属性
const extractPropertyValue = (obj, property) => {
    for (let key in obj) {
        if (key === property) {
            return obj[key];
        } else if (typeof obj[key] === 'object') {
            return extractPropertyValue(obj[key], property);
        }
    }
};

// 指定要提取的属性名
const propertyToExtract = 'key';

// 提取指定属性值
const extractedValue = extractPropertyValue(nestedObject, propertyToExtract);
console.log(extractedValue); // 输出: 'value'

在上面的示例中,我们定义了一个高度嵌套的对象 nestedObject,然后编写了一个递归函数 extractPropertyValue,该函数接收一个对象和要提取的属性名,在对象中查找指定属性,并返回其值。通过调用这个函数,我们成功提取了高度嵌套对象中的指定属性值。

这种方法可以应用于任意层级的嵌套对象,通过递归地遍历对象的属性,找到目标属性并返回其值。

7.对对象与数组的解构的理解

对象和数组的解构是 JavaScript 中一种强大且方便的语法,用于快速提取数组或对象中的值并赋给变量,以便后续使用。下面我将简要介绍对象解构和数组解构的基本概念:

对象解构:
  • 基本语法: 使用花括号 {} 来指定要提取的对象属性,并将属性值赋给相应的变量。

    const person = { name: 'Alice', age: 30 };
    const { name, age } = person;
    console.log(name); // 输出: 'Alice'
    console.log(age); // 输出: 30
    
  • 给变量起别名: 可以使用冒号 : 为提取的属性值指定变量名。

    const { name: personName, age: personAge } = person;
    console.log(personName); // 输出: 'Alice'
    console.log(personAge); // 输出: 30
    
数组解构:
  • 基本语法: 使用方括号 [] 来指定要提取的数组元素,并将值赋给相应的变量。

    const numbers = [1, 2, 3];
    const [first, second, third] = numbers;
    console.log(first); // 输出: 1
    console.log(second); // 输出: 2
    
  • 忽略某些元素: 可以使用逗号 , 来跳过不需要的数组元素。

    const [,, third] = numbers;
    console.log(third); // 输出: 3
    

对象和数组解构可以简化代码,提高可读性,并且方便地从复杂的数据结构中提取所需的值。当结合递归和其他技术时,对象和数组解构可以变得非常强大。

8.Rest参数

Rest 参数是 ES6 中引入的一个新特性,用于捕获函数参数中的剩余参数,将其表示为一个数组。这样可以处理不定数量的参数,而不需要显式地定义参数个数。下面是关于 rest 参数的一些基本概念和示例:

Rest 参数的基本语法
  • 在函数定义时,使用三个点号 ... 加上一个参数名来表示 rest 参数,通常放在参数列表的最后。
function sum(...numbers) {
    return numbers.reduce((acc, curr) => acc + curr, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // 输出: 15

在这个示例中,...numbers 表示将所有传入函数的参数作为一个数组 numbers 来存储。

Rest 参数的应用场景:
  • 处理不定数量的参数: Rest 参数允许函数接受任意数量的参数,无需提前定义参数个数。
function multiply(multiplier, ...numbers) {
    return numbers.map(num => num * multiplier);
}

console.log(multiply(2, 1, 2, 3, 4)); // 输出: [2, 4, 6, 8]
  • 替代 arguments 对象: Rest 参数可以替代传统的 arguments 对象,更直观地操作参数。
function logArguments(...args) {
    console.log(args);
}

logArguments('a', 'b', 'c'); // 输出: ['a', 'b', 'c']

Rest 参数通常用于函数定义中,方便处理不定数量的参数,提高代码的灵活性和可读性。

9.对 rest 参数的理解ES6中模板语法与字符串处理

ES6 中的模板字符串是一种更灵活、更强大的字符串处理方式,可以在字符串中插入变量、表达式,并支持多行字符串的定义。下面我将介绍 ES6 中模板字符串的基本语法和常见用法:

模板字符串的基本语法:

  • 使用反引号 ``(通常位于键盘左上角,与波浪符号~同一个键)来定义模板字符串。
const name = 'Alice';
const greeting = `Hello, ${name}!`;
console.log(greeting); // 输出: 'Hello, Alice!'
  • 在模板字符串中,使用 ${} 来插入变量或表达式,可以是任意有效的 JavaScript 表达式。

多行字符串的定义:

  • 模板字符串可以跨越多行,而无需使用 \n 换行符。
const multiLine = `
    This is a
    multi-line
    string.
`;
console.log(multiLine);
// 输出:
//     This is a
//     multi-line
//     string.

标签模板字符串:

  • 可以使用标签函数对模板字符串进行处理,这种技术称为标签模板字符串。
function customTag(strings, ...values) {
    console.log(strings);
    console.log(values);
}

const value1 = 10;
const value2 = 20;
customTag`The values are: ${value1} and ${value2}`;
// 输出:
//     ["The values are: ", " and ", ""]
//     [10, 20]

模板字符串提供了一种更简洁、更直观的方式来处理字符串内容,特别是在需要插入变量或多行文本时非常有用。标签模板字符串则扩展了这一概念,允许自定义对模板字符串的处理方式。