一、对象
1.认识对象
在JavaScript中,对象(Object)是一种复合数据类型,它允许你存储键值对。对象的属性是连接到对象的变量,而函数或方法是属于对象的函数。
JavaScript中的对象类似于哈希表,其中键可以是字符串或符号,值可以是任意类型的值,包括其他对象。对象字面量是用花括号 {}
包围的,其中冒号 :
用于分隔键和值,逗号 ,
用于分隔不同的键值对。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var xiaoming = {
name: '小明',
age: 12,
sex: '男',
hobbies: ['足球', '编程'],
'favorite-book': '舒克和贝塔'
};
console.log(xiaoming.name);
console.log(xiaoming.age);
console.log(xiaoming.sex);
console.log(xiaoming.hobbies);
console.log(xiaoming['favorite-book']);
var key = 'sex';
console.log(xiaoming[key]);
console.log();
Math.ceil();
</script>
</body>
</html>
对象属性的修改
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var obj = {
a: 10,
b: 20
};
// 对象属性的修改
obj.b = 40;
obj.b++;
console.log(obj.b);
// 对象属性的增加
obj.c = 60;
console.log(obj);
// 对象属性的删除
delete obj.a;
console.log(obj);
</script>
</body>
</html>
2.对象的方法
在JavaScript中,面向对象的方法通常是在构造函数或者类中定义的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var xiaoming = {
name: '小明',
age: 12,
sex: '男',
sayHello: function () {
console.log('你好我是小明,我12岁了,我是一个男生');
},
sleep: function () {
console.log('小明开始睡觉zzzzz');
}
};
var xiaohong = {
name: '小红',
age: 11,
sex: '女',
sayHello: function () {
console.log('你好我是小红,我11岁了,我是一个女生');
},
sleep: function () {
console.log('小红开始睡觉zzzzz');
}
};
xiaoming.sayHello();
xiaohong.sayHello();
xiaohong.sleep();
</script>
</body>
</html>
3.遍历对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var obj = {
a: 11,
b: 22,
c: 88
};
for (var k in obj) {
console.log('对象obj的属性' + k + '的值是' + obj[k]);
}
</script>
</body>
</html>
4.对象的深浅克隆
对象是引用类型
在 JavaScript 中,对象是通过引用传递的,这意味着当我们将一个对象赋值给另一个变量时,我们实际上是在复制该对象的引用,而不是复制整个对象。因此,当我们改变通过引用访问的对象时,所有对该对象的引用都会看到这种变化。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 例子1
var obj1 = {
a: 1,
b: 2,
c: 3
};
var obj2 = {
a: 1,
b: 2,
c: 3
};
console.log(obj1 == obj2); // false
console.log(obj1 === obj2); // false
console.log({} == {}); // false
console.log({} === {}); // false
// 例子2
var obj3 = {
a: 10
};
var obj4 = obj3;
obj3.a ++;
console.log(obj4); // {a: 11}
</script>
</body>
</html>
在JavaScript中,对象的克隆可以通过浅克隆和深克隆来实现。以下是两种克隆方法的实现方式:
浅克隆
浅克隆只会复制对象的第一层属性,如果属性值是基本类型,则拷贝的是基本类型的值;如果属性值是引用类型,则拷贝的是内存地址,因此如果原始对象或克隆对象中的引用类型属性发生变化,另一个对象也会受到影响。
以下是实现浅克隆的一种简单方法:
function shallowClone(obj) {
const cloneObj = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = obj[key];
}
}
return cloneObj;
}
const original = { a: 1, b: { c: 2 } };
const cloned = shallowClone(original);
console.log(cloned); // { a: 1, b: { c: 2 } }
cloned.b.c = 3;
console.log(original); // { a: 1, b: { c: 3 } } // 原始对象也被修改了
<script>
var obj1 = {
a: 1,
b: 2,
c: [44, 55, 66]
};
// 实现浅克隆
var obj2 = {};
for (var k in obj1) {
// 每遍历一个k属性,就给obj2也添加一个同名的k属性
// 值和obj1的k属性值相同
obj2[k] = obj1[k];
}
// 为什么叫浅克隆呢?比如c属性的值是引用类型值,那么本质上obj1和obj2的c属性是内存中的同一个数组,并没有被克隆分开。
obj1.c.push(77);
console.log(obj2); // obj2的c属性这个数组也会被增加77数组
console.log(obj1.c == obj2.c); // true,true就证明了数组是同一个对象
</script>
深克隆
深克隆会复制对象的所有层级属性。对于每个引用类型的属性,都会创建一个新的对象或数组,从而不会相互影响。
以下是实现深克隆的一种方法,它考虑了数组和对象,但并不包含对函数、循环引用或特殊对象(如Date、RegExp等)的处理:
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return null; // 处理null值
if (obj instanceof Date) return new Date(obj); // 处理日期
if (obj instanceof RegExp) return new RegExp(obj); // 处理正则
if (typeof obj !== 'object') return obj; // 如果不是复杂数据类型,直接返回
// 如果是对象或数组,先检查hash中是否克隆过,解决循环引用问题
if (hash.has(obj)) {
return hash.get(obj);
}
// 初始化返回结果,保证数组和对象的原型不丢失
let cloneObj = Array.isArray(obj) ? [] : {};
// 将对象或数组存入hash中
hash.set(obj, cloneObj);
// 遍历对象的key
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
// 递归克隆每个值
cloneObj[key] = deepClone(obj[key], hash);
}
}
// 返回克隆后的对象
return cloneObj;
}
const original = { a: 1, b: { c: 2, d: { e: 3 } } };
const cloned = deepClone(original);
console.log(cloned); // { a: 1, b: { c: 2, d: { e: 3 } } }
cloned.b.d.e = 4;
console.log(original); // { a: 1, b: { c: 2, d: { e: 3 } } } // 原始对象未被修改
需要注意的是,上述深克隆方法并不完美,它没有处理函数、循环引用、特殊对象类型等复杂情况。在实际应用中,你可能需要使用更完善的库,比如 lodash
的 _.cloneDeep
方法,来处理这些情况。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var obj1 = {
a: 1,
b: 2,
c: [33, 44, {
m: 55,
n: 66,
p: [77, 88]
}]
};
// 深克隆
function deepClone(o) {
// 要判断o是对象还是数组
if (Array.isArray(o)) {
// 数组
var result = [];
for (var i = 0; i < o.length; i++) {
result.push(deepClone(o[i]));
}
} else if (typeof o == 'object') {
// 对象
var result = {};
for (var k in o) {
result[k] = deepClone(o[k]);
}
} else {
// 基本类型值
var result = o;
}
return result;
}
var obj2 = deepClone(obj1);
console.log(obj2);
console.log(obj1.c == obj2.c); // false
obj1.c.push(99);
console.log(obj2); // obj2不变的,因为没有“藕断丝连”的现象
obj1.c[2].p.push(999);
console.log(obj2); // obj2不变的,因为没有“藕断丝连”的现象
</script>
</body>
</html>
二、认识函数上下文
这个this.a+this.b=? 不是3 而是不知道 为什么不知道呢 因为函数只有被调用的时候才知道上下文。如果是obj.fn()那么答案是3;
但如果是var fn =obj.fn;
fn();
还是输出NaN不知道;因为圆括号直接调用,函数中的this指代windows对象。
<script>
var obj = {
a: 1,
b: 2,
fn: function() {
console.log(this.a + this.b);
console.log(this === window);
}
};
// var a = 4;
// var b = 9;
obj.fn();
console.log('---------');
var fn = obj.fn; //不加圆括号,代表提炼函数本身
fn();
</script>