常见知识点总结

本节主要讲述常见知识点总结

一、js的数据类型有哪些和区别

(1)两种数据类型:
基本数据类型:null, undefined, number, string, Boolean
引用数据类型:Array, Object, Function, Date
(2)区别:
基本数据类型存储在栈中,直接存值,赋值的时候是相对独立的。

1
2
3
4
例:`var a = 10;
var b = a;
b = 20;
console.log(a); // 10 `

引用数据类型存储在堆中,存的是内存地址,赋值的时候相当于是赋值内存地址

1
2
3
4
例:`var obj1 = new Object();
var obj2 = obj1;
obj2.name = "danny";
console.log(obj1.name); // danny`

二、 js的原型和原型链

A. 每个对象都有 proto 属性,但只有函数对象才有 prototype 属性,指向他的原型对象Person.prototype,原型对象(Person.prototype)是构造函数(Person)的一个实例。
Person.prototype.constructor == Person


B.通过new创建实例person1,实例有constructor属性,指向他的构造函数Person。
person1.constructor == Person


C. 所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person)
Person.prototype.constructor == Person


D. 原型链:JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做proto 的内置属性,用于指向创建它的构造函数的原型对象

三、对象的深度拷贝

使用递归的方法:

1
2
3
4
5
6
7
8
9
10
11
function clone(obj) {
var o = obj instanceof Array ? [] : {};
for (var k in obj)
o[k] = typeof obj[k] === Object ? clone(obj[k]) : obj[k];
return o;
}
let obj = {name: 'danny'}
const objCopy = clone(obj)
obj.age = '18'
console.log(obj) // {name: "danny", age: "18"}
console.log(objCopy) // {name: "danny"}

备注:
1、使用JSON.parse(JSON.stringify(obj))可以实现一些部分情况下的深拷贝,但是如果对象中有undefined、function、symbol 会在转换过程中被忽略,则无法使用
2、Object.assign只能实现浅拷贝(源对象的属性值发现改变,则跟着变)

彻底搞清楚深拷贝、浅拷贝

一、为什么有深拷贝和浅拷贝?

 这个要从js中的数据类型说起,js中数据类型分为**基本数据类型**和**引用数据类型**。

`基本类型值`指的是那些保存在**栈**内存中的简单数据段,即这种值是完全保存在内存中的一个位置。包含`Number`,`String`,`Boolean`,`Null`,`Undefined` ,`Symbol`。

`引用类型值`指的是那些保存在**堆**内存中的对象,所以引用类型的值保存的是一个指针,这个指针指向存储在**堆**中的一个对象。除了上面的 6 种基本数据类型外,剩下的就是引用类型了,统称为 `Object` 类型。细分的话,有:`Object` 类型、`Array` 类型、`Date` 类型、`RegExp` 类型、`Function` 类型 等。

正因为引用类型的这种机制, 当我们从一个变量向另一个变量复制引用类型的值时,实际上是将这个引用类型在**栈**内存中的引用地址复制了一份给新的变量,其实就是一个**指针**。因此当操作结束后,这两个变量实际上指向的是同一个在**堆**内存中的对象,改变其中任意一个对象,另一个对象也会跟着改变。

图片描述

因此深拷贝和浅拷贝只发生在**引用类型**中。简单来说他们的区别在于:

1. 层次

  • 浅拷贝 只会将对象的各个属性进行依次复制,并不会进行递归复制,也就是说只会赋值目标对象的第一层属性。
  • 深拷贝不同于浅拷贝,它不只拷贝目标对象的第一层属性,而是递归拷贝目标对象的所有属性。

2. 是否开辟新的栈

  • 浅拷贝 对于目标对象第一层为基本数据类型的数据,就是直接赋值,即「传值」;而对于目标对象第一层为引用数据类型的数据,就是直接赋存于栈内存中的堆内存地址,即「传址」,并没有开辟新的栈,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变,
  • 深拷贝 而深复制则是开辟新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。

二、浅拷贝

以下是实现浅拷贝的几种实现方式:

1.Array.concat()

1
2
3
4
5
6
7
8
9
10
const arr = [1,2,3,4,[5,6]];
const copy = arr.concat(); \\ 利用concat()创建arr的副本

\\改变基本类型值,不会改变原数组
copy[0] = 2;
arr; //[1,2,3,4,[5,6]];

\\改变数组中的引用类型值,原数组也会跟着改变
copy[4][1] = 7;
arr; //[1,2,3,4,[5,7]];

能实现类似效果的还有slice()和Array.from()等,大家可以自己尝试一下~

2.Object.assign()

1
2
3
4
5
6
const obj1 = {x: 1, y: 2};
const obj2 = Object.assign({}, obj1);

obj2.x = 2; \\修改obj2.x,改变对象中的基本类型值
console.log(obj1) //{x: 1, y: 2} //原对象未改变
console.log(obj2) //{x: 2, y: 2}
1
2
3
4
5
6
7
8
9
10
11
const obj1 = {
x: 1,
y: {
m: 1
}
};
const obj2 = Object.assign({}, obj1);

obj2.y.m = 2; \\修改obj2.y.m,改变对象中的引用类型值
console.log(obj1) //{x: 1, y: {m: 2}} 原对象也被改变
console.log(obj2) //{x: 2, y: {m: 2}}

三、深拷贝

1.JSON.parse()和JSON.stringify()

1
2
3
4
5
6
7
8
9
10
11
12
13
const obj1 = {
x: 1,
y: {
m: 1
}
};
const obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1) //{x: 1, y: {m: 1}}
console.log(obj2) //{x: 1, y: {m: 1}}

obj2.y.m = 2; //修改obj2.y.m
console.log(obj1) //{x: 1, y: {m: 1}} 原对象未改变
console.log(obj2) //{x: 2, y: {m: 2}}

这种方法使用较为简单,可以满足基本日常的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是有以下几个缺点:

  • undefined、任意的函数、正则表达式类型以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时);
  • 它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;
  • 如果对象中存在循环引用的情况无法正确处理。

2.递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function deepCopy1(obj) {
// 创建一个新对象
let result = {}
let keys = Object.keys(obj),
key = null,
temp = null;

for (let i = 0; i < keys.length; i++) {
key = keys[i];
temp = obj[key];
// 如果字段的值也是一个对象则递归操作
if (temp && typeof temp === 'object') {
result[key] = deepCopy(temp);
} else {
// 否则直接赋值给新对象
result[key] = temp;
}
}
return result;
}

const obj1 = {
x: {
m: 1
},
y: undefined,
z: function add(z1, z2) {
return z1 + z2
},
a: Symbol("foo")
};

const obj2 = deepCopy1(obj1);
obj2.x.m = 2;

console.log(obj1); //{x: {m: 1}, y: undefined, z: ƒ, a: Symbol(foo)}
console.log(obj2); //{x: {m: 2}, y: undefined, z: ƒ, a: Symbol(foo)}

跨域

JSONP 、CORS、postMessage

跨域概念解释:当前发起请求的域与该请求指向的资源所在的域不一样。这里的域指的是这样的一个概念:我们认为若协议 + 域名 + 端口号均相同,那么就是同域。

如下表
图

JSONP实现

1
2
3
4
5
6
7
8
9
var script = document.createElement('script');
script.type = 'text/javascript';

script.src = "'http://www.domain2.com:8080/login?user=admin&callback=onBack';";
document.head.appendChild(script);

function onBack(res) {
alert(JSON.stringify(res));
}

CORS:跨源资源共享 Cross-Origin Resource Sharing(CORS),通常服务器设置