JavaScript/ES6中的Object

2023-11-11

记录Object构造函数的一些静态方法;Object是编程中常见的一种构造函数,创建一个对象包装器,为什么说是创建了一个对象包装器?Object构造器会根据传入的参数的类型,将参数包装成具体的对象,如有数字对象、boolean对象、字符串对象、Object对象(null ,undefined,不传参)、Symbol对象;若传入的参数是个对象(引用类型数据),则会返回参数自身。一般情况下,定义一个对象,可以直接用对象字面量如:var obj = { }; var obj1 = {a: '123'}; 一般不会很少用new Object();

1,以下几个例子描述了Object创建的几种对象包装器以及Object的属性:Symbol不是构造函数

//1,当参数为null或undefined,或者不传参的时候---------------------
//以下跟Object()的执行结果一样。
var obj = new Object(null); //{}
obj = new Object(undefined);//{}
obj = new Object();//{}

//2,当参数是简单的原始数据时;-----------------------
//以下例子,Object(arg)与new Object(arg);
//但是String(arg)与new String(arg)是不一样的,
//String(arg) == arg; new String(arg)创建一个原始值为arg的字符串实例。
//Number、Boolean跟String一样。
var strObj = new Object('str');//String{'str'},等同于new String('str');
var boolObj = new Object(true);//Boolean{true},等同于new Boolean(true);
var numberObj = new Object(1);//Number{1},等同于new Number(1);
var symbolObj = new Object(Symbol.for('Symb'));//Symbol(Symbol(Symb)),Symbol不是构造函数

//3, 当参数是引用类型时;----------------
//Object(refrenceObj)与new object(refrenceObj)效果一样;
var arrObj = [1,4,6];
var arrObj1 = new Object(arrObj);
arrObj1 === arrObj;//true, 返回arrObj本身。
var obj = {a: 'hi', b: 'hello'};
obj === new Object();//true
//当参数值是String对象、Number对象、Boolean对象等,结果也是一样的。

//4,顺带一提Array与new Array()的效果一样,都是创建数组实例-----------
Array();//[]
new Array();//[]

Array(9);//[empty*9],长度为9的空数组
new Array(9);//[empty*9],长度为9的空数组

Array(1, 2);//[1,2],长度为2,元素项1,2
new Array(1, 2);//同上

2.1,ES6版本中增加的方法 Object.is、Object.getOwnPropertySymbols、Object.setPrototypeOf、Object.assign:

//1, Object.is(val1, val2)------------
//Object.is是用来比较两个值是否严格相等(数据类型以及值同时相等);
//且Object.is比较两个NaN,返回的是true;
//比较两个引用类型值时,只有当他们指向相同的引用相等。
Object.is('2', '2');//true
Object.is('2', 2);//false;

Object.is(true, true); //true
Object.is(true, 1); //false

Object.is({}, {});//false
var obj = {}; var obj1 = obj;
Object.is(obj, obj1);//true

Object.is(NaN, NaN); //true.

//2.1, 面对Object.assign,可以当作是有3个重载方法-------------
//fucntion(target, source)/function(target, ...sources)/
//function(target, source1, source2, ?source3),那么在使用Object.assign用的是哪个呢??
var obj = {a: 'hi', b: 'hello'};
var mergedObj = Object.assign(obj, {a: 'TEST'}, {c: 'take an exam'});
mergedObj === obj;//true
mergedObj
//{a:'TEST', b:'hello', c: 'take an exam'}
//Object.assign的操作对target对象有影响,并返回target对象本身

//2.2,Object.assign可用于多个数组的合并,也可以对象和数组的混合合并---------
//使用方式同上
//当参数是数组,下标index便是数组的key
var obj = {a: 'TEST', c: 'EXAM'};
var mergedObj = Object.assign(obj, ['for what', 'go ahead']);
mergedObj === obj;//true
mergedObj
//{0: 'for what', 1: 'go ahead',a: 'TEST', c: 'EXAM'}
//target是数组时
var arr = ['here', 'is', 'not', 'right'];
var mergedArr = Object.assign(arr, ['That', , , , '!']);
mergedArr === arr;//true;
mergedArr 
//['That', 'is', 'not', 'right', '!']
//['That', , , , '!']中间跳过了三个元素,这三个元素表示空元素,不同于null或undefined
//空元素并没有覆盖前面的对应位置的数值。

