青训营前端 | 青训营

75 阅读8分钟

青训营上课笔记

三.JavaScript

JavaScript是一种高级的、直译式的编程语言,常用于前端开发,在后端也有广泛应用。

1.数据类型

a.JavaScript有七种基本数据类型:undefinednullbooleannumberstringsymbolobject。其中,nullobjecttypeof操作符中返回"object"。

b.JavaScript的基本数据类型是按值传递的,而对象则是按引用传递的。

c.JavaScript还有一种特殊的包装类型:NumberStringBoolean。这些包装类型实际上是对应的基本数据类型的对象包装。
// 示例代码
let foo = 5;
let bar = foo; // 将foo的值复制到bar,此时bar的值为5
foo = 10;
console.log(bar); // 输出:5,表明bar的值没有被改变

let obj1 = {name: 'John'};
let obj2 = obj1; // 将obj1的引用赋值给obj2,此时obj2指向的是obj1指向的对象
obj1.name = 'Jane';
console.log(obj2.name); // 输出:Jane,表明obj2指向的对象的属性被改变了

2.作用域和闭包

a.JavaScript使用词法作用域,即函数在声明时就确定了其作用域,与函数调用的位置无关。作用域规定了标识符的可见范围。

b.JavaScript支持函数嵌套,并且内部函数可以访问外部函数的变量。这种机制称为闭包。

c.闭包可以用来创建私有变量和访问外部函数的数据。
// 示例代码
function outer() {
  let count = 0; // count是外部函数outer的局部变量
  function inner() {
    count++; // 内部函数inner可以访问外部函数outer的变量count
    console.log(count);
  }
  return inner;
}

let closure = outer(); // 将返回的内部函数inner赋值给closure
closure(); // 输出:1
closure(); // 输出:2

console.log(count); // 报错,count是outer内部的局部变量,无法在外部访问

3.原型和原型链

a.JavaScript中的对象有一个原型(prototype)属性,可以指向另一个对象。如果某个对象的属性或方法在自身找不到,就会去原型对象中查找。

b.原型链是由多个对象的原型构成的链表结构,用于查找对象的属性和方法。
// 示例代码
let person = { name: 'John' };
let student = Object.create(person); // 创建一个新对象,并将person对象设置为其原型
student.id = 123;

console.log(student.name); // 输出:John,student对象的原型是person对象,所以可以访问name属性
console.log(student.id); // 输出:123

console.log(student.toString()); // 输出:[object Object],student对象的原型是Object.prototype,所以可以调用toString方法

4.异步编程

a.JavaScript中的异步编程通过回调函数、Promiseasync/await等方式来实现。

b.回调函数是一种传递给其他函数的函数,用于在异步操作完成后执行。但多层嵌套的回调函数容易产生回调地狱,使代码难以理解和维护。

c.Promise是一个用于处理异步操作的对象,可以更好地组织异步代码。Promise有三种状态:pending、fulfilled(resolved)和rejected。可以链式调用then方法来处理异步操作的结果。

d.async/await是一种更为简洁、直观的异步编程方式,基于Promise实现。使用async声明函数,函数内部使用await关键字来等待异步操作完成。
// 示例代码
// 回调函数示例
function asynchronousOperation(callback) {
  setTimeout(() => {
    callback("Data received");
  }, 1000);
}

function processData(data) {
  console.log("Processing data: " + data);
}

asynchronousOperation(processData);

// Promise示例
function asynchronousOperation() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Data received");
    }, 1000);
  });
}

asynchronousOperation().then((data) => {
  console.log("Processing data: " + data);
});

// async/await示例
async function asynchronousOperation() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Data received");
    }, 1000);
  });
}

(async function() {
  const data = await asynchronousOperation();
  console.log("Processing data: " + data);
})();

5.垃圾回收

a.JavaScript使用垃圾回收机制自动管理内存。当不再使用一个对象时,垃圾回收器会自动释放对象占用的内存空间。

