【面试题】JavaScript基础高频面试(下)

 10、Javascript 闭包是什么,闭包形成的原因和闭包的用途 ?

闭包(Closure)是 JavaScript 中的一个非常重要的概念。简单地说,闭包就是一个函数能够访问另一个函数的作用域。这是因为在 JavaScript 中,函数是一等公民,可以作为参数传递,也可以作为返回值返回。当一个函数 A 返回另一个函数 B,并且这个函数 B 中使用了函数 A 的变量,那么这时候就产生了闭包。

闭包形成的原因主要是因为 JavaScript 的词法作用域(lexical scoping)机制。词法作用域意味着函数的作用域在函数定义的时候就已经确定了,而不是在函数调用的时候确定。因此,即使外部函数已经返回,闭包依然能够访问外部函数的变量。

闭包的用途很广泛,主要有以下几点:

1. 模拟私有变量:在 JavaScript 中,我们没有真正意义上的私有变量,但我们可以通过闭包来模拟私有变量。例如:

```javascript
function createCounter() {
  let count = 0; // 这个变量在外部是无法访问的
  return function() {
    return ++count;
  };
}

let counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
```

在这个例子中,`count` 变量对于外部是不可见的,只有通过 `counter` 函数才能访问和修改。

2. 延长局部变量的生命周期:正常情况下,函数的局部变量在函数执行完之后就会被销毁。但是,如果我们创建了一个闭包,并且这个闭包引用了一些局部变量,那么这些局部变量就会一直存在,直到闭包被销毁。这可以用来保存函数的状态。

3. 实现某些设计模式,如模块模式(Module Pattern):使用闭包可以实现一个模块具有公开的方法和隐藏的私有方法或变量,这是模块模式的基础。

以上就是关于 JavaScript 闭包的一些基本解释和应用场景。希望对您有所帮助!

11、Javascript 跨域的解决方案有哪些?

跨域问题是由于浏览器的同源策略导致的。同源策略是一种约定,它是浏览器为了安全性而采取的策略,即浏览器要求所有的请求必须与当前页面的协议、主机名和端口号完全一致,否则就视为跨域。

解决跨域问题的常见方法有以下几种:

1. **JSONP**:JSONP 是一种常用的跨域数据交互的方法。它通过动态插入 script 标签来获取数据,因为 script 标签的 src 属性不受同源策略的限制。但是 JSONP 只能发送 GET 请求,且不安全。

2. **CORS**:跨源资源共享(CORS)是一种现代并且安全的跨域请求技术。服务器可以在响应头中添加一些 CORS 相关的头信息,如 `Access-Control-Allow-Origin`,来告诉浏览器允许特定的跨域请求。

3. **代理服务器**:通过服务器来转发请求和响应,因为服务器端没有同源策略的限制。例如,在 Node.js 中,我们可以使用 http-proxy-middleware 这样的中间件来实现。

4. **使用 WebSocket**:WebSocket 是一种通讯协议,不受同源策略的限制,可以用来实现跨域通信。

5. **使用 postMessage API**:HTML5 引入的 postMessage API 可以实现跨域通信。两个窗口(或者 iframe 和其父窗口)可以通过 postMessage 和 onmessage 实现数据的传递。

6. **使用 document.domain**:这种方法只能用于二级域名相同的情况。

7. **使用 window.name**:window 对象有一个 name 属性,这个属性在页面跳转时不会改变,可以通过它来传递数据。