//3, Object.getOwnPropertySymbols(obj)方法返回对象的Symbol类型属性的数组,不管可不可枚举----
var obj = {test: 'TEST', [Symbol.for('Sym')]: 'Sym'};
Object.defineProperty(obj, Symbol.for('un enumerable'), {value: 'un', enumerable: false});
Object.getOwnPropertySymbols(obj);
//[Symbol(Sym), Symbol(un enumerable)]
//总结一下Object获取属性名的方法:
//keys返回对象自身的可枚举的非Symbol类型的属性
//getOwnPropertyNames返回对象自身的非Symbol类型的属性,不管可不可枚举
//getOwnPropertySymbols跟getOwnPropertyNames有相似之处

//4, Object.setPrototypeOf(obj, proto)为对象设置原型对象(设置__proto__的值)----------
//调用Object.setPrototypeOf(o, proto),proto会在对象o的原型链中。
var proto = {b: 'back-side', c: 'xigua'};
var obj = {a: 'Test', b: 'bgm'};
Object.setPrototypeOf(obj, proto);
obj.__proto__ === proto;//true
obj.b; //'bgm'  proto.b被屏蔽
obj.c; //'xigua'
//可以用.setPrototypeOf([], [])设置一个数组对象的__proto__;
//注意.setPrototypeOf(fn, proto),这样不能达到想要的结果,不是正确的操作

//5, 貌似JS的类型构造函数都有个length属性,貌似都是值为1 ---------------
// Function.length === 1; Number.length === 1; 至于为什么不清楚;
//像函数function h(){}; h.length === 0;
Object.length === 1;//true

//6, 貌似JS的类型构造函数都有个prototype属性,它是个对象,用来表示对象原型;-----------
//函数的原型用属性prototype表示,实例的原型用属性__proto__表示。
//Object.prototype基本被所有对象继承,这也是基本所有对象都是Object的实例的原因;
//但是Object.prototype对象的属性有可能被覆盖(尽量不要这样),
//也可能会被屏蔽,当原型链上存在相同的属性名时,会发生屏蔽(就近原则的访问)。

2.2,ES6版本增加的对象操作:对象展开运算符、对象属性缩写、计算属性:

//1,新增的对象操作-----
//Object.assign 和Object.is等在上面提到

//2,对象扩展(展开)运算符,扩展运算符由三个点表示... --------------
//扩展运算符是个单操作数运算符,可以将操作数展开到新的对象中
//目前为止,用到扩展运算符的地方:剩余参数function fn(...args);
//传参时数组(或可迭代对象)参数的展开, fn(...array);
//扩展数组, [0, ...arr, ...arr, 8, ...arr],合并数组,不会发生覆盖
//以上三个地方,剩余参数自是个数组;展开参数和数组扩展中,操作数必须是可迭代对象。
//接下来就是本章中提到的对象扩展运算,有点类似于Object.assign(),相同属性名,后来者居上
//语法如下:{...obj, ...obj1, key1:val1, ...obj2, key2:val2}
var obj = {b: 'hi', c: 'Test', d: 'Exam'};
var obj1 = {b: 'dance'};
var mergedObj = {...obj, ...obj1, ...{a: 'outside'}};
mergedObj;//{b:'dance', c:'Test', d:'Exam', a:'outside'}
var arr = [3, 10, 2];
var mixObj = {...obj1, ...arr};
mixObj;//{'0':3, '1':10, '2':2, b: 'dance'}
//注意:扩展运算是浅拷贝源对象的属性;
//扩展运算符不能暴露在外面。使用时可以根据上面语法。

//3,对象属性缩写,缩写可以不用明确指出属性名,变量的名字作为对象的属性名,值作为该属性的值 ----
//语法:
var a = 'can be a name for the prop of an Obj';
var b = 'so is it';
var obj = {a, b}; 
obj;
//{a: 'can be a name for the prop of an Obj', b:'so is it'}
//值为函数的属性也可以缩写