b.JavaScript中的垃圾回收器采用的是基于标记和清除的算法,通过标记不再使用的对象,然后清除它们。

c.对象的引用计数也是一种垃圾回收机制,每个对象都有一个引用计数,当计数为0时,对象将被回收。
// 示例代码
function createObject() {
  let obj = new Object();
  return obj;
}

let obj1 = createObject(); // 创建一个新对象,obj1引用它
let obj2 = obj1; // obj2也引用同一个对象
obj1 = null; // obj1不再引用对象
obj2 = null; // obj2也不再引用对象,此时对象的引用计数为0,垃圾回收器可以回收对象

// 在其他地方仍然引用这个对象
console.log(obj2); // 输出:null,对象已经被回收

6.函数式编程

a.JavaScript支持函数式编程范式,函数是一等公民,可以作为变量、参数、返回值等进行操作。

b.函数式编程强调使用纯函数,即函数的输出仅由输入决定,没有副作用。这样可以减少代码的复杂性和错误概率。

c.在函数式编程中,常用的操作包括map、filter、reduce等,它们能够对数组进行转换和运算,产生新的数组或结果。
// 示例代码
// map示例:将数组中的每个元素进行平方运算
const numbers = [1, 2, 3, 4, 5];
const squaredNumbers = numbers.map((num) => num * num);
console.log(squaredNumbers); // 输出:[1, 4, 9, 16, 25]

// filter示例:过滤数组中的偶数
const evenNumbers = numbers.filter((num) => num % 2 === 0);
console.log(evenNumbers); // 输出:[2, 4]

// reduce示例:求和数组中的所有元素
const sum = numbers.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 输出:15

7.模块化

a.JavaScript的模块化能够将代码分割为多个独立的模块,提高代码的可维护性和可重用性。

b.ES6引入了原生的模块化支持,使用importexport关键字来导入和导出模块。

c.模块化还可以使用第三方库,如CommonJS、AMD等。
//示例代码
在math.js文件中定义一个模块:
// math.js
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}


// 在index.js文件中导入并使用模块:
// index.js
import { add, subtract } from './math.js';

console.log(add(5, 2)); // 输出:7
console.log(subtract(5, 2)); // 输出:3

8.错误处理

a.JavaScript中的错误处理可以通过try/catch语句来实现。try代码块用于包裹可能抛出错误的代码,catch代码块用于捕获并处理错误。

b.错误对象包含了错误的信息,如错误的类型、错误的消息等。

c.在异步操作中,可以使用Promisecatch方法或async/awaittry/catch语句来捕获错误。
//示例代码:
try {
  // 可能抛出错误的代码
  const result = someFunction(); // 假设someFunction函数不存在
  console.log(result); // 不会执行,因为在上一行抛出了错误
} catch (error) {
  // 错误处理代码
  console.log("An error occurred: " + error); // 输出:An error occurred: ReferenceError: someFunction is not defined
}

// Promise错误处理示例
function asynchronousOperation() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error("Something went wrong"));
    }, 1000);
  });
}

asynchronousOperation()
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    console.log("An error occurred: " + error); // 输出:An error occurred: Error: Something went wrong
  });

// async/await错误处理示例
async function main() {
  try {
    const result = await asynchronousOperation(); // 假设asynchronousOperation函数抛出了错误
    console.log(result);
  } catch (error) {
    console.log("An error occurred: " + error); // 输出:An error occurred: Error: Something went wrong
  }
}

main();

9.性能优化

a.在JavaScript中,可以通过一些技巧和最佳实践来提高代码的性能。

b.避免不必要的全局变量,减少作用域链的查找。

c.使用恰当的数据结构和算法,避免低效操作,如循环中的大量拼接字符串可以用数组或join方法代替。

d.频繁操作DOM会影响性能,可以优化为批量操作或使用文档片段。

e.在循环中尽量避免创建和销毁对象,可以重用对象或使用对象池。
// 示例代码:
// 避免频繁操作DOM的示例
const element = document.getElementById('myElement');
let content = '';
for (let i = 0; i < 1000; i++) {
  content += 'New content ';
}
element.innerHTML = content; // 频繁操作DOM,性能较低

