资讯专栏INFORMATION COLUMN

【重温基础】12.使用对象

garfileo / 3158人阅读

摘要:本文是重温基础系列文章的第十二篇。注意对象的名称,对大小写敏感。基础用法第一个参数是目标对象,后面参数都是源对象。用途遍历对象属性。用途将对象转为真正的结构。使用场景取出参数对象所有可遍历属性,拷贝到当前对象中。类似方法合并两个对象。

本文是 重温基础 系列文章的第十二篇。
今日感受:需要总结下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:1
key:b,val:2
key: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:1
key:b,val:2
key: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");   // true
Object.is({}, {});    // false

// ES5
+0 === -0 ;           // true
NaN === NaN;          // false

// ES6
Object.is(+0,-0);     // false
Object.is(NaN,NaN);   // true
4.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"

由于undefinedNaN无法转成对象,所以做为参数会报错。

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
}
*/

valueundefined是因为Object.assign方法不会拷贝其中的getset方法,使用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;  // 1
y;  // 2
z;  // {a:3, b:4} 

对象的解构赋值要求等号右边必须是个对象,所以如果等号右边是undefinednull就会报错无法转成对象。

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;  // undefined
7.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.若参数是nullundefined则忽略且不报错。

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-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推荐 https://github.com/pingan8787...
JS小册 js.pingan8787.com

欢迎关注微信公众号【前端自习课】每天早晨,与您一起学习一篇优秀的前端技术博文 .

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100632.html

相关文章

  • 重温基础】7.时间对象

    摘要:本文是重温基础系列文章的第七篇。系列目录复习资料资料整理个人整理重温基础语法和数据类型重温基础流程控制和错误处理重温基础循环和迭代重温基础函数重温基础表达式和运算符重温基础数字本章节复习的是中的时间对象,一些处理的方法。 本文是 重温基础 系列文章的第七篇。今日感受:做好自律。 系列目录: 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理) 【重温基础】1.语法和数据类型...

    YuboonaZhang 评论0 收藏0
  • 重温基础】15.JS对象介绍

    摘要:构造函数通常首字母大写,用于区分普通函数。这种关系常被称为原型链,它解释了为何一个对象会拥有定义在其他对象中的属性和方法。中所有的对象,都有一个属性,指向实例对象的构造函数原型由于是个非标准属性,因此只有和两个浏览器支持,标准方法是。 从这篇文章开始,复习 MDN 中级教程 的内容了,在初级教程中,我和大家分享了一些比较简单基础的知识点,并放在我的 【Cute-JavaScript】系...

    booster 评论0 收藏0
  • 重温基础】14.元编程

    摘要:本文是重温基础系列文章的第十四篇。元,是指程序本身。有理解不到位,还请指点,具体详细的介绍,可以查看维基百科元编程。拦截,返回一个布尔值。 本文是 重温基础 系列文章的第十四篇。 这是第一个基础系列的最后一篇,后面会开始复习一些中级的知识了,欢迎持续关注呀! 接下来会统一整理到我的【Cute-JavaScript】的JavaScript基础系列中。 今日感受:独乐乐不如众乐乐...

    cc17 评论0 收藏0
  • 重温基础】5.表达式和运算符

    摘要:系列目录复习资料资料整理个人整理重温基础语法和数据类型重温基础流程控制和错误处理重温基础循环和迭代重温基础函数本章节复习的是中的表达式和运算符,用好这些可以大大提高开发效率。 本文是 重温基础 系列文章的第五篇。今日感受:家的意义。 系列目录: 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理) 【重温基础】1.语法和数据类型 【重温基础】2.流程控制和错误处理 【重温基...

    马忠志 评论0 收藏0
  • 重温基础】13.迭代器和生成器

    摘要:迭代器和生成器将迭代的概念直接带入核心语言,并提供一种机制来自定义循环的行为。本文主要会介绍中新增的迭代器和生成器。属性本身是函数,是当前数据结构默认的迭代器生成函数。 本文是 重温基础 系列文章的第十三篇。今日感受:每次自我年终总结,都会有各种情绪和收获。 系列目录: 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理) 【重温基础】1.语法和数据类型 【重温基础】2.流...

    ymyang 评论0 收藏0

发表评论

0条评论

garfileo

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<