JavaScript高频题整理(附答案背诵版)
1、简述JavaScript中map和foreach的区别?
map
和forEach
都是JavaScript数组的迭代方法,但它们之间存在一些关键区别。
-
返回值:
map
方法会返回一个新的数组,这个新数组是由原数组通过某个函数处理后的结果组成的。而forEach
方法则没有返回值,它仅仅是对数组中的每个元素执行一次给定的函数。 -
修改原数组:
forEach
方法可以直接修改原数组,而map
方法则不会改变原数组,而是创建一个全新的数组。 -
性能:在大多数现代浏览器中,
map
的性能通常优于forEach
。
让我们来看一个例子:
let arr = [1, 2, 3, 4, 5];
// 使用map
let mapResult = arr.map(num => num * 2);
console.log(mapResult); // 输出:[2, 4, 6, 8, 10]
console.log(arr); // 输出:[1, 2, 3, 4, 5]
// 使用forEach
let forEachResult = [];
arr.forEach(num => forEachResult.push(num * 2));
console.log(forEachResult); // 输出:[2, 4, 6, 8, 10]
console.log(arr); // 输出:[1, 2, 3, 4, 5]
在这个例子中,我们可以看到,尽管map
和forEach
都可以对数组中的每个元素执行同样的操作(乘以2),但map
返回了一个新的数组,原数组并没有改变;而forEach
则没有返回值,我们需要创建一个新的数组来存储结果。同时,原数组在forEach
操作后也没有发生改变。
2、解释下JavaScript中this是如何工作的?
在 JavaScript 中,this
关键字是一个特殊的变量,它在每个函数作用域内都有定义。它的值取决于函数如何被调用。
- 全局作用域或函数调用: 在全局作用域或者普通函数调用中,
this
指向全局对象,也就是window
(在浏览器中)或者global
(在Node.js中)。
console.log(this); // 输出:Window {...}
function test() {
console.log(this);
}
test(); // 输出:Window {...}
- 作为对象方法调用: 当函数作为对象的一个方法被调用时,
this
指向这个对象。
const obj = {
name: 'Alice',
sayHello: function() {
console.log(this.name);
}
};
obj.sayHello(); // 输出:Alice
- 作为构造函数调用: 当使用
new
关键字调用函数时,this
指向新创建的对象。
function Person(name) {
this.name = name;
}
let alice = new Person('Alice');
console.log(alice.name); // 输出:Alice
- 在事件处理函数中: 在 DOM 事件处理函数中,
this
通常指向触发事件的元素。
button.addEventListener('click', function() {
console.log(this); // 输出:触发点击事件的button元素
});
- 箭头函数: 箭头函数没有自己的
this
,它会捕获其所在(即定义的位置)上下文的this
值。
const obj = {
name: 'Alice',
sayHello: function() {
setTimeout(() => {
console.log(this.name); // 输出:Alice
}, 1000);
}
};
obj.sayHello();
- 使用call,apply,bind调用: 使用
call
,apply
或bind
方法,可以设置函数运行时的this
值。
function greet() {
console.log(`Hello, ${this.name}`);
}
const alice = { name: 'Alice' };
const bob = { name: 'Bob' };
greet.call(alice); // 输出:Hello, Alice
greet.call(bob); // 输出:Hello, Bob
总的来说,this
的值是在函数被调用时确定的,而不是在函数被定义时确定。这就是 JavaScript 中的动态作用域。
3、JavaScript阻止事件冒泡的方法?
在JavaScript中,阻止事件冒泡可以使用事件对象的stopPropagation
方法。当事件发生时,浏览器会创建一个事件对象,这个对象包含了与事件相关的各种信息和方法,其中就包括stopPropagation
方法。
这个方法可以阻止当前事件继续向上层元素传播,也就是停止事件冒泡。
我们来看一个例子:
document.querySelector("#child").addEventListener('click', function(event) {
event.stopPropagation();
console.log("Child element clicked!");
});
document.querySelector("#parent").addEventListener('click', function() {
console.log("Parent element clicked!");
});
在这个例子中,当你点击ID为child
的元素时,浏览器会首先执行该元素的点击事件处理函数,然后因为我们调用了event.stopPropagation()
,事件就不会继续向上冒泡到父元素,也就是ID为parent
的元素。所以,你只会看到控制台打印出"Child element clicked!",而不会看到"Parent element clicked!"。
需要注意的是,stopPropagation
只能阻止事件向上冒泡,但不能阻止其他同级事件监听器的执行。如果你希望完全阻止事件的进一步传播,包括阻止其他同级事件监听器的执行,你可以使用event.stopImmediatePropagation()
方法。
4、JavaScript阻止默认事件?
在JavaScript中,阻止默认事件可以使用事件对象的preventDefault
方法。很多浏览器的事件都有默认的行为,例如点击链接会跳转到新的页面,提交表单会刷新页面等。如果我们不希望触发这些默认行为,就可以使用preventDefault
方法。
下面是一个例子,展示了如何阻止链接的默认跳转行为:
document.querySelector("a").addEventListener('click', function(event) {
event.preventDefault();
console.log("Link clicked, but default action is prevented.");
});
在这个例子中,当你点击链接时,浏览器会首先执行链接的点击事件处理函数。然后因为我们调用了event.preventDefault()
,链接的默认跳转行为就被阻止了。所以,你会看到控制台打印出"Link clicked, but default action is prevented.",但页面并不会跳转到链接的目标地址。
需要注意的是,不是所有的事件都有默认行为,只有部分事件才有。对于没有默认行为的事件,调用preventDefault
方法没有任何效果。另外,一些事件的默认行为无法被取消,例如页面的unload事件。对于这些事件,调用preventDefault
方法也没有任何效果。
5、简述 Javascript 盒子模型?
在 Web 开发中,CSS 盒模型是用来布局和设计的基本概念。在 CSS 盒模型中,每个元素都被视为一个矩形的盒子,这个盒子具有宽度、高度、边距、填充和边框。
盒模型主要包含四个部分:
-
内容(Content): 这是盒子里面的实际内容,如文本、图片等。其尺寸可以通过
width
和height
属性来设置。 -
内边距(Padding): 内边距是内容周围的空白区域,它清晰地隔离了内容和边框。内边距的大小可以通过
padding
属性来设置。 -
边框(Border): 边框就像是盒子的外壳,它包围了内容和内边距。边框的大小和样式可以通过
border
属性来设置。 -
外边距(Margin): 外边距是盒子和其他元素之间的空白区域。它在边框的外面,用来隔离盒子和其他元素。外边距的大小可以通过
margin
属性来设置。
在 CSS 中,盒模型有两种:标准盒模型和IE盒模型。
-
标准盒模型: 在这个模型中,
width
和height
指的是内容区的宽度和高度,而不包括内边距、边框和外边距。总的盒子大小计算公式为:总宽度 = width + padding-left + padding-right + border-left + border-right + margin-left + margin-right
,高度同理。 -
IE盒模型: 在这个模型中,
width
和height
指的是内容区、内边距和边框的总宽度和高度。外边距不包括在内。总的盒子大小计算公式为:总宽度 = width + margin-left + margin-right
,高度同理。
可以使用 CSS 的 box-sizing
属性来选择使用哪种盒模型,content-box
为标准盒模型,border-box
为IE盒模型。
6、Javascipt中async await 和promise和generator有什么区别?
async/await
、Promise
和 generator
都是 JavaScript 中用于处理异步操作的工具,但它们的使用方式和机制各有不同。
Promise: Promise 是 JavaScript 中处理异步操作的一个对象。它有三种状态:pending(待定)、fulfilled(已成功)和 rejected(已失败)。Promise 在创建时处于 pending 状态,然后可能转换为 fulfilled 或 rejected 状态。一旦状态改变,就不能再次改变。Promise 可以使用 then
和 catch
方法来处理成功或失败的结果。
let promise = new Promise((resolve, reject) => {
// 异步操作
if (/* 成功 */) {
resolve(value);
} else {
reject(error);
}
});
promise.then(value => {
// 处理成功的结果
}).catch(error => {
// 处理错误
});
Generator: Generator 是 ES6 引入的一种新的函数类型,它可以在执行过程中被暂停和恢复。Generator 使用 yield
关键字来暂停函数的执行,使用 next
方法来恢复执行。Generator 可以用于处理异步操作,但需要配合其他工具(如 Promise)使用。
function* myGenerator() {
const data = yield doAsyncOperation();
console.log(data);
}
const generator = myGenerator();
generator.next().value.then(data => {
generator.next(data);
});
Async/Await: Async/Await 是 ES7 引入的一种新的处理异步操作的方式,它是基于 Promise 的。Async/Await 使用 async
和 await
关键字,使得异步代码看起来像同步代码。这大大提高了代码的可读性和可维护性。
async function myAsyncFunction() {
try {
const data = await doAsyncOperation();
console.log(data);
} catch (error) {
// 处理错误
}
}
myAsyncFunction();
总的来说,Promise 提供了一种在异步操作完成后处理结果的方式,Generator 提供了一种可以暂停和恢复的函数,而 Async/Await 则提供了一种更简洁、更直观的处理异步操作的方式。
7、JavaScript中手写promise ?
在 JavaScript 中,Promise 是一个代表了异步操作最终完成或者失败的对象。我们可以手动创建一个 Promise,如下所示:
function MyPromise(executor) {
let self = this;
self.status = 'pending'; // Promise 当前的状态
self.value = undefined; // Promise 的值
self.reason = undefined; // Promise 失败的原因
// 成功
function resolve(value) {
if (self.status === 'pending') {
self.status = 'fulfilled';
self.value = value;
}
}
// 失败
function reject(reason) {
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
}
}
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
MyPromise.prototype.then = function(onFulfilled, onRejected) {
let self = this;
if (self.status === 'fulfilled') {
onFulfilled(self.value);
}
if (self.status === 'rejected') {
onRejected(self.reason);
}
}
这是一个最基础的 Promise 实现,它包含了 Promise 的基本逻辑。但是,这个实现还缺少很多真正的 Promise 具备的功能,比如链式调用、异步支持、错误处理、catch
方法、Promise.all
方法、Promise.race
方法等等。
在实际开发中,我们通常使用内置的 Promise 对象,因为它已经实现了完整的功能,并且经过了大量的测试和优化。
8、JavaScript中promise.all作用?
Promise.all
是 JavaScript 中的一个 Promise 方法,它接收一个 Promise 对象的数组作为参数,返回一个新的 Promise 对象。这个新的 Promise 对象只有在所有的 Promise 对象都成功地完成(fulfilled)时才会完成,如果有任何一个 Promise 对象失败(rejected)了,那么新的 Promise 对象会立即失败。
在所有的 Promise 对象都成功完成时,新的 Promise 对象的结果会是一个数组,这个数组包含了每个 Promise 对象的结果。这些结果的顺序和原来的 Promise 对象的顺序一致。
如果有任何一个 Promise 对象失败了,新的 Promise 对象的结果会是那个失败的 Promise 对象的结果。
下面是一个例子:
let promise1 = Promise.resolve(3);
let promise2 = 42;
let promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(values => {
console.log(values); // 输出:[3, 42, "foo"]
});
在这个例子中,Promise.all
接收了三个 Promise 对象,当这三个 Promise 对象都成功完成时,它返回的 Promise 对象也成功完成,结果是一个包含了每个 Promise 对象结果的数组。
Promise.all
在处理多个相互独立的异步操作,并且需要等待所有异步操作都完成时非常有用。
9、Javascript 浅拷贝/深度拷贝的区别?
在 JavaScript 中,浅拷贝和深拷贝都是用来复制对象的,但它们复制的深度不同。
浅拷贝(Shallow Copy): 浅拷贝只复制对象的顶层属性。如果对象的属性值是基本类型(如数字、字符串、布尔值),那么就直接复制这个值;如果属性值是引用类型(如对象、数组),那么复制的是这个值的引用,而不是实际的对象或数组。这就意味着,如果你修改了新对象的一个引用类型的属性,那么原对象的对应属性也会被修改。
let obj1 = { a: 1, b: [1, 2, 3] };
let obj2 = {...obj1};
obj2.b.push(4);
console.log(obj1.b); // 输出:[1, 2, 3, 4]
在这个例子中,我们使用了对象扩展运算符(...)来创建一个新的对象,这是一种浅拷贝的方式。当我们修改了新对象的 b 属性时,原对象的 b 属性也被修改了。
深拷贝(Deep Copy): 深拷贝不仅复制对象的顶层属性,还会递归地复制所有的子属性。无论属性值是基本类型还是引用类型,都会创建一个新的副本。这就意味着,新对象和原对象完全独立,修改其中一个不会影响另一个。
let obj1 = { a: 1, b: [1, 2, 3] };
let obj2 = JSON.parse(JSON.stringify(obj1));
obj2.b.push(4);
console.log(obj1.b); // 输出:[1, 2, 3]
在这个例子中,我们使用了 JSON.stringify
和 JSON.parse
方法来创建一个新的对象,这是一种深拷贝的方式。当我们修改了新对象的 b 属性时,原对象的 b 属性没有被修改。
需要注意的是,使用 JSON.stringify
和 JSON.parse
方法进行深拷贝有一些限制,比如无法复制函数和循环引用的对象等。在实际开发中,我们通常会使用一些库(如 lodash)的深拷贝函数,因为这些函数已经处理了各种边缘情况。
10、Javascript 闭包是什么,闭包形成的原因和闭包的用途 ?
闭包(Closure)是 JavaScript 中的一个非常重要的概念。简单地说,闭包就是一个函数能够访问另一个函数的作用域。这是因为在 JavaScript 中,函数是一等公民,可以作为参数传递,也可以作为返回值返回。当一个函数 A 返回另一个函数 B,并且这个函数 B 中使用了函数 A 的变量,那么这时候就产生了闭包。
闭包形成的原因主要是因为 JavaScript 的词法作用域(lexical scoping)机制。词法作用域意味着函数的作用域在函数定义的时候就已经确定了,而不是在函数调用的时候确定。因此,即使外部函数已经返回,闭包依然能够访问外部函数的变量。
闭包的用途很广泛,主要有以下几点:
- 模拟私有变量:在 JavaScript 中,我们没有真正意义上的私有变量,但我们可以通过闭包来模拟私有变量。例如:
function createCounter() {
let count = 0; // 这个变量在外部是无法访问的
return function() {
return ++count;
};
}
let counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
在这个例子中,count
变量对于外部是不可见的,只有通过 counter
函数才能访问和修改。
-
延长局部变量的生命周期:正常情况下,函数的局部变量在函数执行完之后就会被销毁。但是,如果我们创建了一个闭包,并且这个闭包引用了一些局部变量,那么这些局部变量就会一直存在,直到闭包被销毁。这可以用来保存函数的状态。
-
实现某些设计模式,如模块模式(Module Pattern):使用闭包可以实现一个模块具有公开的方法和隐藏的私有方法或变量,这是模块模式的基础。
以上就是关于 JavaScript 闭包的一些基本解释和应用场景。希望对您有所帮助!
11、Javascript 跨域的解决方案有哪些?
跨域问题是由于浏览器的同源策略导致的。同源策略是一种约定,它是浏览器为了安全性而采取的策略,即浏览器要求所有的请求必须与当前页面的协议、主机名和端口号完全一致,否则就视为跨域。
解决跨域问题的常见方法有以下几种:
-
JSONP:JSONP 是一种常用的跨域数据交互的方法。它通过动态插入 script 标签来获取数据,因为 script 标签的 src 属性不受同源策略的限制。但是 JSONP 只能发送 GET 请求,且不安全。
-
CORS:跨源资源共享(CORS)是一种现代并且安全的跨域请求技术。服务器可以在响应头中添加一些 CORS 相关的头信息,如
Access-Control-Allow-Origin
,来告诉浏览器允许特定的跨域请求。 -
代理服务器:通过服务器来转发请求和响应,因为服务器端没有同源策略的限制。例如,在 Node.js 中,我们可以使用 http-proxy-middleware 这样的中间件来实现。
-
使用 WebSocket:WebSocket 是一种通讯协议,不受同源策略的限制,可以用来实现跨域通信。
-
使用 postMessage API:HTML5 引入的 postMessage API 可以实现跨域通信。两个窗口(或者 iframe 和其父窗口)可以通过 postMessage 和 onmessage 实现数据的传递。
-
使用 document.domain:这种方法只能用于二级域名相同的情况。
-
使用 window.name:window 对象有一个 name 属性,这个属性在页面跳转时不会改变,可以通过它来传递数据。
-
使用 location.hash:通过改变 URL 的 hash(# 后面的部分)来传递数据,这种方法通常用于 iframe 间的通信。
以上就是一些常见的解决跨域问题的方法,具体使用哪一种方法,需要根据具体的应用场景和需求来决定。
12、Http协议详解 Http请求方式有 Http响应状态码 ?
HTTP协议:HTTP(HyperText Transfer Protocol)是一种无状态的、应用层的协议,主要用于在用户端(通常是 Web 浏览器)和服务器端之间传输数据。HTTP 是基于 TCP/IP 协议的,它通过请求和响应的方式来进行通信。
HTTP 请求方法:HTTP 定义了一组请求方法,也被称为“动词”,用来描述对资源的不同操作:
- GET:获取资源。
- POST:提交数据,通常会改变服务器的状态。
- PUT:更新资源。
- DELETE:删除资源。
- HEAD:类似于 GET,但是只返回 HTTP 头部信息,不返回实体内容。
- OPTIONS:获取资源支持的操作类型。
- PATCH:对资源进行部分修改。
HTTP 响应状态码:HTTP 响应状态码用来表示服务器对请求的处理结果。常见的有:
- 1xx(信息响应):表示请求已被接收,需要继续处理。
- 2xx(成功):表示请求已成功被服务器接收、理解、并接受。
- 200 OK:请求成功。
- 3xx(重定向):需要后续操作才能完成请求。
- 301 Moved Permanently:资源永久性转移。
- 302 Found:资源临时性转移。
- 4xx(客户端错误):表示请求含有语法错误或者无法被服务器执行。
- 400 Bad Request:请求语法错误。
- 401 Unauthorized:请求需要认证。
- 403 Forbidden:服务器拒绝请求。
- 404 Not Found:请求的资源无法找到。
- 5xx(服务器错误):表示服务器在处理请求的过程中发生了错误。
- 500 Internal Server Error:服务器内部错误。
- 503 Service Unavailable:服务器暂时无法处理请求。
以上就是对 HTTP 协议、请求方法和响应状态码的简单介绍。
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 元素,但是它们之间有一些重要的区别:
-
空间占用:当元素被设置为
display: none
时,这个元素会从文档流中完全移除,就像它从来没有存在过一样。它不会占据任何空间,也不会影响到其他元素的布局。而当元素被设置为visibility: hidden
时,这个元素虽然不可见,但是它依然会占据空间,依然会参与布局。 -
对子元素的影响:
display: none
会影响到元素的所有子元素,如果一个元素被设置为display: none
,那么它的所有子元素也都会被隐藏,无论子元素的display
属性是什么。而visibility: hidden
不会影响到子元素的visibility
属性,也就是说,如果一个元素被设置为visibility: hidden
,它的子元素依然可以通过设置visibility: visible
来显示。 -
对事件的影响:被设置为
display: none
的元素不会响应任何事件,例如鼠标点击事件。而被设置为visibility: hidden
的元素依然可以响应事件,例如,即使一个按钮被设置为visibility: hidden
,用户依然可以通过 Tab 键导航到这个按钮,并使用 Enter 键来触发点击事件。
以上就是 display: none
和 visibility: hidden
的主要区别。总的来说,display: none
更像是“删除”元素,而 visibility: hidden
更像是“隐藏”元素。
15、JavaScript中常用的数组方法?
JavaScript 中的数组有许多内置的方法可以帮助我们操作数组。以下是一些常用的数组方法:
- push():在数组的末尾添加一个或多个元素,并返回新的长度。
let arr = ['a', 'b', 'c'];
arr.push('d'); // 返回 4
console.log(arr); // 输出 ['a', 'b', 'c', 'd']
- pop():删除并返回数组的最后一个元素。
let arr = ['a', 'b', 'c'];
let last = arr.pop(); // 返回 'c'
console.log(arr); // 输出 ['a', 'b']
- shift():删除并返回数组的第一个元素。
let arr = ['a', 'b', 'c'];
let first = arr.shift(); // 返回 'a'
console.log(arr); // 输出 ['b', 'c']
- unshift():在数组的开头添加一个或多个元素,并返回新的长度。
let arr = ['a', 'b', 'c'];
arr.unshift('0'); // 返回 4
console.log(arr); // 输出 ['0', 'a', 'b', 'c']
- splice():在数组中添加或删除元素。
let arr = ['a', 'b', 'c'];
arr.splice(1, 0, 'x'); // 在索引为1的位置插入'x'
console.log(arr); // 输出 ['a', 'x', 'b', 'c']
- slice():返回一个新的数组,包含从 start 到 end(不包括 end)的数组元素。
let arr = ['a', 'b', 'c', 'd', 'e'];
let newArr = arr.slice(1, 3); // 返回 ['b', 'c']
- sort():对数组的元素进行排序。
let arr = ['c', 'a', 'b'];
arr.sort();
console.log(arr); // 输出 ['a', 'b', 'c']
- reverse():颠倒数组中元素的顺序。
let arr = ['a', 'b', 'c'];
arr.reverse();
console.log(arr); // 输出 ['c', 'b', 'a']
- join():将所有的数组元素连接成一个字符串。
let arr = ['a', 'b', 'c'];
let str = arr.join('-'); // 返回 'a-b-c'
- map():创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
let arr = [1, 2, 3];
let newArr = arr.map(x => x * 2); // 返回 [2, 4, 6]
- filter():创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
let arr = [1, 2, 3, 4, 5];
let newArr = arr.filter(x => x > 3); // 返回 [4, 5]
- reduce():对数组中的每个元素执行一个由您提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。
let arr = [1, 2, 3, 4, 5];
let sum = arr.reduce((acc, cur) => acc + cur, 0); // 返回 15
以上就是 JavaScript 中常用的数组方法。
16、手写防抖、节流,防抖和节流的区别 ?
防抖(debounce):如果一个函数持续地触发,那么只在它停止触发的一段时间后才执行,如果在这段时间内又开始持续触发,则重新计算时间。
防抖函数的实现如下:
function debounce(func, wait) {
let timeout;
return function() {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, arguments);
}, wait);
};
}
节流(throttle):如果一个函数持续地触发,那么固定在一段时间内只执行一次。
节流函数的实现如下:
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
上下文并立即调用这个函数。它们的主要区别在于参数的传递方式:
- call 方法接受的是参数列表,第一个参数是
this
的值,之后是传递给函数的参数。例如:
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."
- apply 方法接受的是一个参数数组,第一个参数同样是
this
的值,第二个参数是一个数组,其中包含了传递给函数的参数。例如:
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
相同的效果:
greet.apply(, ['Alice', 25]); // 使用 apply
greet(...['Alice', 25]); // 使用扩展运算符,效果和上面一样
总的来说,call
和 apply
的功能是相同的,只是参数的传递方式不同。你可以根据实际需求选择使用哪一个。
18、JavaScript 闭包是什么,有什么特性,对页面有什么影响?简要介绍你理解的闭包?
闭包是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
都会增加并打印出来。这就是闭包的一个典型的应用场景。
19、阐述Javascript的同源策略?
同源策略(Same-origin policy)是一种重要的安全策略,它被用于限制从同一源加载的文档或脚本如何与来自其他源的资源进行交互。这是一种防止恶意行为的重要安全机制。
在JavaScript中,如果两个网页的协议,端口(如果有的话)和主机都相同,那么这两个网页属于同源。这意味着http://www.example.com/dir/page1.html 和 http://www.example.com/dir/page2.html 是同源的。
但是,以下例子则不属于同源:
- http://www.example.com 和 https://www.example.com (协议不同)
- http://www.example.com:80 和 http://www.example.com:81 (端口不同)
- http://www.example.com 和 http://en.example.com (主机不同)
同源策略的主要影响包括:
- Cookie、LocalStorage 和 IndexDB 无法读写。
- DOM 无法获得和操作。
- AJAX 请求不能发送。
当然,也有一些方法可以在不同源的情况下进行数据通信,比如 JSONP、CORS(跨源资源共享)等。
比如,CORS是一种W3C标准,它允许服务器使用特定的HTTP头来告诉浏览器,它允许来自其他源的请求。这样,即使是不同源,我们也可以在满足CORS策略的条件下,进行数据通信。
由于内容太多,更多内容以链接形势给大家,点击进去就是答案了
22. Javascript null和undefined的区别?
23. Javascript中callee和caller的作用?
27. Javascript 描述以下变量的区别:null,undefined或undeclared?
28. 请指出JavaScript宿主对象和原生对象的区别?
30. 请指出document.onload和document.ready两个事件的区别?
32. JavaScript里函数参数arguments是数组吗?
33. Javascript 什么是use strict?使用它的好处和坏处分别是什么?
35. 简述在Javascript中什么是伪数组?如何将伪数组转化为标准数组?
36. JavaScript中的split、slice、splice函数区别?
40. JavaScrpit隐式类型强制有什么作用?举个例子 ?
45. 解释为什么要在 JavaScript 中使用严格模式?
46. JavaScript语言中preventDefault() 方法有什么作用?
48. 简述JavaScript标签中 defer和 async属性的区别 ?
49. 简述为什么不建议在 JavaScript中使用 innerHTML?
52. 简述documen.wrte和 innerHTML的区别是什么?
54. 列出不同浏览器中关于 JavaScript兼容性的两个常见问题 ?
55. JavaScript语言中ViewState和 SessionState有什么区别?
56. 如何在 JavaScript中将base字符串转换为 integer?
57. 解释JavaScript void(0)的作用是什么?
59. 请区分解释 window. onload和 onDocumentReady?
60. 简述JavaScript什么是构造函数?它与普通函数有什么区别?
64. 使用 typeof bar===" object"可以确定bar是不是对象的潜在陷阱,如何避免这个陷阱?
70. JavaScript freeze() 方法有什么作用?
71. JavaScript NoScript标签有什么作用?
73. JavaScript 创建“原生”(native)方法 ?
76. 简述JS判断数据类型的方法有哪四种?(列出四种即可)?
77. 简述Javascript数组怎么去重有哪些?(方法) ?
79. 简述Set、Map、WeakSet 和 WeakMap 的区别 ?