//4,计算属性为对象添加动态属性,用[expression]的形式表示,expression的计算结果为属性名 -----
//语法:{[expression]: val},貌似动态属性不支持缩写
var propKey = 'a key';
var obj = {[propKey]: 'computed prop', [1+4]: 'it is 5'};
obj;//{'a key': 'computed prop', '5': 'it is 5'}

3,这应该是在ES7或ES8版本增加:Object.entries、Object.values、Object.fromEntries:

//1, Object.entries(obj)方法用于获取对象的键值对 ----------
//每个键值对是一个数组,最后组成一个大的数组作为返回值:
//只能遍历到可枚举的非symbol类型的属性,类似于 for...in...操作
var obj = {a: 'hi', b: 'test'};
Object.defineProperty(obj, 'c', {value: 'exam', enumerable: false});
Object.defineProperty(obj, Symbol.for('hi'), {value:'Sym', enumerable: false});
Object.defineProperty(obj, Symbol.for('bgm'), {value: 'bgm', enumerable: true});
Object.entries(obj);
//[['a', 'hi'], ['b', 'test']]

//2, Object.values(obj)方法的规则与Object.entries(obj)类似; --------------
// Object.values(obj)返回以属性值为元素的数组
Object.values(obj); //obj是上面例子定义的变量
//['hi', 'test']

//2, Object.fromEntries(entries)方法通过键值对数组创建一个对象,逻辑跟Object.entries相反-----
//如
Object.fromEntries([['a', 'hi'], ['b', 'test']]);
//{a:'hi', b:'test'}

4,其他静态方法:Object.keys、Object.getPrototypeOf、Object.freeze(Object.isFrozen)、 Object.getOwnPropertyNames、 Object.getOwnPropertyDescriptor、 Object.defineProperty、Object.defineProperties、Object.create、Object.seal(Object.isSeal )、Object.preventExtensions(Object.isExensible)

//Object.keys(obj)方法返回对象本身的可枚举的非Symbol类型的属性名数组----------
//上述规则与Object.entries()、Object.values、for...in...类型
var obj = {a: 'hi', b: 'test'};
Object.defineProperty(obj, 'c', {value: 'exam', enumerable: false});
Object.defineProperty(obj, Symbol.for('hi'), {value:'Sym', enumerable: false});
Object.defineProperty(obj, Symbol.for('bgm'), {value: 'bgm', enumerable: true});
Object.keys(obj);
//['a', 'b']

//Object.getPrototypeOf(obj)方法读取对象的原型对象__proto__ ------------

//Object.getOwnPropertyNames(obj)方法获取对象自身的非Symbol类型的所有属性名,返回值是个数组--
//使用Object.keys的用例
Object.getOwnPropertyNames(obj); //可枚举和不可枚举的非Symbol类型的
//["a", "b", "c"]

//Object.getOwnPropertyDescriptor(obj, key)返回指定属性名的descriptor ---------
//使用Object.keys的用例
Object.getOwnPropertyDescriptor(obj, Symbol.for('hi'));
//{configurable: false, enumerable: false, value: "Sym", writable: false}

//Object.getOwnPropertyDescriptors(obj)返回对象自身的所有属性的descriptor ------------
//使用Object.keys的用例
Object.getOwnPropertyDescriptors(obj);
//输出如下:
//{ a: {value: "hi", writable: true, enumerable: true, configurable: true},
    b: {value: "test", writable: true, enumerable: true, configurable: true},
    c: {value: "exam", writable: false, enumerable: false, configurable: false},
    Symbol(bgm): {value: "bgm", writable: false, enumerable: true, configurable: false},
    Symbol(hi): {value: "Sym", writable: false, enumerable: false, configurable: false}
  }

//Object.defineProperty(obj, name, descriptor)方法为对象新增属性-----------
var obj = {prop1: 'prop 1'};
Object.defineProperty(obj, 'prop2', {value: 'prop 2', enumerable: false});

//Object.defineProperties(obj, descsObj)方法为对象新增属性,与defineProperty不同的是:----
//defineProperties可同时定义多个属性
var obj = {prop1: 'prop 1'};
Object.defineProperties(obj, {prop2: {value: 'prop 2', enumerable: false}});

