Ref: 对象的扩展

Outline:

  1. 属性的简洁表示法
  2. 属性名表达式
  3. 方法的 name 属性
  4. Object.is()
  5. Object.assign()
  6. 属性的可枚举性和遍历
  7. Object.getOwnPropertyDescriptors()
  8. __proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()
  9. super 关键字
  10. Object.keys(),Object.values(),Object.entries()
  11. 对象的扩展运算符

简洁表示法

ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

  • 属性:
  1. // 推荐
    function f(x, y) {
  2. return {x, y};
  3. }
  4.  
  5. // 等同于
  6.  
  7. function f(x, y) {
  8. return {x: x, y: y};
  9. }
  10.  
  11. f(1, 2) // Object {x: 1, y: 2}
  • 方法:
  1. // 推荐
    const o = {
  2. method() { return "Hello!"; }
  3. };
  4.  
  5. // 等同于
  6.  
  7. const o = {
  8. method: function() {
  9. return "Hello!";
  10. }
  11. };
  • 变量名就是属性名的写法:
  1. let ms = {};
  2.  
  3. function getItem (key) {
  4. return key in ms ? ms[key] : null;
  5. }
  6.  
  7. function setItem (key, value) {
  8. ms[key] = value;
  9. }
  10.  
  11. function clear () {
  12. ms = {};
  13. }
  14.  
  15. module.exports = { getItem, setItem, clear };  // <---- 如此,简洁了许多
  16. // 等同于
  17. module.exports = {
  18. getItem: getItem,
  19. setItem: setItem,
  20. clear: clear
  21. };
  • 属性的赋值器(setter)和取值器(getter):
  1. const cart = {
  2. _wheels: 4,

  3.  // 采用了简洁的写法
  4. get wheels () {
  5. return this._wheels;
  6. },
  7.  // 采用了简洁的写法
  8. set wheels (value) {
  9. if (value < this._wheels) {
  10. throw new Error('数值太小了!');
  11. }
  12. this._wheels = value;
  13. }
  14. }
  • 定义属性名 - 把表达式放在方括号内
  1. let lastWord = 'last word';
  2.  
  3. const a = {
  4. 'first word': 'hello',
  5. [lastWord]: 'world'
  6. };
  7.  
  8. a['first word'] // "hello"
  9. a[lastWord] // "world"
  10. a['last word'] // "world"
  • 定义方法名 - 把表达式放在方括号内
  1. let obj = {
  2. ['h' + 'ello']() {
  3. return 'hi';
  4. }
  5. };
  6.  
  7. obj.hello() // hi

雷区:注意对[...]的理解

  1. // 报错
  2. const foo = 'bar';
  3. const bar = 'abc';
  4. const baz = { [foo] };  // bar有不是值,当然不能这么写;[]只是告诉编译器,你需要计算一下才能得到结果
  5.  
  6. // 正确
  7. const foo = 'bar';
  8. const baz = { [foo]: 'abc'};

雷区:[keyA][keyB]得到的都是[object Object],所以[keyB]会把[keyA]覆盖掉,而myObject最后只有一个[object Object]属性。

  1. const keyA = {a: 1};
  2. const keyB = {b: 2};
  3.  
  4. const myObject = {
  5. [keyA]: 'valueA',
  6. [keyB]: 'valueB'  // Object的内部细节看不到,所以就引发了”覆盖“.
  7. };
  8.  
  9. myObject // Object {[object Object]: "valueB"}

对象方法的 name 属性

返回函数名(即方法名)。

get or set方法,需要特殊处理:

  1. const obj = {
  2. get foo() {},
  3. set foo(x) {}
  4. };
  5.  
  6. obj.foo.name
  7. // TypeError: Cannot read property 'name' of undefined
  8.  
  9. const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');  // <---- 需要特殊处理
  10.  
  11. descriptor.get.name // "get foo"
  12. descriptor.set.name // "set foo"

有两种特殊情况:

bind方法创造的函数 --> name属性返回bound加上原函数的名字;

Function构造函数创造的函数 --> name属性返回anonymous