// 优化为批量操作DOM的示例
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  const p = document.createElement('p');
  p.textContent = 'New content';
  fragment.appendChild(p);
}
element.appendChild(fragment); // 批量操作DOM,性能更高

// 重用对象的示例
function calculateSum(a, b) {
  // 重用数组对象,避免重复创建和销毁数组
  const result = [];
  for (let i = 0; i < a.length; i++) {
    result[i] = a[i] + b[i];
  }
  return result;
}

const array1 = [1, 2, 3];
const array2 = [4, 5, 6];
console.log(calculateSum(array1, array2)); // 输出:[5, 7, 9]

10.结合HTML与CSS

当结合HTML和CSS应用JavaScript时,可以实现各种交互效果和动态功能。

a.创建一个按钮,点击按钮时改变元素的样式

当点击按钮时,元素的背景色会切换为黄色。

HTML:

<button id="myButton">Click me</button>
<div id="myElement">Hello, world!</div>
CSS:

#myElement {
  font-size: 18px;
  color: blue;
}

#myElement.highlight {
  background-color: yellow;
}
JavaScript:

const button = document.getElementById('myButton');
const element = document.getElementById('myElement');

button.addEventListener('click', () => {
  element.classList.toggle('highlight');
});

b.使用JavaScript加载远程数据并动态更新页面内容

这段代码使用函数从远程API加载数据,并在加载完成后将数据以JSON字符串的形式显示在页面上。

HTML:

<div id="dataContainer"></div>
CSS:

#dataContainer {
  font-size: 18px;
}
JavaScript:

const dataContainer = document.getElementById('dataContainer');

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => {
    dataContainer.textContent = JSON.stringify(data);
  })
  .catch(error => {
    console.error(error);
    dataContainer.textContent = 'Error occurred while loading data.';
  });

c.创建一个图片轮播器

这段代码创建了一个简单的图片轮播器。每3秒钟自动切换到下一张图片,通过更改图片的opacity属性来实现淡入淡出效果。

HTML:

<div id="slider">
  <img src="image1.jpg" alt="Image 1">
  <img src="image2.jpg" alt="Image 2">
  <img src="image3.jpg" alt="Image 3">
</div>
CSS:

#slider {
  position: relative;
  width: 500px;
  height: 300px;
  overflow: hidden;
}

#slider img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  transition: opacity 0.5s ease-in-out;
}

.active {
  opacity: 1;
}
JavaScript:

const slider = document.getElementById('slider');
const images = slider.getElementsByTagName('img');
let index = 0;

function changeSlide() {
  images[index].classList.remove('active');
  index = (index + 1) % images.length;
  images[index].classList.add('active');
}

setInterval(changeSlide, 3000);

4.创建一个表单验证程序

这段代码创建了一个简单的表单验证程序。当点击提交按钮时,会检查输入框中的内容是否为空,如果有空字段,则显示错误消息;如果所有字段都填写了,则显示成功提交的消息。

HTML:

<form id="myForm">
  <input type="text" id="nameInput" placeholder="Enter your name">
  <input type="email" id="emailInput" placeholder="Enter your email">
  <button type="submit">Submit</button>
</form>
<p id="message"></p>
CSS:

form input {
  display: block;
  margin-bottom: 10px;
}

#message {
  font-weight: bold;
}
const form = document.getElementById('myForm');
const nameInput = document.getElementById('nameInput');
const emailInput = document.getElementById('emailInput');
const message = document.getElementById('message');

form.addEventListener('submit', (event) => {
  event.preventDefault(); // 阻止表单的默认提交行为

  const name = nameInput.value;
  const email = emailInput.value;

  if (name === '' || email === '') {
    message.textContent = 'Please fill in all fields.';
    message.style.color = 'red';
  } else {
    message.textContent = 'Form submitted successfully.';
    message.style.color = 'green';
  }
});