原文网址:https://www.nowcoder.com/discuss/353158719997419520?sourceSSR=search
浅拷贝只复制对象的第一层属性,不会递归复制嵌套对象。
使用Object.assign()
Object.assign()
静态方法将一个或者多个源对象中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象。
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// Expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget === target);
// Expected output: true
使用展开运算符
let obj1 = {
name: "jack",
age: 18
}
let obj2 = {}
obj2 = { ...obj1 }
console.log(obj2)
输出
{
"name": "jack",
"age": 18
}
深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性
const _ = require('lodash');
const obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
const obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
let newobj = JSON.parse(JSON.stringify(obj))//深拷贝
JSON.stringify()
方法将一个 JavaScript 对象或值转换为 JSON 字符串
JSON.parse()
方法用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象
但是这种方式存在弊端,会忽略undefined
、symbol
和函数
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象;
2.如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象;
3.如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4.如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5.JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6.如果对象中存在循环引用的情况也无法正确实现深拷贝;
function checkType(target){
return Object.prototype.toString.call(target).slice(8,-1)
}
function deepClone(data){
let type = checkType(data)
let obj
if(type === 'Array'){
obj = []
for(let i = 0; i < data.length; i++){
obj.push(deepClone(data[i]))
}
}else if(type === 'Object'){
obj = {}
for(const key in data){
if(data.hasOwnProperty(key)){
obj[key] = deepClone(data[key])
}
}
}else{
return data
}
return obj
}
事件委托(Event delegation)是一种事件处理模式,它将事件处理程序绑定到一个父元素上,以代替直接在子元素上进行事件绑定。当事件触发时,事件会冒泡到父元素,然后由父元素的事件处理程序来处理。
举个例子,假设有一个HTML列表,其中包含多个列表项(li元素)。如果要为每个列表项添加点击事件处理程序,传统的做法是为每个列表项分别绑定事件处理程序。而采用事件委托的方式,可以将事件处理程序绑定到整个列表的父元素上,然后通过事件冒泡机制来处理每个列表项的点击事件。
- Item 1
- Item 2
- Item 3
- Item 4
为什么要将事件绑定在父元素而不是子元素呢?有以下几个原因:
Promise(承诺)是JavaScript中用于处理异步操作的对象。它代表了一个尚未完成但最终会完成的操作,并可以获取其结果或错误。
Promise有三种状态:
当创建一个Promise对象时,可以传入一个执行器函数(executor function),该函数接受两个参数:resolve和reject。通过调用resolve函数,可以将Promise从Pending状态转变为Fulfilled状态,并传递一个值作为操作的结果。通过调用reject函数,可以将Promise从Pending状态转变为Rejected状态,并传递一个错误对象作为操作的原因。
Promise对象具有then方法,它接受两个回调函数作为参数:onFulfilled和onRejected。当Promise状态变为Fulfilled时,会调用onFulfilled回调函数,并传递操作结果作为参数;当Promise状态变为Rejected时,会调用onRejected回调函数,并传递错误对象作为参数。
Promise还提供了其他方法,例如catch方法用于捕获错误、finally方法用于指定无论Promise状态如何都要执行的回调函数等。
Promise的主要优势在于它简化了处理异步操作的流程和提供了更好的代码结构。它可以避免回调地狱(callback hell)的问题,使异步操作更易于理解和组织。通过使用Promise,可以将异步代码写成连续的链式调用,使其更具可读性和可维护性。
对于axios来说,它返回的是一个Promise对象,可以通过调用then方法来处理异步请求的结果,或者通过catch方法来捕获可能发生的错误。这样可以更方便地进行异步请求的处理和错误处理。
按照套路背就好了
在Vue.js 2中,key
属性用于辨识和跟踪在使用v-for
指令时渲染的DOM元素的身份。它在Vue的虚拟DOM算法中起着重要的作用。
当使用v-for
指令循环渲染一个数据列表时,Vue会生成一组DOM元素来表示每个列表项。为了优化性能和提高渲染效率,Vue会尽可能地重用已经存在的DOM元素,而不是重新创建新的元素。
这就引出了一个问题:如何判断两个元素是否是相同的,并且可以重用?这就是key
属性的作用。Vue使用key
属性来标识每个生成的DOM元素的唯一性。它们不是组件的属性,而是Vue特定的属性。
当数据发生变化,Vue会比较新的数据和旧的数据,并根据key
属性来判断哪些元素需要被更新、重用或删除。如果两个元素具有相同的key
,Vue会假定它们是相同的元素,从而重用之前的DOM元素,避免不必要的重新渲染。
key
属性的原理可以总结如下:
1.key属性必须是唯一的,通常使用具有唯一标识的数据来生成。
2.在使用v-for指令渲染列表时,为每个列表项添加一个key属性。
3.当数据更新时,Vue会比较新旧数据,并根据key属性来确定哪些元素需要进行重新渲染、重用或删除。
4.如果两个元素具有相同的key,Vue会假定它们是相同的元素,并重用之前的DOM元素。
通过合理使用key
属性,可以最大限度地提高Vue应用的性能和渲染效率,特别是在涉及列表渲染的情况下。
key是虚拟DOM对象的标识,当数据发生变化的时候,vue会根据新数据生成新的虚拟DOM,随后进行新虚拟DOM和旧的虚拟DOM比较,比较规则如下:
1.旧虚拟DOM中找到了与新虚拟DOM相同的key:
若虚拟DOM中内容没有发生改变,则直接使用之前的真实DOM
若虚拟DOM中的内容发生改变,则生成新的真实DOM,替换掉页面中真实的DOM
2.旧虚拟DOM中未找到与新虚拟DOM相同的key:
创建新的真实DOM,随后渲染到页面上
1.若对数据进行逆序添加、逆序删除等破坏顺序的操作:
会产生没有必要的真实DOM更新,界面效果没问题,但是效率低下
2.如果结构中还包含输入类的DOM:
会产生错误的DOM更新,界面有问题
Vue Router提供了两种路由模式:history模式和hash模式。它们在URL的表现形式和浏览器兼容性上有所不同。
http://example.com/#/about
。这种模式的好处是它在所有现代浏览器中都能正常工作,因为改变hash部分不会导致浏览器向服务器发送请求。但是,它的缺点是URL中带有冗余的”#"符号,不够美观,可能不太友好。http://example.com/about
。这种模式看起来更加干净和友好。它通过使用HTML5的History API来管理URL,可以动态地修改URL而不刷新页面。然而,history模式在一些较旧的浏览器上可能不被支持,因此需要服务器的支持来处理路由请求。什么时候使用History模式?
什么时候使用Hash模式?
不会向服务器发送请求,直接从缓存中读取资源,从chrome控制台中的network选项中可以看到该请求返回的状态码是200
在使用本地缓存之前,需要向服务器发送请求,服务器会根据这个请求的request header中的一些参数来判断是否命中协商缓存,如果名字,则返回304的状态码并带上新的response header通知浏览器从缓存中读取资源,协商缓存可以解决强制缓存的情况下资源不更新的问题
Expires:response header里的过期时间
Cache-Control:当值设为max-age=数字时,则代表这个请求返回资源的缓存时间
Cache-Control除了该字段外,还有下面几个比较常用的设置值:
no-cache:不使用本地缓存。需要使用协商缓存,先与服务器确认返回的响应是否被修改,如果之前的响应中存在ETag,那么请求的时候会与服务器验证,如果资源未被修改,则可以避免重新下载
no-store:禁止使用浏览器缓存
public:可以被所有用户缓存,包括终端用户和CDN等中间代理服务器
private:只能被终端用户的浏览器缓存,不允许CDN等中间缓存服务器缓存
Cache-Control:no-cache
Last-Modify/If-Modify/Since:浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-Modify是一个时间标识该资源的最后修改时间,当浏览器再次请求资源时,request的请求头中会包含If-Modify-Since,该值为缓存之前返回的Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存。 因为Last-Modify的单位是秒,所以1秒钟之内被修改多次是不知道的,所以就有了下面的ETag
Etag/If-none-Match:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识。web服务器收到的请求会根据收到的哈希值进行比较判断资源是否被修改
跨域(Cross-Origin)是指在前端网页中,当一个请求的源(Origin)与当前页面的源不同,即两个网页的域名、协议或端口不一致时,就会发生跨域问题。
由于安全原因,现代浏览器限制了跨域请求的访问。这是因为如果没有跨域限制,恶意网站就可以通过在用户浏览器中执行脚本来获取其他网站的数据,从而导致安全问题。
要实现跨域请求,有几种常见的方法:
JSONP(JSON with Padding):利用标签没有跨域限制的特性,通过动态创建
标签来请求其他域的数据。但是 JSONP 只能用于 GET 请求,且需要后端支持返回包裹在函数调用中的 JSON 数据。
CORS(Cross-Origin Resource Sharing):CORS 是一种现代浏览器提供的机制,通过在请求和响应头部添加特定的字段,允许跨域访问。CORS 使用预检请求(OPTIONS 请求)来检查服务器是否允许特定的跨域请求,并且支持各种类型的请求方法(GET、POST 等)。
CORS 的原理是在请求和响应的头部添加特定的字段来告知浏览器是否允许跨域访问。当浏览器发起跨域请求时,会首先发送一个 OPTIONS 预检请求,以检查服务器是否支持跨域请求。如果服务器返回的响应中包含允许跨域的头部字段,浏览器就会继续发送实际的请求。
CORS 的关键头部字段包括:
Origin
:指示请求的源,即当前页面的域名。Access-Control-Allow-Origin
:指示服务器允许的跨域请求源。可以设置为具体的域名或使用通配符 *
表示允许任意域名的请求。Access-Control-Allow-Methods
:指示服务器允许的请求方法。Access-Control-Allow-Headers
:指示服务器允许的额外请求头部字段。Access-Control-Allow-Credentials
:指示是否允许发送跨域请求的凭据(如 Cookie、HTTP 认证)。代理:通过在同一域名下设置代理服务器,将跨域请求转发到目标服务器,然后将响应返回给前端。这种方法需要在后端进行相应的配置。
在跨域请求中,根据请求的类型和内容的复杂程度,可以将请求分为简单请求(Simple Request)和复杂请求(Complex Request)两种。
简单请求满足以下条件:
简单请求的特点是在发送请求时,浏览器会自动处理跨域请求,不会发送预检请求(OPTIONS 请求),而是直接发送实际请求。简单请求不会触发跨域安全机制的预检步骤,因此服务器的响应头部不需要特殊设置。
对于复杂请求,当请求不满足简单请求的条件时,浏览器会自动发送一个预检请求(OPTIONS 请求)到服务器,以获取服务器允许的跨域规则。
复杂请求的特点包括:
预检请求中会包含一个Access-Control-Request-Method
字段,用于指示实际请求的方法。服务器在收到预检请求后,会根据该字段判断是否允许实际请求。如果服务器允许跨域请求,会在预检请求的响应头部中设置允许跨域的字段,然后浏览器才会发送实际的请求。
需要注意的是,预检请求是在浏览器自动发送的,并且预检请求和实际请求是分开发送的,因此可能会导致额外的网络开销和延迟。为了避免频繁的预检请求,服务器可以在响应头部中设置Access-Control-Max-Age
字段,指定预检请求的缓存时间,减少预检请求的发送频率。
需要注意的是,浏览器会自动处理简单请求和复杂请求的跨域安全机制,但服务器端也需要进行相应的配置,确保返回正确的响应头部字段,以便浏览器正确处理跨域请求。
实现登录有多种方式,以下是几种常见的方式:
每种登录方式都有其优点和缺点,下面是它们的简要介绍: