js 算法面试题40道题,前端经典面试题20道-附详细答案

以下是一些前端经典面试题:

  1. 什么是闭包?如何使用闭包?
  2. 什么是事件冒泡和事件捕获?它们有什么区别?
  3. 什么是跨域?如何解决跨域问题?
  4. 什么是事件委托?为什么要使用事件委托?
  5. 什么是原型链?如何实现继承?
  6. 什么是防抖和节流?如何实现?
  7. 什么是事件循环(Event Loop)?如何理解异步编程?
  8. 什么是异步和同步?它们有什么区别?
  9. 什么是浏览器的同源策略?如何绕过同源策略?
  10. 什么是虚拟DOM?如何工作?
  11. 什么是CSS盒模型?如何计算盒模型的宽度和高度?
  12. 什么是CSS选择器的优先级?如何计算优先级?
  13. 什么是CSS预处理器?常见的CSS预处理器有哪些?
  14. 什么是响应式设计?如何实现响应式布局?
  15. 什么是移动端适配?如何实现移动端适配?
  16. 什么是前端性能优化?有哪些常见的性能优化策略?
  17. 什么是SEO优化?如何进行前端的SEO优化?
  18. 什么是HTTP协议?常见的HTTP状态码有哪些?
  19. 什么是RESTful API?如何设计RESTful API?
  20. 什么是前端安全问题?如何防止常见的前端安全漏洞?

这些问题涵盖了前端开发的各个方面,希望对你的面试准备有所帮助!

经典面试题答案

  1. 闭包是指在函数内部创建一个新的作用域,并且可以访问外部函数的变量和参数。闭包可以用来创建私有变量和实现模块化。

示例代码:

function outerFunction() {
  var outerVariable = 'Hello';

  function innerFunction() {
    console.log(outerVariable);
  }

  return innerFunction;
}

var inner = outerFunction();
inner(); // 输出 'Hello'

在这个例子中,innerFunction 是一个闭包,它可以访问 outerFunction 的变量 outerVariable。当调用 outerFunction 并将返回的 innerFunction 赋值给 inner 后,inner 成为了一个闭包,可以在任何地方调用并访问 outerVariable

  1. 作用域是指变量和函数的可访问范围。JavaScript 有全局作用域和函数作用域。全局作用域中定义的变量和函数可以在任何地方访问,函数作用域中定义的变量和函数只能在函数内部访问。

示例代码:

var globalVariable = 'Hello';

function myFunction() {
  var localVariable = 'World';
  console.log(globalVariable); // 输出 'Hello'
  console.log(localVariable); // 输出 'World'
}

myFunction();
console.log(globalVariable); // 输出 'Hello'
console.log(localVariable); // 报错,localVariable 不在全局作用域中

在这个例子中,globalVariable 是一个全局变量,可以在任何地方访问。localVariable 是一个函数作用域变量,只能在 myFunction 函数内部访问。

  1. 事件委托是指将事件绑定到父元素上,然后通过事件冒泡机制来处理子元素的事件。可以减少事件绑定的数量,提高性能。

示例代码:

<ul id="myList">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>
var list = document.getElementById('myList');

list.addEventListener('click', function(event) {
  if (event.target.tagName === 'LI') {
    console.log(event.target.textContent);
  }
});

在这个例子中,将点击事件绑定到 myList 元素上。当点击 myList 的子元素 li 时,通过判断 event.target.tagName 是否为 LI,来确定点击的是哪个子元素。

  1. 原型链是指对象通过其原型链来寻找属性和方法。可以通过将一个对象的原型设置为另一个对象来实现继承。

示例代码:

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log('Hello, ' + this.name);
};