如果对象的方法是一个 Symbol 值,那么name属性返回的是这个 Symbol 值的描述。

key1对应的 Symbol 值有描述,key2没有。

Object.is()

给相等运算符(==)和严格相等运算符(===)进一步加了补丁。

与严格比较运算符(===)的行为基本一致。

不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

  1. Object.is('foo', 'foo')
  2. // true
  3. Object.is({}, {})
  4. // false
  5.  
  6. +0 === -0 //true
  7. NaN === NaN // false
  8.  
  9. Object.is(+0, -0) // false
  10. Object.is(NaN, NaN) // true

ES5 可以通过下面的代码,部署Object.is。【给es5一个打补丁的机会】

Object.assign()

将源对象(source)的所有可枚举属性,复制到目标对象(target)。This is 是浅拷贝

如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

  1. const target = { a: 1, b: 1 };
  2.  
  3. const source1 = { b: 2, c: 2 };
  4. const source2 = { c: 3 };
  5.  
  6. Object.assign(target, source1, source2);
  7. target // {a:1, b:2, c:3}

如果只有一个参数,Object.assign会直接返回该参数。

如果该参数不是对象,则会先转成对象,然后返回。【强制类型转换】

雷区:由于undefinednull无法转成对象,所以如果它们作为参数,就会报错。

  1. Object.assign(undefined) // 报错
  2. Object.assign(null) // 报错

雷区:如果undefinednull不在首参数,就不会报错。

  1. let obj = {a: 1};
  2. Object.assign(obj, undefined) === obj // true
  3. Object.assign(obj, null) === obj // true

雷区:只有字符串的包装对象,会产生可枚举属性;数值和布尔值都会被忽略。

  1. const v1 = 'abc';
  2. const v2 = true;
  3. const v3 = 10;
  4.  
  5. const obj = Object.assign({}, v1, v2, v3);
  6. console.log(obj); // { "0": "a", "1": "b", "2": "c" }
  1. Object(true) // {[[PrimitiveValue]]: true}
  2. Object(10) // {[[PrimitiveValue]]: 10}
  3. Object('abc') // {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}  // 可见,只有字符串的包装对象,会产生可枚举的实义属性,会被拷贝。

雷图:属性名为 Symbol 值的属性,也会被Object.assign拷贝。

  1. Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' })
  2. // { a: 'b', Symbol(c): 'd' }

把数组视为对象,以及同名属性(index)的替换

  1. Object.assign([1, 2, 3], [4, 5])
  2. // [4, 5, 3]

取值函数的处理

  1. const source = {
  2. get foo() { return 1 }
  3. };
  4. const target = {};
  5.  
  6. Object.assign(target, source)
  7. // { foo: 1 }

常见用途

  • 为对象添加属性/方法
  1. class Point {
  2. constructor(x, y) {
  3. Object.assign(this, {x, y});  // 将x属性和y属性添加到Point类的对象实例
  4. }
  5. }
  1. // 方法
  2.  
  3. Object.assign(SomeClass.prototype, {
  4. someMethod(arg1, arg2) {  // 直接写进来即可
  5. ···
  6. },
  7. anotherMethod() {      // 直接写进来即可
  8. ···
  9. }
  10. });
  11. -----------------------------------------------------------------------
  12. // 等同于下面的写法
  13. SomeClass.prototype.someMethod = function (arg1, arg2) {
  14. ···
  15. };
  16. SomeClass.prototype.anotherMethod = function () {
  17. ···
  18. };
  • 克隆对象
  1. function clone(origin) {
  2. return Object.assign({}, origin);
  3. }
  1. // 如果想要保持继承链,可以采用下面的代码
  2.  
  3. function clone(origin) {
  4. let originProto = Object.getPrototypeOf(origin);
  5. return Object.assign(Object.create(originProto), origin);
  6. }
  • 合并多个对象
  1. const merge =
  2. (target, ...sources) => Object.assign(target, ...sources);
  3.  
  4. const merge =
  5. (...sources) => Object.assign({}, ...sources); // 合并后返回一个新对象
  • 为属性指定默认值
  1. const DEFAULTS = {
  2. logLevel: 0,
  3. outputFormat: 'html'
  4. };
  5.  
  6. function processContent(options) {
  7. options = Object.assign({}, DEFAULTS, options);  // 将DEFAULTSoptions合并成一个新对象,如果两者有同名属性,则option的属性值会覆盖DEFAULTS的属性值。
  8. console.log(options);
  9. // ...
  10. }
  11.  
  12. --------------------------------------------------
  1. const DEFAULTS = {
  2. url: {
  3. host: 'example.com',
  4. port: 7070
  5. },
  6. };
  7. processContent({ url: {port: 8000} })  // 不是替换port,而是直接把url给覆盖掉了,哈哈
  8. // {
  9. // url: {port: 8000}
  10. // }