8. **使用 location.hash**:通过改变 URL 的 hash(# 后面的部分)来传递数据,这种方法通常用于 iframe 间的通信。

以上就是一些常见的解决跨域问题的方法,具体使用哪一种方法,需要根据具体的应用场景和需求来决定。

12、Http协议详解 Http请求方式有 Http响应状态码 ?

**HTTP协议**:HTTP(HyperText Transfer Protocol)是一种无状态的、应用层的协议,主要用于在用户端(通常是 Web 浏览器)和服务器端之间传输数据。HTTP 是基于 TCP/IP 协议的,它通过请求和响应的方式来进行通信。

**HTTP 请求方法**:HTTP 定义了一组请求方法,也被称为“动词”,用来描述对资源的不同操作:

1. **GET**:获取资源。
2. **POST**:提交数据,通常会改变服务器的状态。
3. **PUT**:更新资源。
4. **DELETE**:删除资源。
5. **HEAD**:类似于 GET,但是只返回 HTTP 头部信息,不返回实体内容。
6. **OPTIONS**:获取资源支持的操作类型。
7. **PATCH**:对资源进行部分修改。

**HTTP 响应状态码**:HTTP 响应状态码用来表示服务器对请求的处理结果。常见的有:

1. **1xx(信息响应)**:表示请求已被接收,需要继续处理。
2. **2xx(成功)**:表示请求已成功被服务器接收、理解、并接受。
   - 200 OK:请求成功。
3. **3xx(重定向)**:需要后续操作才能完成请求。
   - 301 Moved Permanently:资源永久性转移。
   - 302 Found:资源临时性转移。
4. **4xx(客户端错误)**:表示请求含有语法错误或者无法被服务器执行。
   - 400 Bad Request:请求语法错误。
   - 401 Unauthorized:请求需要认证。
   - 403 Forbidden:服务器拒绝请求。
   - 404 Not Found:请求的资源无法找到。
5. **5xx(服务器错误)**:表示服务器在处理请求的过程中发生了错误。
   - 500 Internal Server Error:服务器内部错误。
   - 503 Service Unavailable:服务器暂时无法处理请求。

13、JavaScript什么是长连接 ?

长连接(也被称为持久连接、keep-alive连接或者连接保持)是一种通信机制,它允许客户端和服务器在一个连接上发送多个请求和响应,而不需要为每个请求/响应对创建新的连接。这种机制可以显著地降低服务器的负载,提高资源的使用率。

在 HTTP/1.0 中,每一个 HTTP 请求/响应对都需要建立一个新的 TCP 连接,这会带来很大的开销。而在 HTTP/1.1 中,引入了长连接的概念,允许在一个连接上进行多次 HTTP 交互,直到客户端或者服务器主动关闭连接。

在 JavaScript 中,我们可以使用 XMLHttpRequest 或 Fetch API 发送 HTTP 请求,它们默认都会使用长连接。此外,我们还可以使用 WebSocket 或 Server-Sent Events 来实现真正的双向长连接,这两种技术都允许服务器主动向客户端推送数据。

例如,WebSocket 可以用来实现实时聊天、多人游戏、实时数据更新等功能。在这些场景中,服务器需要能够随时向客户端推送新的数据,而不需要客户端每次都发送请求。WebSocket 通过在客户端和服务器之间建立一个持久的、全双工的连接,使得数据可以在任何时间点从任一方向传输。

14、display:none和visibility:hidden的区别是 ?

`display: none` 和 `visibility: hidden` 都可以用来隐藏 HTML 元素,但是它们之间有一些重要的区别:

1. **空间占用**:当元素被设置为 `display: none` 时,这个元素会从文档流中完全移除,就像它从来没有存在过一样。它不会占据任何空间,也不会影响到其他元素的布局。而当元素被设置为 `visibility: hidden` 时,这个元素虽然不可见,但是它依然会占据空间,依然会参与布局。

2. **对子元素的影响**:`display: none` 会影响到元素的所有子元素,如果一个元素被设置为 `display: none`,那么它的所有子元素也都会被隐藏,无论子元素的 `display` 属性是什么。而 `visibility: hidden` 不会影响到子元素的 `visibility` 属性,也就是说,如果一个元素被设置为 `visibility: hidden`,它的子元素依然可以通过设置 `visibility: visible` 来显示。

3. **对事件的影响**:被设置为 `display: none` 的元素不会响应任何事件,例如鼠标点击事件。而被设置为 `visibility: hidden` 的元素依然可以响应事件,例如,即使一个按钮被设置为 `visibility: hidden`,用户依然可以通过 Tab 键导航到这个按钮,并使用 Enter 键来触发点击事件。

以上就是 `display: none` 和 `visibility: hidden` 的主要区别。总的来说,`display: none` 更像是“删除”元素,而 `visibility: hidden` 更像是“隐藏”元素。

15、JavaScript中常用的数组方法?

JavaScript 中的数组有许多内置的方法可以帮助我们操作数组。以下是一些常用的数组方法:

1. **push()**:在数组的末尾添加一个或多个元素,并返回新的长度。

```javascript
let arr = ['a', 'b', 'c'];
arr.push('d'); // 返回 4
console.log(arr); // 输出 ['a', 'b', 'c', 'd']
```

2. **pop()**:删除并返回数组的最后一个元素。

```javascript
let arr = ['a', 'b', 'c'];
let last = arr.pop(); // 返回 'c'
console.log(arr); // 输出 ['a', 'b']
```

3. **shift()**:删除并返回数组的第一个元素。

```javascript
let arr = ['a', 'b', 'c'];
let first = arr.shift(); // 返回 'a'
console.log(arr); // 输出 ['b', 'c']
```

4. **unshift()**:在数组的开头添加一个或多个元素,并返回新的长度。

```javascript
let arr = ['a', 'b', 'c'];
arr.unshift('0'); // 返回 4
console.log(arr); // 输出 ['0', 'a', 'b', 'c']
```

5. **splice()**:在数组中添加或删除元素。

```javascript
let arr = ['a', 'b', 'c'];
arr.splice(1, 0, 'x'); // 在索引为1的位置插入'x'
console.log(arr); // 输出 ['a', 'x', 'b', 'c']
```

6. **slice()**:返回一个新的数组,包含从 start 到 end(不包括 end)的数组元素。

```javascript
let arr = ['a', 'b', 'c', 'd', 'e'];
let newArr = arr.slice(1, 3); // 返回 ['b', 'c']
```

7. **sort()**:对数组的元素进行排序。

```javascript
let arr = ['c', 'a', 'b'];
arr.sort(); 
console.log(arr); // 输出 ['a', 'b', 'c']
```

8. **reverse()**:颠倒数组中元素的顺序。

```javascript
let arr = ['a', 'b', 'c'];
arr.reverse();
console.log(arr); // 输出 ['c', 'b', 'a']
```

9. **join()**:将所有的数组元素连接成一个字符串。

```javascript
let arr = ['a', 'b', 'c'];
let str = arr.join('-'); // 返回 'a-b-c'
```

10. **map()**:创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

```javascript
let arr = [1, 2, 3];
let newArr = arr.map(x => x * 2); // 返回 [2, 4, 6]
```

11. **filter()**:创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。

```javascript
let arr = [1, 2, 3, 4, 5];
let newArr = arr.filter(x => x > 3); // 返回 [4, 5]
```

12. **reduce()**:对数组中的每个元素执行一个由您提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。

```javascript
let arr = [1, 2, 3, 4, 5];
let sum = arr.reduce((acc, cur) => acc + cur, 0); // 返回 15
```

以上就是 JavaScript 中常用的数组方法。

16、手写防抖、节流,防抖和节流的区别 ?

**防抖(debounce)**:如果一个函数持续地触发,那么只在它停止触发的一段时间后才执行,如果在这段时间内又开始持续触发,则重新计算时间。

防抖函数的实现如下:

```javascript
function debounce(func, wait) {
  let timeout;
  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(this, arguments);
    }, wait);
  };
}
```

**节流(throttle)**:如果一个函数持续地触发,那么固定在一段时间内只执行一次。

节流函数的实现如下:

```javascript
function throttle(func, wait) {
  let lastTime = 0;
  return function() {
    let now = Date.now();
    if (now - lastTime > wait) {
      func.apply(this, arguments);
      lastTime = now;
    }
  };
}
```

**防抖和节流的区别**:

- 防抖是让连续触发的函数在一段时间后只执行一次,如果在这段时间内又触发了该函数,则重新计算时间。适用场景:文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)。
- 节流是让连续触发的函数在一段时间内只执行一次,并且这段时间内的多次触发只会计算一次。适用场景:滚动加载,时间间隔内只加载一次,模拟鼠标移动(mousemove),监听滚动事件(比如是否滑到底部自动加载更多,用 throttle 是为了降低频率)。

