一、对象的创建和应用
1、之前学过的创建方法
1、字面量创建法:
var obj={a:1,b:2};
console.log(obj);
2、构造函数创建法:
var obj=new Object();
obj.a=1; //增加属性
obj.b=2;
2、Object.create()方法创建
1、根据空对象创建:
var obj1=Object.create({});
2、根据已有对象创建:
var obj1=Object.create(obj);
console.log(obj2,obj1);
3、获取,会先查找该对象下有没有该属性,如果没有就去原型链中查找:
console.log("obj1.b:"+obj1.b);
打印查看结果:根据obj创建的obj1,继承于obj的属性被放在了__protp__原型链属性中。
4、_ _ proto _ _:原型链属性
被放在原型链下的属性,叫原型属性;
直接放在对象下的属性,叫对象属性;
如果想要修改原型链属性,只需要修改最顶端的对象的属性即可;比如obj.b = -10;obj1与obj2原型链中的b都会变成-10 。
如果该对象有对象属性,就可以直接获取对象属性;如果没有对象属性就会获取距离该对象最近的原型链属性。
设置属性时,只会设置对象属性,不能设置原型链属性。
obj1.b = 10;
console.log(obj1);
console.log("obj1.b="+obj1.b); //obj1.b=10
var obj2=Object.create(obj1);
console.log(obj2);
原型、原型链其实是一个东西,只是针对的东西不一样。原型针对类,原型链针对对象,但他们的引用完全相等。
原型是指类中用来描述该类实际特征、属性、方法的;
如果直接在类上写属性 、方法,是静态方法;而如果用原型,上面所描述的属性、方法,实例化后,就可以通过实例来调用该方法。
原型链是实例化类以后的对象,会自动拥有类自身的原型。
有篇文章,介绍原型、原型链的,我觉得挺好:js原型和原型链,以及__proto__、prototype属性
5、禁止使用这种方式进行修改:
obj2.__proto__.__proto__.b=-100; //绝对不允许
console.log(obj);
console.log(obj1);
console.log(obj2);
6、原型链中的属性不能被直接删除:
var obj = { a: 1, b: 5 };
var obj1 = Object.create(obj);
delete obj1.b;
console.log(obj1);
7、对象属性可以直接删除;原型链中的属性可以获取到:
var obj = { a: 1, b: 5 };
var obj1 = Object.create(obj);
var bn = document.querySelector("button");
bn.addEventListener("click", clickHandler);
function clickHandler(e) {
obj1.b++;
// if(obj1.b>=10) obj1.b=obj1.__proto__.b; //可以获取原型链里的属性,重新赋值
if (obj1.b >= 10) delete obj1.b; //删除对象属性,下次还是会去原型链中查找最近的
this.textContent = obj1.b;
}
结果:按钮从无到有,从5到9,在5~9之间来回变换
8、对象属性的删除
var obj={};
var obj1={b:2};
obj.a=obj1; // obj1作为obj.a一个对象属性
delete obj.a; //删除对象属性,但没有删除引用关系
obj1=null; //这样才彻底删除引用关系
但有种情况是没法使用上面的方法删除的:
var obj={a:{b:2}};
// 这个时候用delete直接删掉obj.a的话,obj.a是会变成孤儿对象,造成内存引用泄漏;
// 但下面的就可以了:先删除引用关系,再把这个对象属性删掉
obj.a=null;
delete obj.a;
3、Object.assign()方法创建
Object.assign(目标对象,源对象1,源对象2,… ); 返回目标对象;
Object.create()通过原型创建对象,把另一个对象作为原型放入新对象的原型链中,可以创建对象;
Object.assign()是不能创建对象的,必须拥有目标对象才能创建;
<1> obj1不存在,也可以被直接创建
var obj={a:1};
var obj1=Object.create(obj);
<2> obj1必须先创建存在
var obj1={};
obj1=Object.assign(obj1,obj);
// 或者使用下面的写法,先创建了一个空对象,然后将obj复制到这个空对象上:
var obj1=Object.assign({},obj); // 这个空对象和obj1引用相同
1、所以Object.assign只能复制对象属性,不能复制对象的原型链属性
2、只能复制可枚举类型
3、引用关系也会一同被复制,只能浅复制
4、多个源对象复制时,如果有相同的属性,则后面的将会覆盖前面的属性
var obj={a:1,b:2};
var obj1={b:3,c:10};
var obj2 = Object.assign({}, obj, obj1);
console.log(obj2); //{a: 1, b: 3, c: 10}
// 其他用法
Object.assign(div.style,{
// 我们之前不是经常这么写样式嘛
})
二、对象的属性定义 & 获取
1、之前我们用的对象属性定义方法
obj.a=3;
obj["b"]=5;
仅仅定义了值,没有定义引用,复杂的就不行了。
2、Object.defineProperty()
- 1、功能:定义属性。
原型链属性是不可以被修改描述对象的。
var obj = {_b:1};
三个参数: 对象 对象属性 描述对象
Object.defineProperty(obj, "a", {
// 是否可删除属性并且是否可以重定义该属性的属性描述对象,默认值是false:
configurable: false,
// 是否可以遍历,是否可枚举(该对象在使用for in遍历时,不可枚举属性是不能被遍历,也不能被Object.assign()复制 ):
enumerable: false,
// 是否可写,是否能够修改值:
writable: false,
// 是值,还可以是function方法:
value: 10,
});
后面再来讲解get和set,get和set不能够和writable和value同时定义,所以分开展示了:
Object.defineProperty(obj, "b", {
configurable: false,
enumerable: false,
// get和set不能够和writable和value同时定义
get: function () {
return this._b;
},
set: function (value) {
this._b=value;
}
})
- 2、定义一个obj,里面有函数abc,用for in遍历的话,会遍历函数abc;所以如果想要只遍历属性,就不能用这种方法。
var obj={
a:1,
b:2,
abc:function(){
console.log("abc")
}
}
for(var prop in obj){
console.log(prop);
}
- 3、如果把该属性函数的描述对象里设置成 enumerable:false,就不会遍历:
var obj0 = {
a: 1,
b: 2
}
Object.defineProperty(obj0, "abc", {
// 其实下面这三句可以不写,不写,默认false
// enumerable:false,
// configurable:false,
// writable:false,
value: function () {
console.log("abc")
}
});
console.log(obj0);
// 只遍历属性了,不会遍历函数abc(不可枚举)
for (var prop in obj0) {
console.log(prop);
}
// 然后此时再用Object.assign()就不会复制方法
var obj1 = Object.assign({}, obj0);
console.log(obj1);
- 4、定义多个属性 & 将属性返回 & 获取描述对象
Object.defineProperties(obj,{
"c":{
// 描述对象
value:function(){
}
},
d:{
// 描述对象
value:10,
writable:true
},
e:{
value:20,
writable:true,
configurable:true
},
f:{
value:30,
enumerable:true
}
});
console.log(obj);
(1)不过除了enumerable:true的,以上那些属性都没法遍历;
(2)不过可以通过获取obj的所有属性(不包括原型链属性__proto__),把所有的属性名放在一个数组中返回:
var arr = Object.getOwnPropertyNames(ob
j);
console.log(arr);
(3)获取某个属性的描述对象:
var desc = Object.getOwnPropertyDescriptor(obj, "f");
console.log(desc);
三、对象浅复制&深复制
浅复制:有相同的引用,比如说,新对象改变了其中一个对象属性后,源对象里该对象属性也改变了。
深复制:源对象的属性如果有对象,该对象属性修改后,不会引起复制后的对象各属性的改变,源对象的任何属性及子属性,与新对象的没有任何引用关系。
目前学的浅复制的方法:
1、for in 看不到不可枚举属性,不遍历空属性
for(var prop in obj){
// 浅复制
}
2、Object.assign();//浅复制
3、var obj1={...obj};//浅复制,obj作为一个对象属性;如果原obj1就存在,还会改变原obj1的引用地址
4、JSON字符串,表面上看是深复制,但其实,复制的不完全;所以既不是深复制,也不是浅复制。
var obj1=JSON.parse(JSON.stringify(obj));//不能达到真正的复制
console.l(obj1,obj);//可以自己看一下,有很多不同
目前学的深复制的方法:之前讲过很多例子,自己去翻翻以前的笔记。
下面要展示一个特别全活的深复制例子,这个很重要:js 对象深复制
四、对象的其他方法集合
Object.freeze()
- 冻结的对象不能增加属性,不能重新设置属性,不能删除,也不能重新设置属性的描述对象。
- 对象自身是可以删除的,只是对象所对应的属性,都是被冻结的。
var obj={a:1,b:2};
Object.freeze(obj);
// 对象冻结后,不能增加属性
obj.c=30;
// 不能重新设置属性
obj.a=1000;
// 也不能删除属性
delete obj.a;
console.log(obj);//虽然能够打印出来,但是obj没有任何改变
// 不能重新设置属性的描述对象
Object.defineProperty(obj,"a",{
configurable:true,
writable:true,
enumerable:true,
value:3000
})
console.log(obj);//出错了
// obj自身是可以删除的
obj=null;
console.log(obj);//null
- 对象被冻结后,使用Object.assign()与Object.create()
// obj虽被冻结,但可以用于复制,并且复制后的属性是可以删除和修改的
var obj1=Object.assign({},obj);
obj1.a=10;
console.log(obj1);
var o=Object.create(obj);//属性描述特征是被继承过去的
// 首先判断对象有没有原型属性a,如果有,还需要看原型属性a是否冻结,如果冻结则不能增加对象属性
o.a=10;//不能
o.c=10;//能增加,因为o没有被冻结
console.log(o);
判断对象是否冻结的两种方法 Object.isExtensible()、Object.isFrozen()
- Object.isExtensible() 判断当前对象是否可扩展:若对象被冻结,结果为false,则为不可扩展;若为true,则可以扩展。
- Object.isFrozen() 判断当前对象是否冻结:若对象被冻结,结果为true;否则,未被冻结。
var obj = { a: 1 };
Object.freeze(obj);
console.log(Object.isExtensible(obj));// false 不可扩展
console.log(Object.isFrozen(obj));// true obj被冻结
所以,判断对象冻结有以上两种判断方法。
Object.is()
- 比较数据和数据的类型,等同于" === " 的用法。
var a=3;
var b="3";
console.log(Object.is(a, b));//false 类型不等
console.log(Object.is([],[]));//false 引用不同
var a = 3; //数值类型
var b = new Number(3); //对象类型 创造数值型对象
console.log(Object.is(a, b)); //false 类型不等
var b=Number(3);//强制转换为数值3 数值类型
console.log(Object.is(a, b)); //true
// 很特殊的例子:(其他都跟 === 用法一样)
console.log(Object.is(NaN, NaN));//true
console.log(NaN === NaN);//false
hasOwnProperty() 与 in
- hasOwnProperty()判断对象实例是否具有某个对象属性。
- hasOwnProperty()方法无法检查该对象的原型链中是否具有该属性,该属性必须是对象本身的一个成员。
- 如果该属性或方法是该对象自身定义的而不是原型链中定义的,则返回true;否则返回false。
- in判断的是对象的所有属性,包括对象实例及其原型的属性。
var obj = { a: 1};
console.log(obj.hasOwnProperty("a"));//true
console.log(obj.hasOwnProperty("b"));//false
var o=Object.create(obj);
console.log(o.hasOwnProperty("a"));//false 原型属性不是当前对象的对象属性
console.log("a" in o);//true in判断属性是否是对象属性时,只要是该对象的原型属性和对象属性都可以判断
isPrototypeOf()
- isPrototypeOf() 是用来判断指定对象object1是否存在于另一个对象object2的原型链中,是则返回true,否则返回false。
- 或者这么理解,object2._ _ proto _ _ ==== object1.prototype ? true:false 。
var obj = { a: 1};
var o=Object.create(obj);
var o1=Object.create(o);
o.a=10;
o1.a=1000;
console.log(o.isPrototypeOf(obj));//false
console.log(obj.isPrototypeOf(o));//true obj是o对象的原型
console.log(o,o1);
console.log(obj.isPrototypeOf(o1));//true obj是o1的原型链中的一个
console.log(o.isPrototypeOf(o1));//true o也是o1的原型链中的一个
- 类中,isPrototypeOf 可以判断某个类的父类是否是这个指定类。
- 语法:父类.isPrototypeOf(子类)
class Box {
a = 3;
constructor() {
}
}
class Ball extends Box {
b = 10;
constructor() {
super();
}
}
class Rect extends Ball {
c = 20;
constructor() {
super();
}
}
var rect=new Rect();
console.log(rect);
// 说明Rect继承于Ball,Ball继承于Box,所以Rect的父类当中有Box、Ball
console.log(Box.isPrototypeOf(Rect));//true
console.log(Ball.isPrototypeOf(Rect));//true
propertyIsEnumerable
- propertyIsEnumerable 判断是否是可枚举属性。
var obj={a:1,b:2};
Object.defineProperty(obj,"b",{
enumerable:true //设置可枚举
});
Object.defineProperty(obj,"c",{
value:3000
});
var names=Object.getOwnPropertyNames(obj);
console.log(names);
for(var i=0;i<names.length;i++){
if(obj.propertyIsEnumerable(names[i])){
console.log(names[i]+"是可枚举属性")
}else{
console.log(names[i]+"是不可枚举属性")
}
}