属性的可枚举性和遍历

  • 属性的描述对象(Descriptor)

描述对象(Descriptor),用来控制该属性的行为。

- Object.getOwnPropertyDescriptor方法

如下:描述对象的enumerable属性,称为”可枚举性“,如果该属性为false,就表示某些操作会忽略当前属性。

      • for...in循环:只遍历对象自身的和继承的可枚举的属性。
      • Object.keys():返回对象自身的所有可枚举的属性的键名。
      • JSON.stringify():只串行化对象自身的可枚举的属性。
      • Object.assign(): 忽略enumerablefalse的属性,只拷贝对象自身的可枚举的属性。

- Object.getOwnPropertyDescriptors方法

ES2017 引入了Object.getOwnPropertyDescriptors方法,返回指定对象所有自身属性(非继承属性)的描述对象。

例一:

  1. let obj = { foo: 123 };

例二:

  1. Object.getOwnPropertyDescriptor( Object.prototype, 'toString' ).enumerable
  2. // false
  3.  
  4. Object.getOwnPropertyDescriptor( [], 'length' ).enumerable
  5. // false
  6.  
  7. Object.getOwnPropertyDescriptor( class {foo() {}}.prototype, 'foo' ).enumerable
  8. // false

ES6 规定,所有 Class 的原型的方法都是不可枚举的。

  • 属性的遍历

(1)for...in

(2)Object.keys(obj)

(3)Object.getOwnPropertyNames(obj)

(4)Object.getOwnPropertySymbols(obj)

(5)Reflect.ownKeys(obj)

以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。

    • 首先遍历所有数值键,按照数值升序排列。
    • 其次遍历所有字符串键,按照加入时间升序排列。
    • 最后遍历所有 Symbol 键,按照加入时间升序排列。
  1. Reflect.ownKeys({ [Symbol()]:, b:, :, :, a: })
    // ['2', '10', 'b', 'a', Symbol()]

遍历对象

ES2017 引入了跟Object.keys配套的Object.valuesObject.entries,作为遍历一个对象的补充手段,供for...of循环使用。

  • Object.keys()
  1. let {keys, values, entries} = Object;
  2. let obj = { a: 1, b: 2, c: 3 };
  3.  
  4. for (let key of keys(obj)) {
  5. console.log(key); // 'a', 'b', 'c'
  6. }
  7.  
  8. for (let value of values(obj)) {
  9. console.log(value); // 1, 2, 3
  10. }
  11.  
  12. for (let [key, value] of entries(obj)) {
  13. console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
  14. }
  • Object.values()

只返回对象自身的可遍历的属性,比如value是个对象,那么就要将该对象内的enumerate设置为true,才能遍历到。

  1. const obj = Object.create({}, {p: {value: 42}});
  2. Object.values(obj) // []
  3.  
  4. -------------------------------------------------
  1. const obj = Object.create({}, {p:
  2. {
  3. value: 42,
  4. enumerable: true
  5. }
  6. });
  7. Object.values(obj) // [42]
  • Object.entries()

返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

解构赋值与扩展运算符

略,之前已有总结。