//Object.create(proto, obj)方法创建一个新对象,---------------------
//第一参数:赋给新对象的原型对象;第二个参数:属性的descriptions对象,属性值会浅拷贝给新对象;
//记得把enumerable等属性也加上并赋值为true,否则默认为false
var obj = {a: {value: 'to new obj', enumerable: true}, b:{value: {cl: 'for test'}}};
var proto = {protoProp: 'hi, new obj'};
var newObj = Object.create(proto, obj);
newObj; //
Object.getPrototypeOf(newObj) === proto;//true

//obj.hasOwnProperty(key);用来判断指定属性名是否是对象自身的属性---------
//注意,要用对象实例调用hasOwnProperty方法,它是原型链上的一个方法,不是Object的静态方法
//以Object.create(proto, obj)的结果作为obj对象
newObj.hasOwnProperty('a');//or这样调用Object.prototype.hasOwnProperty.call(newObj, 'a');
//true
newObj.hasOwnProperty('protoProp');//protoProp是原型对象的属性
//false

//obj.isPrototypeOf(obj1);用来判断一个对象是不是另一个对象的原型对象----------
//注意,跟Object.prototype.hasOwnProperty一样,isPrototypeOf也是原型链上的一个方法
var proto = {b: 'haha'};
var obj = {d: 'keke'};
Object.setPrototypeOf(obj, proto);
proto.isPrototypeOf(obj);//true

//Object.freeze(obj) 冻结对象(Object.isFrozen(obj))------------
//与seal不同的是,freeze保护属性不被修改,相同的是不能新增、不能删除
var obj = {a: 'hi', b: 'test'};
obj.o = {kl: 'deep'};
Object.freeze(obj);
obj.a = 'df'; //不能修改
delete obj.b;//不能删除
obj.test = 'ddd';//不能添加
obj.o.kl = 'changed'; //会被修改
obj; //{a: "hi", b: "test", o: {kl: "changed"}}
Object.isFrozen(obj); //true
Object.isExtensible(obj);//false

//Object.seal(obj)、Object.isSeal(obj) --------------------
//以下例子可以看出,seal之后的对象属性可修改,但是不能删除,且对象不能添加新属性: 
var obj = {a: 'hi', b: 'test'};
obj.o = {kl: 'deep'};
Object.seal(obj);
obj.a = 'df';
delete obj.b;
obj.test = 'ddd';
obj; //{a: "df",b: "test",o: {kl: "deep"}}
Object.isSealed(obj);//true
Object.isExtensible(obj);//false

//Object.preventExtensions(obj)和 Object.isExensible(obj)----------------
//Object.freeze(obj)、Object.seal(obj)、Object.preventExtensions(obj)这三个方法都
//会让obj的isExtensible为false,即都不能扩展
var obj = {a: 'hi', b: 'test'};
obj.o = {kl: 'deep'};
Object.preventExtensions(obj);
obj.a = 'df'; //可修改
delete obj.b; //可删除
obj.test = 'ddd'; //会被忽略,false,不可添加,即不可扩展
Object.isExtensible(obj);//true
obj; 
//{a: "df", o: {kl: "deep"}}
Object.assign(obj, {h: 'hahah'}); //会报错
//Uncaught TypeError: Cannot add property h, object is not extensible

5,Object操作的总结:

//访问对象属性或属性值:-------------------------
//Object.keys(obj)方法获取对象本身的可枚举的非Symbol类型的属性名,返回数组。
//getOwnPropertySymbols(obj)方法获取对象自身的Symbol类型属性,不管可不可枚举,返回数组;
//getOwnPropertyNames(obj)返回对象自身的非Symbol类型的属性,不管可不可枚举;
//getOwnPropertySymbols跟Symbol类型有关,getOwnPropertyNames跟非Symbol类型有关。

//Object.entries(obj)方法获取对象的键值对,返回数组,元素是由键值组成的数组;
//只能遍历到可枚举的非symbol类型的属性,类似于 for...in...操作。
//但是注意for...in...操作会遍历原型对象的属性,像这样:-------!!!!!
//var a={a: 'test'}; var b={b:'gho'};Object.setPrototypeOf(b, a);
//那么for...in...能遍历到a属性,最终能遍历到的属性集是['b', 'a']。
//Object.values(obj)方法的规则与Object.entries(obj)类似;只返回可枚举的非Symbol类型的属性值;
//返回值类型是数组;与entries方法不同的是:
//Object.values(obj)获取的属性值,Object.entries获取的是属性名。

