本文是 重温基础 系列文章的第十二篇。 今日感受:需要总结下2018。这几天,重重的感冒发烧,在家休息好几天,伤···。系列目录:【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理)【重温基础】1.语法和数据类型【重温基础】2.流程控制和错误处理【重温基础】3.循环和迭代【重温基础】4.函数【重温基础】5.表达式和运算符【重温基础】6.数字【重温基础】7.时间对象【重温基础】8.字符串【重温基础】9.正则表达式【重温基础】10.数组【重温基础】11.Map和Set对象本章节复习的是JS中对象的使用,这是重点。 前置知识: JavaScrip的设计是一个简单的基于对象的范式。 对象是一系列属性的集合,一个属性包含一个键和一个值,也叫键值对。 若一个属性的值,是一个函数,则称这个属性为方法。 一个对象,可以有很多方法,就像一个杯子,可以有颜色,重量,形状等属性。 注意: 对象的名称,对大小写敏感。1.创建对象本文主要是复习对象的使用,至于对象的创建,下面这里简单介绍一下常见的创建对象的方法: 创建方法1:let obj = new Object(); // 声明一个空对象创建方法2:let obj = {}; // 声明一个空对象上面的name是对象obj中的一个属性,对应的值为"leo"。2.设置对象属性和访问属性设置对象属性,也有两种方法: 直接设置:let obj = {};obj.name = “leo”; // 为对象添加属性和值创建时设置:let obj = {name : ’leo’};obj.name = “leo”; // 为对象添加属性和值当一个对象定义了一个属性,我们可以用点符号(.)来访问它。obj.name; // “leo"注意:属性的另外一种写法:let obj = {};let n = “name”;obj[n] = “leo”; // 属性名使用变量obj[“height”] = 188; // 字符串obj[“age” + “Is”] = 15; // 字符串拼接console.log(obj);/obj = { ageIs: 15 height: 188 name: “leo”}/属性的值也可以是个方法:let obj = {};obj.getTime = function(){ return new Date();}obj.getTime(); // 访问属性的方法//Wed Jan 02 2019 21:07:59 GMT+0800 (中国标准时间) 3.枚举对象的所有属性从 ECMAScript 5 开始,有三种原生的方法用于列出或枚举对象的属性:3.1 for…in 循环该方法依次访问一个对象及其原型链中所有可枚举的属性。let obj = {a:1, b:2, c:3};for (let i in obj) { console.log(“obj.” + i + " = " + obj[i]);}/“obj.a = 1"“obj.b = 2"“obj.c = 3”/3.2 Object.keys(o)该方法返回一个对象 o 自身包含(不包括原型中)的所有属性的名称的数组。let obj = {a:1, b:2, c:3};let arr = Object.keys(obj);console.log(arr);/[“a”, “b”, “c”]/arr.forEach(function(val,index,array){ console.log(“key:” + val+",val:"+obj[val])})/key:a,val:1key:b,val:2key:c,val:3/3.3 Object.getOwnPropertyNames(o)该方法返回一个数组,它包含了对象 o 所有拥有的属性(无论是否可枚举)的名称。let obj = {a:1, b:2, c:3};let arr = Object.getOwnPropertyNames(obj);console.log(arr);/[“a”, “b”, “c”]/arr.forEach(function(val,index,array){ console.log(“key:” + val+",val:"+obj[val])})/key:a,val:1key:b,val:2key:c,val:3/4.ES6新增:对象的拓展4.1 属性的简洁表示let a = ‘a1’;let b = { a }; // b => { a : ‘a1’ }// 等同于let b = { a : a };function f(a, b){ return {a, b}; }// 等同于function f (a, b){ return {a:a ,b:b};}let a = { fun () { return ’leo’; }}// 等同于let a = { fun : function(){ return ’leo’; }}4.2 属性名表达式JavaScript提供2种方法定义对象的属性。// 方法1 标识符作为属性名a.f = true;// 方法2 字符串作为属性名a[‘f’ + ‘un’] = true;延伸出来的还有:let a = ‘hi leo’;let b = { [a]: true, [‘a’+‘bc’]: 123, [‘my’ + ‘fun’] (){ return ‘hi’; }};// b.a => undefined ; b.abc => 123 ; b.myfun() => ‘hi’// b[a] => true ; b[‘abc’] => 123 ; b[‘myfun’] => ƒ [‘my’ + ‘fun’] (){ return ‘hi’; }注意: 属性名表达式不能与简洁表示法同时使用,否则报错。// 报错let a1 = ‘aa’;let a2 = ‘bb’;let b1 = {[a1]};// 正确let a1 = ‘aa’;let b1 = { [a1] : ‘bb’};4.3 Object.is()Object.is() 用于比较两个值是否严格相等,在ES5时候只要使用相等运算符(==)和严格相等运算符(===)就可以做比较,但是它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。Object.is(‘a’,‘a’); // trueObject.is({}, {}); // false// ES5+0 === -0 ; // trueNaN === NaN; // false// ES6Object.is(+0,-0); // falseObject.is(NaN,NaN); // true4.4 Object.assign()Object.assign()方法用于对象的合并,将原对象的所有可枚举属性复制到目标对象。 基础用法: 第一个参数是目标对象,后面参数都是源对象。let a = {a:1};let b = {b:2};Object.assign(a,b); // a=> {a:1,b:2}注意:若目标对象与源对象有同名属性,则后面属性会覆盖前面属性。let a = {a:1, b:2};let b = {b:3, c:4};Object.assign(a, b); // a => {a:1, b:3, c:4}若只有一个参数,则返回该参数。let a = {a:1};Object.assign(a) === a; // true若参数不是对象,则先转成对象后返回。typeof Object.assign(2); // ‘object’由于undefined或NaN无法转成对象,所以做为参数会报错。Object.assign(undefined) // 报错Object.assign(NaN); // 报错Object.assign()实现的是浅拷贝。Object.assign()拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。let a = {a: {b:1}};let b = Object.assign({},a);a.a.b = 2;console.log(b.a.b); // 2将数组当做对象处理,键名为数组下标,键值为数组下标对应的值。Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3]5.ES8新增:Object.values(),Object.entries()ES7中新增加的 Object.values()和Object.entries()与之前的Object.keys()类似,返回数组类型。 回顾下Object.keys():var a = { f1: ‘hi’, f2: ’leo’};Object.keys(a); // [‘f1’, ‘f2’]5.1 Object.values()返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值。let a = { f1: ‘hi’, f2: ’leo’};Object.values(a); // [‘hi’, ’leo’]如果参数不是对象,则返回空数组:Object.values(10); // []Object.values(true); // []5.2 Object.entries()返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组。let a = { f1: ‘hi’, f2: ’leo’};Object.entries(a); // [[‘f1’,‘hi’], [‘f2’, ’leo’]]用途1:遍历对象属性。let a = { f1: ‘hi’, f2: ’leo’};for (let [k, v] of Object.entries(a)){ console.log( ${JSON.stringfy(k)}:${JSON.stringfy(v)} )}// ‘f1’:‘hi’// ‘f2’:’leo’用途2:将对象转为真正的Map结构。let a = { f1: ‘hi’, f2: ’leo’};let map = new Map(Object.entries(a));手动实现Object.entries()方法:// Generator函数实现: function* entries(obj){ for (let k of Object.keys(obj)){ yield [k ,obj[k]]; }}// 非Generator函数实现:function entries (obj){ let arr = []; for(let k of Object.keys(obj)){ arr.push([k, obj[k]]); } return arr;}6.ES8新增:Object.getOwnPropertyDescriptors()之前有Object.getOwnPropertyDescriptor方法会返回某个对象属性的描述对象,新增的Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象let a = { a1:1, get f1(){ return 100}}Object.getOwnPropetyDescriptors(a);/{ a:{ configurable:true, enumerable:true, value:1, writeable:true} f1:{ configurable:true, enumerable:true, get:f, set:undefined}}/实现原理:function getOwnPropertyDescriptors(obj) { const result = {}; for (let key of Reflect.ownKeys(obj)) { result[key] = Object.getOwnPropertyDescriptor(obj, key); } return result;}引入这个方法,主要是为了解决Object.assign()无法正确拷贝get属性和set属性的问题。let a = { set f(v){ console.log(v) }}let b = {};Object.assign(b, a);Object.a(b, ‘f’);/f = { configurable: true, enumable: true, value: undefined, writeable: true}/value为undefined是因为Object.assign方法不会拷贝其中的get和set方法,使用getOwnPropertyDescriptors配合Object.defineProperties方法来实现正确的拷贝:let a = { set f(v){ console.log(v) }}let b = {};Object.defineProperties(b, Object.getOwnPropertyDescriptors(a));Object.getOwnPropertyDescriptor(b, ‘f’)/* configurable: true, enumable: true, get: undefined, set: function(){…}*/Object.getOwnPropertyDescriptors方法的配合Object.create方法,将对象属性克隆到一个新对象,实现浅拷贝。const clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));// 或者const shallowClone = (obj) => Object.create( Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));7.ES9新增:对象的拓展运算符7.1 介绍对象的拓展运算符,即对象的Rest/Spread属性,可将对象解构赋值用于从一个对象取值,搜键值对分配到指定对象上,与数组的拓展运算符类似:let {x, y, …z} = {x:1, y:2, a:3, b:4};x; // 1y; // 2z; // {a:3, b:4} 对象的解构赋值要求等号右边必须是个对象,所以如果等号右边是undefined或null就会报错无法转成对象。let {a, …b} = null; // 运行时报错let {a, …b} = undefined; // 运行时报错解构赋值必须是最后一个参数,否则报错。let {…a, b, c} = obj; // 语法错误let {a, …b, c} = obj; // 语法错误注意:1.解构赋值是浅拷贝。let a = {a1: {a2: ’leo’}};let {…b} = a;a.a1.a2 = ’leo’;b.a1.a2 = ’leo’;2.拓展运算符的解构赋值,不能复制继承自原型对象的属性。let o1 = { a: 1 };let o2 = { b: 2 };o2.proto = o1;let { …o3 } = o2;o3; // { b: 2 }o3.a; // undefined7.2 使用场景1.取出参数对象所有可遍历属性,拷贝到当前对象中。let a = { a1:1, a2:2 };let b = { …a };b; // { a1:1, a2:2 }// 类似Object.assign方法2.合并两个对象。let a = { a1:1, a2:2 };let b = { b1:11, b2:22 };let ab = { …a, …b }; // {a1: 1, a2: 2, b1: 11, b2: 22}// 等同于let ab = Object.assign({}, a, b);3.将自定义属性放在拓展运算符后面,覆盖对象原有的同名属性。let a = { a1:1, a2:2, a3:3 };let r = { …a, a3:666 }; // r {a1: 1, a2: 2, a3: 666}// 等同于let r = { …a, …{ a3:666 }};// r {a1: 1, a2: 2, a3: 666}// 等同于let r = Object.assign({}, a, { a3:666 });// r {a1: 1, a2: 2, a3: 666}4.将自定义属性放在拓展运算符前面,就会成为设置新对象的默认值。let a = { a1:1, a2:2 };let r = { a3:666, …a };// r {a3: 666, a1: 1, a2: 2}// 等同于let r = Object.assign({}, {a3:666}, a);// r {a3: 666, a1: 1, a2: 2}// 等同于let r = Object.assign({a3:666}, a);// r {a3: 666, a1: 1, a2: 2}5.拓展运算符后面可以使用表达式。let a = { …(x>1? {a:!:{}), b:2}6.拓展运算符后面如果是个空对象,则没有任何效果。{…{}, a:1}; // {a:1}7.若参数是null或undefined则忽略且不报错。let a = { …null, …undefined }; // 不报错8.若有取值函数get则会执行。// 不会打印 因为f属性只是定义 而不没执行let a = { …a1, get f(){console.log(1)}}// 会打印 因为f执行了let a = { …a1, …{ get f(){console.log(1)} }}参考资料1.MDN 使用对象 2.W3school JavaScript 对象本部分内容到这结束Author王平安E-mailpingan8787@qq.com博 客www.pingan8787.com微 信pingan8787每日文章推荐https://github.com/pingan8787…ES小册es.pingan8787.com