[JS] ECMAScript 6 - Object : compare with c#的更多相关文章

  1. [JS] ECMAScript 6 - Variable : compare with c#

    前言 范围包括:ECMAScript 新功能以及对象. 当前的主要目的就是,JS的学习 --> ECMAScript 6 入门 let 命令 js 因为let, i的范围限制在了循环中. var ...

  2. [JS] ECMAScript 6 - Class : compare with c#

    Ref: Class 的基本语法 Ref: Class 的基本继承 许多面向对象的语言都有修饰器(Decorator)函数,用来修改类的行为.目前,有一个提案将这项功能,引入了 ECMAScript. ...

  3. [JS] ECMAScript 6 - Inheritance : compare with c#

    这一章,估计是js最操蛋的一部分内容. 现代方法: 简介 Object.getPrototypeOf() super 关键字 类的 prototype 属性和__proto__属性 原生构造函数的继承 ...

  4. [JS] ECMAScript 6 - Prototype : compare with c#

    开胃菜 prototype 对象 JavaScript 语言的继承则是通过“原型对象”(prototype). function Cat(name, color) { // <----构造函数 ...

  5. [JS] ECMAScript 6 - Array : compare with c#

    扩展运算符(spread) 先复习下 rest 参数. (1) argument模式,但不够好. // https://blog.csdn.net/weixin_39723544/article/de ...

  6. [JS] ECMAScript 6 - Async : compare with c#

    一段引言: Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大. 它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对 ...

  7. js types & primitive & object

    js types & primitive & object js 数据类型 typeof null // "object" typeof undefined // ...

  8. JS 深度拷贝 Object Array

    JS 深度拷贝 Object Array function cloneObj(o) { var isArray = o instanceof Array; var isObject = o insta ...

  9. JS如何遍历Object中的所有属性?

    JS如何遍历Object中的所有属性? var params = ""; for(var i in baseParams){ params += "&" ...

随机推荐

  1. javac编译出现需要标识符问题解决

    因为没有写public static void mian(String[] args) 在类里面只有属性和方法,内部类.不能直接写System.out.println():

  2. centos7 重置root 密码

    重置Centos 7 Root密码的方式和Centos 6完全不同.让我来展示一下到底如何操作. 1 - 在启动grub菜单,选择编辑选项启动 2 - 按键盘e键,来进入编辑界面 3 - 找到Linu ...

  3. POI设置excle单元格样式

    Java利用POI生成Excel强制换行 使用POI创建一个简单的   myXls.xls   文件       常用的包为   org.apache.poi.hssf.usermodel.*;    ...

  4. AB Test 是什么

    关于AB Test是什么 一种灰度发布方式. ps:什么是灰度发布 每个灰度对象都是0%(白色)到100%(黑色)的中间值,灰度发布是指在黑白之间,能够平滑过度的一种发布方式. 实现方式 让一部分用户 ...

  5. boolean和Boolean, char和Character , byte和Byte, short和Short, int和Integer , long和Long , float和Float, double和Double的区别 , String和StringBuffer的区别

    Java提供两种不同的类型:引用类型和原始类型(内置类型).Int是java的原始数据类型,Integer是java为int提供的封装类. Java为每个原始数据类型提供了封装类. 其中原始数据类型封 ...

  6. facebook's HipHop for PHP: Move Fast

    One of the key values at Facebook is to move fast. For the past six years, we have been able to acco ...

  7. Amazon.com 美国亚马逊 直邮中国 手把手教程(转)

    什么值得买已经发布2014最新版美亚直邮攻略 海淘攻略:美国亚马逊 直邮服务 手把手教程(2014最新版) ,调整幅度较大,值友们可以移步到新攻略中查看. 相比德国.英国亚马逊,美国亚马逊的大部分商品 ...

  8. 018-Go将磁盘目录实现简单的静态Web服务

    package main import( "net/http" ) func main(){ http.Handle("/", http.FileServer( ...

  9. Ubuntu16.04下安装搭配Python3.6相关配置软件方法

    1 安装Python3.6.4 此处推荐直接安装Anaconda3,来实现Python3.6.4的环境配置. Anaconda3下载链接:https://www.anaconda.com/downlo ...

  10. PPTP 在Centos 7.1 建立方法与失效处理

    # yum install pptp pptp-setup pptpsetup --create vpn --server  (serverName) --username (Username)  - ...