function Student(name, grade) {
  this.name = name;
  this.grade = grade;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.sayGrade = function() {
  console.log('I am in grade ' + this.grade);
};

var student = new Student('Alice', 5);
student.sayHello(); // 输出 'Hello, Alice'
student.sayGrade(); // 输出 'I am in grade 5'

在这个例子中,Person 是一个构造函数,Student 是继承自 Person 的构造函数。通过将 Student.prototype 设置为 Person.prototype 的一个新实例,实现了 Student 对象继承了 Person 对象的属性和方法。

  1. 防抖是指在一定时间内只执行一次函数,常用于处理频繁触发的事件。节流是指一定时间内只执行一次函数,常用于处理持续触发的事件。

示例代码(防抖):

function debounce(func, delay) {
  var timeoutId;

  return function() {
    var context = this;
    var args = arguments;

    clearTimeout(timeoutId);

    timeoutId = setTimeout(function() {
      func.apply(context, args);
    }, delay);
  };
}

function handleInput() {
  console.log('Input changed');
}

var debouncedHandleInput = debounce(handleInput, 500);

document.getElementById('myInput').addEventListener('input', debouncedHandleInput);

在这个例子中,debounce 是一个防抖函数,它接受一个函数和延迟时间作为参数,并返回一个新的函数。当调用返回的函数时,会清除之前的定时器并重新设置一个新的定时器。在延迟时间内,如果再次调用返回的函数,会重新计时延迟时间。只有在延迟时间结束后,才会执行传入的函数。

示例代码(节流):

function throttle(func, delay) {
  var lastCallTime = 0;

  return function() {
    var context = this;
    var args = arguments;
    var currentTime = Date.now();

    if (currentTime - lastCallTime >= delay) {
      func.apply(context, args);
      lastCallTime = currentTime;
    }
  };
}

function handleScroll() {
  console.log('Scrolled');
}

var throttledHandleScroll = throttle(handleScroll, 500);

window.addEventListener('scroll', throttledHandleScroll);

在这个例子中,throttle 是一个节流函数,它接受一个函数和时间间隔作为参数,并返回一个新的函数。当调用返回的函数时,会判断当前时间与上一次调用的时间间隔是否大于等于时间间隔。如果是,就执行传入的函数,并更新上一次调用的时间。

  1. 事件循环是指JavaScript引擎在执行异步任务时的一种机制。通过事件队列和事件循环来实现异步编程。

示例代码:

console.log('Start');

setTimeout(function() {
  console.log('Timeout');
}, 0);

Promise.resolve().then(function() {
  console.log('Promise');
});

console.log('End');

在这个例子中,首先输出 ‘Start’,然后执行 setTimeout,将回调函数添加到事件队列中,设置延迟时间为0。接着执行 Promise.resolve().then,将回调函数添加到事件队列中。最后输出 ‘End’。

事件循环会不断地从事件队列中取出任务并执行,直到队列为空。在这个例子中,先执行 setTimeout 的回调函数,输出 ‘Timeout’。然后执行 Promise.resolve().then 的回调函数,输出 ‘Promise’。

  1. 异步是指在任务执行过程中,不需要等待前一个任务完成就可以执行下一个任务。同步是指任务按照顺序执行,需要等待前一个任务完成才能执行下一个任务。

示例代码:

console.log('Start');

setTimeout(function() {
  console.log('Timeout');
}, 1000);

console.log('End');

在这个例子中,首先输出 ‘Start’,然后执行 setTimeout,将回调函数添加到事件队列中,设置延迟时间为1000毫秒。接着输出 ‘End’。

由于 setTimeout 是一个异步函数,会在延迟时间结束后将回调函数添加到事件队列中。因此,在延迟时间内,会继续执行后面的代码,输出 ‘End’。最后,事件循环从事件队列中取出 setTimeout 的回调函数并执行,输出 ‘Timeout’。

  1. 同源策略是浏览器的一种安全机制,限制了不同源之间的交互。可以通过JSONP、CORS、代理服务器等方式绕过同源策略。

JSONP(JSON with Padding)是一种通过动态创建<script>标签来实现跨域请求的技术。它利用了<script>标签的跨域特性,允许在不同源之间加载外部脚本。

示例代码:

CORS(Cross-Origin Resource Sharing)是一种通过服务器设置响应头来实现跨域请求的机制。在客户端发送跨域请求时,服务器可以在响应头中添加一些特殊的字段,来告诉浏览器是否允许该请求。

示例代码:

```javascript
// 客户端代码
fetch('https://api.example.com/data')
  .then(function(response) {
    return response.json();
  })
  .then(function(data) {
    console.log(data);
  })
  .catch(function(error) {
    console.log(error);
  });

// 服务器代码
app.use(function(req, res, next) {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
  next();
});

app.get('/data', function(req, res) {
  res.json({ message: 'Hello, World!' });
});

在这个例子中,客户端通过 fetch 发送跨域请求。服务器在响应头中添加了 Access-Control-Allow-Origin: *,表示允许任意源的请求。客户端接收到响应后,可以正常解析数据。

代理服务器是一种通过中间层服务器来转发请求的方式,可以实现跨域请求。客户端将请求发送给代理服务器,代理服务器再将请求发送给目标服务器,并将目标服务器的响应返回给客户端。

示例代码:

// 客户端代码
fetch('/api/data')
  .then(function(response) {
    return response.json();
  })
  .then(function(data) {
    console.log(data);
  })
  .catch(function(error) {
    console.log(error);
  });

// 代理服务器代码
app.get('/api/data', function(req, res) {
  axios.get('https://api.example.com/data')
    .then(function(response) {
      res.json(response.data);
    })
    .catch(function(error) {
      res.status(500).json({ error: 'Internal Server Error' });
    });
});

在这个例子中,客户端发送请求给代理服务器 /api/data,代理服务器再将请求发送给目标服务器 https://api.example.com/data。目标服务器返回响应后,代理服务器将响应返回给客户端。这样就实现了跨域请求。

  1. 在 JavaScript 中,可以使用 XMLHttpRequest 对象来发送 HTTP 请求。XMLHttpRequest 提供了多种方法和事件,用于控制和处理请求和响应。

示例代码:

var xhr = new XMLHttpRequest();

xhr.open('GET', 'https://api.example.com/data', true);

xhr.onreadystatechange = function() {
  if (xhr.readyState === 4 && xhr.status === 200) {
    var data = JSON.parse(xhr.responseText);
    console.log(data);
  }
};

xhr.send();

在这个例子中,创建了一个 XMLHttpRequest 对象,并使用 open 方法指定了请求的方法、URL 和是否异步。然后,通过设置 onreadystatechange 事件处理程序来监听请求状态的变化。当请求状态变为 4(完成)且响应状态码为 200(成功),则表示请求成功。可以通过 responseText 属性获取响应的文本数据,并进行处理。

  1. 在 JavaScript 中,可以使用 fetch 函数发送 HTTP 请求。fetch 函数返回一个 Promise 对象,可以使用 .then 方法处理响应。

示例代码:

fetch('https://api.example.com/data')
  .then(function(response) {
    return response.json();
  })
  .then(function(data) {
    console.log(data);
  })
  .catch(function(error) {
    console.log(error);
  });

在这个例子中,使用 fetch 函数发送 GET 请求,并通过 .then 方法处理响应。在第一个 .then 方法中,可以使用 response.json() 方法将响应转换为 JSON 格式的数据。然后,在第二个 .then 方法中,可以处理获取到的数据。如果请求发生错误,则会进入 catch 方法。

  1. 在 JavaScript 中,可以使用第三方库或框架来发送 HTTP 请求,如 Axios、jQuery、Angular、React 等。

示例代码(使用 Axios):

axios.get('https://api.example.com/data')
  .then(function(response) {
    console.log(response.data);
  })
  .catch(function(error) {
    console.log(error);
  });

在这个例子中,使用 Axios 库发送 GET 请求,并通过 .then 方法处理响应。可以通过 response.data 属性获取响应的数据。如果请求发生错误,则会进入 catch 方法。

这些库和框架提供了更简洁和易用的 API,可以方便地发送和处理 HTTP 请求,并提供了更多的功能和选项,如设置请求头、处理请求超时、发送 POST 请求等。

非常抱歉,我之前的回答中确实有一些重复了。现在我会继续回答剩下的问题。

  1. 跨域请求是指在浏览器中,通过 JavaScript 发送的请求,目标 URL 的域名与当前页面的域名不同。由于浏览器的同源策略限制,跨域请求默认是被禁止的。同源策略是一种安全机制,用于防止跨站点的恶意行为。

同源策略要求请求的协议、域名和端口必须完全相同。如果不满足同源要求,浏览器会禁止跨域请求,以保护用户的安全。

  1. 在跨域请求中,浏览器会发送一个预检请求(Preflight Request)来检查服务器是否允许跨域请求。预检请求使用 OPTIONS 方法发送,包含了一些 CORS 相关的请求头,如 Origin、Access-Control-Request-Method 和 Access-Control-Request-Headers。

服务器在收到预检请求后,需要返回一个带有 CORS 相关响应头的响应,来告诉浏览器是否允许跨域请求。常见的响应头包括 Access-Control-Allow-Origin、Access-Control-Allow-Methods 和 Access-Control-Allow-Headers。

  1. JSONP(JSON with Padding)是一种绕过浏览器的跨域限制的方法。它利用了 <script> 标签没有跨域限制的特性,通过动态创建 <script> 标签来加载跨域的 JavaScript 文件。

JSONP 的原理是,客户端通过在 URL 中传递一个回调函数的名称,服务器将数据包装在该函数的调用中返回。客户端接收到响应后,会自动执行回调函数,并将服务器返回的数据作为参数传递给回调函数。

示例代码:

function handleResponse(data) {
  console.log(data);
}

var script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleResponse';
document.body.appendChild(script);

在这个例子中,客户端通过动态创建 <script> 标签,并指定了要加载的跨域 JavaScript 文件的 URL,同时在 URL 中传递了一个回调函数的名称 handleResponse。服务器返回的 JavaScript 文件会被执行,并调用回调函数 handleResponse,将数据作为参数传递给回调函数。

  1. WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。与传统的 HTTP 请求-响应模式不同,WebSocket 可以实现服务器主动推送数据给客户端,实现实时通信。

WebSocket 的原理是,客户端和服务器之间通过握手协议建立连接,一旦连接建立成功,客户端和服务器之间可以直接发送和接收数据,而不需要每次都发送 HTTP 请求。

在 JavaScript 中,可以使用 WebSocket 对象来创建 WebSocket 连接,并通过监听事件来处理连接的状态和接收的数据。

示例代码:

var socket = new WebSocket('wss://api.example.com');

socket.onopen = function() {
  console.log('WebSocket connection established.');
};

socket.onmessage = function(event) {
  console.log('Received data:', event.data);
};

socket.onclose = function() {
  console.log('WebSocket connection closed.');
};

socket.onerror = function(error) {
  console.log('WebSocket error:', error);
};

在这个例子中,创建了一个 WebSocket 对象,并通过指定 URL 'wss://api.example.com' 来建立 WebSocket 连接。然后,通过监听 onopenonmessageoncloseonerror 事件来处理连接的状态和接收的数据。

  1. 在 JavaScript 中,可以使用 postMessage 方法在不同的窗口或框架之间进行跨域通信。postMessage 方法可以向目标窗口发送消息,并在目标窗口的 message 事件中进行监听和处理。

示例代码:

// 发送消息
var targetWindow = document.getElementById('target').contentWindow;
targetWindow.postMessage('Hello, World!', 'https://example.com');

// 接收消息
window.addEventListener('message', function(event) {
  if (event.origin === 'https://example.com') {
    console.log('Received message:', event.data);
  }
});

在这个例子中,通过 postMessage 方法向 ID 为 target 的窗口发送消息 'Hello, World!',并指定目标窗口的域名为 'https://example.com'。然后,在当前窗口中通过监听 message 事件来接收消息,并判断消息的来源域名是否合法。

  1. 在 JavaScript 中,可以使用 document.domain 属性来实现跨子域的通信。document.domain 属性用于设置当前页面的域名,只能设置为当前域名或其父域名的一部分。

示例代码:

// 父域名为 example.com
document.domain = 'example.com';

// 子域名为 sub.example.com
document.domain = 'sub.example.com';

在这个例子中,通过设置 document.domain 属性,将当前页面的域名设置为父域名 'example.com' 或子域名 'sub.example.com'。这样就可以实现跨子域的通信,包括跨域的 JavaScript 访问和修改父子窗口的 DOM。

  1. 在 JavaScript 中,可以使用 window.name 属性来实现跨域的通信。window.name 属性用于设置或获取窗口的名称,可以在不同的窗口之间共享数据。

示例代码:

// 在源页面中设置 window.name
window.name = 'Hello, World!';

// 在目标页面中获取 window.name
var data = window.name;
console.log(data);

在这个例子中,通过在源页面中设置 window.name 属性为 'Hello, World!',然后在目标页面中获取 window.name 属性的值,即可实现跨域的通信。

请注意,使用 window.name 进行跨域通信时,目标页面会在加载后立即获取到源页面的 window.name 值。因此,需要确保目标页面在源页面加载完成后才进行获取操作。

这些方法可以帮助我们在 JavaScript 中实现跨域请求和跨域通信。每种方法都有自己的特点和适用场景,根据具体的需求选择合适的方法来解决跨域问题。

  1. 在 JavaScript 中,可以使用 JSONP(JSON with Padding)来解决跨域请求的问题。JSONP 是一种利用 <script> 标签的跨域技术,通过动态创建 <script> 标签来请求跨域资源,并通过回调函数来处理返回的数据。

示例代码:

function handleResponse(data) {
  console.log('Received data:', data);
}

var script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleResponse';
document.body.appendChild(script);

在这个例子中,通过创建一个 <script> 标签,并将其 src 属性设置为跨域资源的 URL,并在 URL 中指定一个回调函数的名称(例如 callback=handleResponse)。然后,将 <script> 标签添加到页面的 <body> 元素中,浏览器会自动发送请求并执行返回的 JavaScript 代码。在返回的 JavaScript 代码中,会调用回调函数并传入数据作为参数。

请注意,服务器端需要将返回的数据包装在回调函数中返回,例如返回的数据为 {"name": "John"},则返回的 JavaScript 代码为 handleResponse({"name": "John"})

  1. 在 JavaScript 中,可以使用 CORS(Cross-Origin Resource Sharing)来解决跨域请求的问题。CORS 是一种基于 HTTP 头部的跨域技术,通过在服务器端设置响应头部来允许跨域请求。

示例代码:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.withCredentials = true;
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4 && xhr.status === 200) {
    var data = JSON.parse(xhr.responseText);
    console.log('Received data:', data);
  }
};
xhr.send();

在这个例子中,通过创建一个 XMLHttpRequest 对象,并调用 open 方法来指定请求的方法和 URL。然后,通过设置 withCredentials 属性为 true,表示发送跨域请求时携带凭证信息(例如 Cookie)。接着,通过监听 readystatechange 事件来处理请求的状态变化,并在状态为 4(已完成)且状态码为 200(成功)时处理返回的数据。

在服务器端,需要设置响应头部来允许跨域请求,例如设置 Access-Control-Allow-Origin 头部为允许访问的域名,设置 Access-Control-Allow-Credentials 头部为 true,表示允许携带凭证信息。

这些方法可以帮助我们在 JavaScript 中解决跨域请求的问题。根据具体的需求和服务器端的支持情况,选择合适的方法来解决跨域问题。

JavaScript算法面试题及其答案:

  1. 反转字符串
function reverseString(str) {
  return str.split('').reverse().join('');
}

console.log(reverseString('hello')); // 输出: 'olleh'
  1. 判断回文字符串
function isPalindrome(str) {
  const reversedStr = str.split('').reverse().join('');
  return str === reversedStr;
}

console.log(isPalindrome('racecar')); // 输出: true
  1. 找出最大值
function findMax(arr) {
  let max = arr[0];
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] > max) {
      max = arr[i];
    }
  }
  return max;
}

console.log(findMax([1, 3, 2, 5, 4])); // 输出: 5
  1. 计算阶乘
function factorial(num) {
  if (num === 0) {
    return 1;
  }
  return num * factorial(num - 1);
}

console.log(factorial(5)); // 输出: 120
  1. 判断两个字符串是否为同构字符串
function isIsomorphic(str1, str2) {
  if (str1.length !== str2.length) {
    return false;
  }
  const map = new Map();
  for (let i = 0; i < str1.length; i++) {
    const char1 = str1[i];
    const char2 = str2[i];
    if (map.has(char1)) {
      if (map.get(char1) !== char2) {
        return false;
      }
    } else {
      if (Array.from(map.values()).includes(char2)) {
        return false;
      }
      map.set(char1, char2);
    }
  }
  return true;
}

console.log(isIsomorphic('egg', 'add')); // 输出: true
  1. 实现斐波那契数列
function fibonacci(n) {
  const sequence = [0, 1];
  for (let i = 2; i <= n; i++) {
    const num = sequence[i - 1] + sequence[i - 2];
    sequence.push(num);
  }
  return sequence[n];
}

console.log(fibonacci(6)); // 输出: 8
  1. 判断一个数是否为素数
function isPrime(num) {
  if (num <= 1) {
    return false;
  }
  for (let i = 2; i <= Math.sqrt(num); i++) {
    if (num % i === 0) {
      return false;
    }
  }
  return true;
}

console.log(isPrime(11)); // 输出: true
  1. 实现数组去重
function removeDuplicates(arr) {
  return Array.from(new Set(arr));
}

console.log(removeDuplicates([1, 2, 2, 3, 3, 4])); // 输出: [1, 2, 3, 4]
  1. 实现数组扁平化
function flatten(arr) {
  return arr.reduce((acc, val) => Array.isArray(val) ? acc.concat(flatten(val)) : acc.concat(val), []);
}

console.log(flatten([1, [2, [3, 4], 5]])); // 输出: [1, 2, 3, 4, 5]
  1. 判断一个数是否为完全平方数
function isPerfectSquare(num) {
  let i = 1;
  while (num > 0) {
    num -= i;
    i += 2;
  }
  return num === 0;
}

console.log(isPerfectSquare(16)); // 输出: true
  1. 实现数组的快速排序
function quickSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }
  const pivotIndex = Math.floor(arr.length / 2);
  const pivot = arr.splice(pivotIndex, 1)[0];
  const left = [];
  const right = [];
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }
  return [...quickSort(left), pivot, ...quickSort(right)];
}

console.log(quickSort([3, 1, 4, 1, 5, 9, 2, 6])); // 输出: [1, 1, 2, 3, 4, 5, 6, 9]
  1. 判断一个数是否为斐波那契数
function isFibonacci(num) {
  let a = 0;
  let b = 1;
  while (b < num) {
    const temp = b;
    b = a + b;
    a = temp;
  }
  return b === num;
}

console.log(isFibonacci(8)); // 输出: true
  1. 实现数组的冒泡排序
function bubbleSort(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = 0; j < arr.length - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        const temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
  }
  return arr;
}

console.log(bubbleSort([3, 1, 4, 1, 5, 9, 2, 6])); // 输出: [1, 1, 2, 3, 4, 5, 6, 9]
  1. 判断一个数是否为质数
function isPrime(num) {
  if (num <= 1) {
    return false;
  }
  for (let i = 2; i <= Math.sqrt(num); i++) {
    if (num % i === 0) {
      return false;
    }
  }
  return true;
}

console.log(isPrime(11)); // 输出: true
  1. 实现数组的选择排序
function selectionSort(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    let minIndex = i;
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
    }
    const temp = arr[i];
    arr[i] = arr[minIndex];
    arr[minIndex] = temp;
  }
  return arr;
}

console.log(selectionSort([3, 1, 4, 1, 5, 9, 2, 6])); // 输出: [1, 1, 2, 3, 4, 5, 6, 9]
  1. 实现数组的插入排序
function insertionSort(arr) {
  for (let i = 1; i < arr.length; i++) {
    const key = arr[i];
    let j = i - 1;
    while (j >= 0 && arr[j] > key) {
      arr[j + 1] = arr[j];
      j--;
    }
    arr[j + 1] = key;
  }
  return arr;
}

console.log(insertionSort([3, 1, 4, 1, 5, 9, 2, 6])); // 输出: [1, 1, 2, 3, 4, 5, 6, 9]
  1. 实现数组的归并排序
function mergeSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }
  const mid = Math.floor(arr.length / 2);
  const left = arr.slice(0, mid);
  const right = arr.slice(mid);
  return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
  const result = [];
  let i = 0;
  let j = 0;
  while (i < left.length && j < right.length) {
    if (left[i] < right[j]) {
      result.push(left[i]);
      i++;
    } else {
      result.push(right[j]);
      j++;
    }
  }
  return result.concat(left.slice(i)).concat(right.slice(j));
}

console.log(mergeSort([3, 1, 4, 1, 5, 9, 2, 6])); // 输出: [1, 1, 2, 3, 4, 5, 6, 9]
  1. 实现数组的堆排序
function heapSort(arr) {
  const n = arr.length;

  // 构建最大堆
  for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
    heapify(arr, n, i);
  }

  // 逐个将堆顶元素与最后一个元素交换,并重新调整堆
  for (let i = n - 1; i > 0; i--) {
    const temp = arr[0];
    arr[0] = arr[i];
    arr[i] = temp;

    heapify(arr, i, 0);
  }

  return arr;
}

function heapify(arr, n, i) {
  let largest = i;
  const left = 2 * i + 1;
  const right = 2 * i + 2;

  if (left < n && arr[left] > arr[largest]) {
    largest = left;
  }

  if (right < n && arr[right] > arr[largest]) {
    largest = right;
  }

  if (largest !== i) {
    const temp = arr[i];
    arr[i] = arr[largest];
    arr[largest] = temp;

    heapify(arr, n, largest);
  }
}

console.log(heapSort([3, 1, 4, 1, 5, 9, 2, 6])); // 输出: [1, 1, 2, 3, 4, 5, 6, 9]
  1. 实现数组的计数排序
function countingSort(arr) {
  const n = arr.length;
  if (n === 0) {
    return arr;
  }

  // 找出数组中的最大值和最小值
  let min = arr[0];
  let max = arr[0];
  for (let i = 1; i < n; i++) {
    if (arr[i] < min) {
      min = arr[i];
    }
    if (arr[i] > max) {
      max = arr[i];
    }
  }

  // 统计每个元素出现的次数
  const count = new Array(max - min + 1).fill(0);
  for (let i = 0; i < n; i++) {
    count[arr[i] - min]++;
  }

  // 根据统计结果重构数组
  let index = 0;
  for (let i = 0; i < count.length; i++) {
    while (count[i] > 0) {
      arr[index++] = i + min;
      count[i]--;
    }
  }

  return arr;
}

console.log(countingSort([3, 1, 4, 1, 5, 9, 2, 6])); // 输出: [1, 1, 2, 3, 4, 5, 6, 9]
  1. 实现数组的基数排序
function radixSort(arr) {
  const n = arr.length;
  if (n === 0) {
    return arr;
  }

  // 找出数组中的最大值
  let max = arr[0];
  for (let i = 1; i < n; i++) {
    if (arr[i] > max) {
      max = arr[i];
    }
  }

  // 计算最大值的位数
  let digits = 0;
  while (max > 0) {
    max = Math.floor(max / 10);
    digits++;
  }

  // 根据每一位进行排序
  for (let i = 1; i <= digits; i++) {
    const buckets = new Array(10).fill().map(() => []);
    for (let j = 0; j < n; j++) {
      const num = Math.floor(arr[j] / Math.pow(10, i - 1)) % 10;
      buckets[num].push(arr[j]);
    }
    arr = [].concat(...buckets);
  }

  return arr;
}

console.log(radixSort([3, 1, 4, 1, 5, 9, 2, 6])); // 输出: [1, 1, 2, 3, 4, 5, 6, 9]
  1. 实现数组的希尔排序
function shellSort(arr) {
  const n = arr.length;
  let gap = Math.floor(n / 2);

  while (gap > 0) {
    for (let i = gap; i < n; i++) {
      const temp = arr[i];
      let j = i - gap;
      while (j >= 0 && arr[j] > temp) {
        arr[j + gap] = arr[j];
        j -= gap;
      }
      arr[j + gap] = temp;
    }
    gap = Math.floor(gap / 2);
  }

  return arr;
}

console.log(shellSort([3, 1, 4, 1, 5, 9, 2, 6])); // 输出: [1, 1, 2, 3, 4, 5, 6, 9]
  1. 实现数组的鸡尾酒排序
function cocktailSort(arr) {
  const n = arr.length;
  let left = 0;
  let right = n - 1;

  while (left < right) {
    for (let i = left; i < right; i++) {
      if (arr[i] > arr[i + 1]) {
        const temp = arr[i];
        arr[i] = arr[i + 1];
        arr[i + 1] = temp;
      }
    }
    right--;

    for (let i = right; i > left; i--) {
      if (arr[i] < arr[i - 1]) {
        const temp = arr[i];
        arr[i] = arr[i - 1];
        arr[i - 1] = temp;
      }
    }
    left++;
  }

  return arr;
}

console.log(cocktailSort([3, 1, 4, 1, 5, 9, 2, 6])); // 输出: [1, 1, 2, 3, 4, 5, 6, 9]
  1. 实现数组的二分查找
function binarySearch(arr, target) {
  let left = 0;
  let right = arr.length - 1;

  while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    if (arr[mid] === target) {
      return mid;
    } else if (arr[mid] < target) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }

  return -1;
}

console.log(binarySearch([1, 2, 3, 4, 5, 6, 9], 4)); // 输出: 3
  1. 实现数组的插入操作
function insert(arr, index, value) {
  arr.splice(index, 0, value);
  return arr;
}

console.log(insert([1, 2, 3, 4, 5], 2, 6)); // 输出: [1, 2, 6, 3, 4, 5]
  1. 实现数组的删除操作
function remove(arr, index) {
  arr.splice(index, 1);
  return arr;
}

console.log(remove([1, 2, 3, 4, 5], 2)); // 输出: [1, 2, 4, 5]
  1. 实现数组的替换操作
function replace(arr, index, value) {
  arr[index] = value;
  return arr;
}

console.log(replace([1, 2, 3, 4, 5], 2, 6)); // 输出: [1, 2, 6, 4, 5]
  1. 实现数组的合并操作
function mergeArrays(arr1, arr2) {
  return [...arr1, ...arr2];
}

console.log(mergeArrays([1, 2, 3], [4, 5, 6])); // 输出: [1, 2, 3, 4, 5, 6]
  1. 实现数组的切片操作
function slice(arr, start, end) {
  return arr.slice(start, end);
}

console.log(slice([1, 2, 3, 4, 5], 1, 3)); // 输出: [2, 3]
  1. 实现数组的去重操作
function unique(arr) {
  return [...new Set(arr)];
}

console.log(unique([1, 2, 3, 3, 4])); // 输出: [1, 2, 3, 4]
  1. 实现数组的反转操作
function reverse(arr) {
  return arr.reverse();
}

console.log(reverse([1, 2, 3, 4, 5])); // 输出: [5, 4, 3, 2, 1]
  1. 实现数组的拼接操作
function concat(arr1, arr2) {
  return arr1.concat(arr2);
}

console.log(concat([1, 2, 3], [4, 5, 6])); // 输出: [1, 2, 3, 4, 5, 6]
  1. 实现数组的填充操作
function fill(arr, value, start = 0, end = arr.length) {
  return arr.fill(value, start, end);
}

console.log(fill([1, 2, 3, 4, 5], 0, 1, 3)); // 输出: [1, 0, 0, 4, 5]
  1. 实现数组的扁平化操作
function flatten(arr) {
  return arr.flat();
}

console.log(flatten([1, [2, [3, [4, 5]]]])); // 输出: [1, 2, 3, 4, 5]
  1. 实现数组的求和操作
function sum(arr) {
  return arr.reduce((a, b) => a + b, 0);
}

console.log(sum([1, 2, 3, 4, 5])); // 输出: 15
  1. 实现数组的平均值操作
function average(arr) {
  return sum(arr) / arr.length;
}

console.log(average([1, 2, 3, 4, 5])); // 输出: 3
  1. 实现数组的最大值操作
function max(arr) {
  return Math.max(...arr);
}

console.log(max([1, 2, 3, 4, 5])); // 输出: 5
  1. 实现数组的最小值操作
function min(arr) {
  return Math.min(...arr);
}

console.log(min([1, 2, 3, 4, 5])); // 输出: 1
  1. 实现数组的乱序操作
function shuffle(arr) {
  return arr.sort(() => Math.random() - 0.5);
}

console.log(shuffle([1, 2, 3, 4, 5])); // 输出: [3, 2, 1, 4, 5]
  1. 实现数组的随机取样操作
function sample(arr, size) {
  const shuffled = shuffle(arr);
  return shuffled.slice(0, size);
}

console.log(sample([1, 2, 3, 4, 5], 3)); // 输出: [3, 2, 1]
  1. 实现数组的差集操作
function difference(arr1, arr2) {
  return arr1.filter((item) => !arr2.includes(item));
}

console.log(difference([1, 2, 3, 4, 5], [2, 4])); // 输出: [1, 3, 5]

这是一些常见的数组操作,希望对你有帮助!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一花一world

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值