17、Javascipt的call和apply的区别 ?

`call` 和 `apply` 都是 Function 对象的方法,它们都可以用来改变函数的 `this` 上下文并立即调用这个函数。它们的主要区别在于参数的传递方式:

1. **call** 方法接受的是参数列表,第一个参数是 `this` 的值,之后是传递给函数的参数。例如:

```javascript
function greet(name, age) {
  console.log(`Hello, my name is ${name} and I am ${age} years old.`);
}

greet.call(, 'Alice', 25);  // 输出 "Hello, my name is Alice and I am 25 years old."
```

2. **apply** 方法接受的是一个参数数组,第一个参数同样是 `this` 的值,第二个参数是一个数组,其中包含了传递给函数的参数。例如:

```javascript
function greet(name, age) {
  console.log(`Hello, my name is ${name} and I am ${age} years old.`);
}

greet.apply(, ['Alice', 25]);  // 输出 "Hello, my name is Alice and I am 25 years old."
```

在 ES6 中,你还可以使用扩展运算符(spread operator)和 `apply` 达到和 `call` 相同的效果:

```javascript
greet.apply(, ['Alice', 25]);  // 使用 apply
greet(...['Alice', 25]);  // 使用扩展运算符,效果和上面一样
```

总的来说,`call` 和 `apply` 的功能是相同的,只是参数的传递方式不同。你可以根据实际需求选择使用哪一个。

18、JavaScript 闭包是什么,有什么特性,对页面有什么影响?简要介绍你理解的闭包?

闭包是JavaScript中一种非常重要的概念,它的定义可能有点抽象:闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见方式就是在一个函数内部创建另一个函数。

闭包的特性主要包括以下几点:

1. 函数嵌套:外部函数中嵌套内部函数,内部函数可以访问外部函数的变量和参数。
2. 变量引用:即使外部函数已经返回,内部函数仍然可以引用外部函数的变量和参数。
3. 内存消耗:由于内部函数保持了对外部函数变量的引用,所以这些变量不会被垃圾收集器回收,可能会导致内存消耗。

闭包的影响主要体现在以下几个方面:

1. 数据封装和私有成员:通过闭包,我们可以创建私有变量,防止外部访问,达到数据封装和保护的目的。
2. 持久化变量:闭包可以使得函数中的变量在函数执行完毕后仍然保存在内存中,可用于在不同函数调用间保持状态。

举个例子说明闭包:

```javascript
function outerFunction() {
    var count = 0;
    function innerFunction() {
        count++;
        console.log(count);
    }
    return innerFunction;
}

var instance = outerFunction();
instance();  // 输出:1
instance();  // 输出:2
```

在这个例子中,`outerFunction`返回了`innerFunction`,并且`innerFunction`引用了`outerFunction`的`count`变量。即使`outerFunction`已经执行完毕,但是由于`innerFunction`对`count`的引用,`count`变量仍然存在,每次调用`instance()`,`count`都会增加并打印出来。这就是闭包的一个典型的应用场景。
 

  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值