//Object.getOwnPropertyDescriptor(obj, key)获取对象自身的指定属性名的descriptor对象,
//指定属性可以是可枚举或不可枚举,也可以是Symbol类型或非Symbol类型。
//Object.getOwnPropertyDescriptors(obj)获取对象自身的所有属性的descriptor对象,
//返回值类型是对象,obj的属性作为返回值对象的属性。

//Object.getPrototypeOf(obj)方法读取对象的原型对象__proto__
//与Object.setPrototypeOf(obj, proto)方法的逻辑是反着来的,一个是获取原型,一个是设置

//设置属性值: ------------------------------------------------
//Object.defineProperty(obj, name, descriptor)
//Object.defineProperties(obj, descsObj)
//使用以上两个方法,注意重定义属性时,当该属性是writable===false,要设置value值与之前的不同,
//可能会报错:不能重定义属性xx
//defineProperty和直接在对象上添加一个属性有很大的差别
//defineProperty(obj, key, descriptor);不仅可以新加属性,还可以改变一个已有可配置属性的
//descriptor,相当于覆盖已有属性的descriptor,是质的改变;
//直接添加或修改,是值的改变,像这样obj[key] = val;

//Object.setPrototypeOf(obj, proto)为对象设置原型对象(设置__proto__的值)
//调用Object.setPrototypeOf(o, proto),proto会在对象o的原型链中。
//可以用.setPrototypeOf([], [])设置一个数组对象的__proto__;
//注意.setPrototypeOf(fn, proto),这样不能达到想要的结果,不是正确的操作
//另外当obj[protoProp] = (any);这样的操作不会改变原型链上的proto[protoProp]的值,
//而是在对象自身添加了一个属性(protoProp).

//面对Object.assign,可以当作是有3个重载方法
//fucntion(target, source)/function(target, ...sources)/
//function(target, source1, source2, ?source3)
//Object.assign的操作对target对象有影响,并返回target对象本身;
//Object.assign可用于多个数组的合并,也可以对象和数组的混合合并;
//当参数是数组,下标index便是数组的key;
//当参数对象出现相同的属性时,取最后出现的属性值。
//数组也是,但空元素([,,,])并没有覆盖前面的对应位置的数值。

//对象扩展(展开)运算符,扩展运算符由三个点(...)表示
//扩展运算符是个单操作数运算符,可以将操作数展开到新的对象中
//目前为止,可用到扩展运算符的地方:剩余参数function fn(...args);
//传参时数组(或可迭代对象)参数, fn(...array);
//扩展数组, [0, ...arr, ...arr, 8, ...arr],合并数组,不会发生覆盖
//以上三个地方,剩余参数自是个数组;展开参数和展开数组中,操作数必须是可迭代对象。
//接下来就是对象扩展运算,有点类似于Object.assign(),相同属性名,后来者居上
//注意:扩展运算是浅拷贝源对象的属性;
//扩展运算符不能暴露在外面。使用时可以根据上面语法。

//修改对象的操作权限-------------------------------------
//Object.freeze(obj) 不可修改已有属性、不可删除已有属性、不可添加属性(Object.isFrozen)、 
//Object.seal(obj) 可修改已有属性,不可删除已有属性,不可添加属性(Object.isSeal)、
//Object.preventExtensions(obj) 可删除、修改已有属性,但不能添加属性(Object.isExensible)
//上述3个方法都会但会导致Object.isExensible返回false。且obj的configurable===false。
//freeze 和 seal改变了属性的可配置性为false(configurable===false),
//freeze还改变了writable==false

//创建新对象------------------------------------------------
//Object.create(proto, descriptionsObj)
//proto 作为新对象的原型对象
//当proto是null的时候,Object.create(null)返回的是个没有任何property的对象。

6,其他文档

扩展运算符

7,参考

runoob ES6 Object

MDN Object

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaScript/ES6中的Object 的相关文章