[JS] ECMAScript 6 - Object : compare with c#
Ref: 对象的扩展
Outline:
- 属性的简洁表示法
- 属性名表达式
- 方法的 name 属性
- Object.is()
- Object.assign()
- 属性的可枚举性和遍历
- Object.getOwnPropertyDescriptors()
- __proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()
- super 关键字
- Object.keys(),Object.values(),Object.entries()
- 对象的扩展运算符
简洁表示法
ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
- 属性:
- // 推荐
function f(x, y) {- return {x, y};
- }
- // 等同于
- function f(x, y) {
- return {x: x, y: y};
- }
- f(1, 2) // Object {x: 1, y: 2}
- 方法:
- // 推荐
const o = {- method() { return "Hello!"; }
- };
- // 等同于
- const o = {
- method: function() {
- return "Hello!";
- }
- };
- 变量名就是属性名的写法:
- let ms = {};
- function getItem (key) {
- return key in ms ? ms[key] : null;
- }
- function setItem (key, value) {
- ms[key] = value;
- }
- function clear () {
- ms = {};
- }
- module.exports = { getItem, setItem, clear }; // <---- 如此,简洁了许多
- // 等同于
- module.exports = {
- getItem: getItem,
- setItem: setItem,
- clear: clear
- };
- 属性的赋值器(setter)和取值器(getter):
- const cart = {
- _wheels: 4,
// 采用了简洁的写法- get wheels () {
- return this._wheels;
- },
- // 采用了简洁的写法
- set wheels (value) {
- if (value < this._wheels) {
- throw new Error('数值太小了!');
- }
- this._wheels = value;
- }
- }
- 定义属性名 - 把表达式放在方括号内
- let lastWord = 'last word';
- const a = {
- 'first word': 'hello',
- [lastWord]: 'world'
- };
- a['first word'] // "hello"
- a[lastWord] // "world"
- a['last word'] // "world"
- 定义方法名 - 把表达式放在方括号内
- let obj = {
- ['h' + 'ello']() {
- return 'hi';
- }
- };
- obj.hello() // hi
雷区:注意对[...]的理解
- // 报错
- const foo = 'bar';
- const bar = 'abc';
- const baz = { [foo] }; // bar有不是值,当然不能这么写;[]只是告诉编译器,你需要计算一下才能得到结果
- // 正确
- const foo = 'bar';
- const baz = { [foo]: 'abc'};
雷区:[keyA]
和[keyB]
得到的都是[object Object]
,所以[keyB]
会把[keyA]
覆盖掉,而myObject
最后只有一个[object Object]
属性。
- const keyA = {a: 1};
- const keyB = {b: 2};
- const myObject = {
- [keyA]: 'valueA',
- [keyB]: 'valueB' // Object的内部细节看不到,所以就引发了”覆盖“.
- };
- myObject // Object {[object Object]: "valueB"}
对象方法的 name 属性
返回函数名(即方法名)。
get or set方法,需要特殊处理:
- const obj = {
- get foo() {},
- set foo(x) {}
- };
- obj.foo.name
- // TypeError: Cannot read property 'name' of undefined
- const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo'); // <---- 需要特殊处理
- descriptor.get.name // "get foo"
- descriptor.set.name // "set foo"
有两种特殊情况:
bind
方法创造的函数 --> name
属性返回bound
加上原函数的名字;
Function
构造函数创造的函数 --> name
属性返回anonymous
。
如果对象的方法是一个 Symbol 值,那么name
属性返回的是这个 Symbol 值的描述。
key1
对应的 Symbol 值有描述,key2
没有。
Object.is()
给相等运算符(==
)和严格相等运算符(===
)进一步加了补丁。
与严格比较运算符(===)的行为基本一致。
不同之处只有两个:一是+0
不等于-0
,二是NaN
等于自身。
- Object.is('foo', 'foo')
- // true
- Object.is({}, {})
- // false
- +0 === -0 //true
- NaN === NaN // false
- Object.is(+0, -0) // false
- Object.is(NaN, NaN) // true
ES5 可以通过下面的代码,部署Object.is
。【给es5一个打补丁的机会】
Object.assign()
将源对象(source)的所有可枚举属性,复制到目标对象(target)。This is 是浅拷贝!
如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
- const target = { a: 1, b: 1 };
- const source1 = { b: 2, c: 2 };
- const source2 = { c: 3 };
- Object.assign(target, source1, source2);
- target // {a:1, b:2, c:3}
如果只有一个参数,Object.assign
会直接返回该参数。
如果该参数不是对象,则会先转成对象,然后返回。【强制类型转换】
雷区:由于undefined
和null
无法转成对象,所以如果它们作为参数,就会报错。
- Object.assign(undefined) // 报错
- Object.assign(null) // 报错
雷区:如果undefined
和null
不在首参数,就不会报错。
- let obj = {a: 1};
- Object.assign(obj, undefined) === obj // true
- Object.assign(obj, null) === obj // true
雷区:只有字符串的包装对象,会产生可枚举属性;数值和布尔值都会被忽略。
- const v1 = 'abc';
- const v2 = true;
- const v3 = 10;
- const obj = Object.assign({}, v1, v2, v3);
- console.log(obj); // { "0": "a", "1": "b", "2": "c" }
Object(true) // {[[PrimitiveValue]]: true}
Object(10) // {[[PrimitiveValue]]: 10}
Object('abc') // {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"} // 可见,只有字符串的包装对象,会产生可枚举的实义属性,会被拷贝。
雷图:属性名为 Symbol 值的属性,也会被Object.assign
拷贝。
- Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' })
- // { a: 'b', Symbol(c): 'd' }
把数组视为对象,以及同名属性(index)的替换
- Object.assign([1, 2, 3], [4, 5])
- // [4, 5, 3]
取值函数的处理
- const source = {
- get foo() { return 1 }
- };
- const target = {};
- Object.assign(target, source)
- // { foo: 1 }
常见用途
- 为对象添加属性/方法
- class Point {
- constructor(x, y) {
- Object.assign(this, {x, y}); // 将
x
属性和y
属性添加到Point
类的对象实例- }
- }
- // 方法
- Object.assign(SomeClass.prototype, {
- someMethod(arg1, arg2) { // 直接写进来即可
- ···
- },
- anotherMethod() { // 直接写进来即可
- ···
- }
- });
- -----------------------------------------------------------------------
- // 等同于下面的写法
- SomeClass.prototype.someMethod = function (arg1, arg2) {
- ···
- };
- SomeClass.prototype.anotherMethod = function () {
- ···
- };
- 克隆对象
- function clone(origin) {
- return Object.assign({}, origin);
- }
- // 如果想要保持继承链,可以采用下面的代码
- function clone(origin) {
- let originProto = Object.getPrototypeOf(origin);
- return Object.assign(Object.create(originProto), origin);
- }
- 合并多个对象
- const merge =
- (target, ...sources) => Object.assign(target, ...sources);
- const merge =
- (...sources) => Object.assign({}, ...sources); // 合并后返回一个新对象
- 为属性指定默认值
- const DEFAULTS = {
- logLevel: 0,
- outputFormat: 'html'
- };
- function processContent(options) {
- options = Object.assign({}, DEFAULTS, options); // 将
DEFAULTS
和options
合并成一个新对象,如果两者有同名属性,则option
的属性值会覆盖DEFAULTS
的属性值。- console.log(options);
- // ...
- }
- --------------------------------------------------
const DEFAULTS = {
url: {
host: 'example.com',
port: 7070
},
};
processContent({ url: {port: 8000} }) // 不是替换port,而是直接把url给覆盖掉了,哈哈
// {
// url: {port: 8000}
// }
属性的可枚举性和遍历
- 属性的描述对象(Descriptor)
描述对象(Descriptor),用来控制该属性的行为。
- Object.getOwnPropertyDescriptor
方法
如下:描述对象的enumerable
属性,称为”可枚举性“,如果该属性为false
,就表示某些操作会忽略当前属性。
for...in
循环:只遍历对象自身的和继承的可枚举的属性。Object.keys()
:返回对象自身的所有可枚举的属性的键名。JSON.stringify()
:只串行化对象自身的可枚举的属性。Object.assign()
: 忽略enumerable
为false
的属性,只拷贝对象自身的可枚举的属性。
- Object.getOwnPropertyDescriptors
方法
ES2017 引入了Object.getOwnPropertyDescriptors
方法,返回指定对象所有自身属性(非继承属性)的描述对象。
例一:
- let obj = { foo: 123 };
例二:
- Object.getOwnPropertyDescriptor( Object.prototype, 'toString' ).enumerable
- // false
- Object.getOwnPropertyDescriptor( [], 'length' ).enumerable
- // false
- Object.getOwnPropertyDescriptor( class {foo() {}}.prototype, 'foo' ).enumerable
- // false
ES6 规定,所有 Class 的原型的方法都是不可枚举的。
- 属性的遍历
(1)for...in
(2)Object.keys(obj)
(3)Object.getOwnPropertyNames(obj)
(4)Object.getOwnPropertySymbols(obj)
(5)Reflect.ownKeys(obj)
以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。
- 首先遍历所有数值键,按照数值升序排列。
- 其次遍历所有字符串键,按照加入时间升序排列。
- 最后遍历所有 Symbol 键,按照加入时间升序排列。
- Reflect.ownKeys({ [Symbol()]:, b:, :, :, a: })
// ['2', '10', 'b', 'a', Symbol()]
遍历对象
ES2017 引入了跟Object.keys
配套的Object.values
和Object.entries
,作为遍历一个对象的补充手段,供for...of
循环使用。
- Object.keys()
- let {keys, values, entries} = Object;
- let obj = { a: 1, b: 2, c: 3 };
- for (let key of keys(obj)) {
- console.log(key); // 'a', 'b', 'c'
- }
- for (let value of values(obj)) {
- console.log(value); // 1, 2, 3
- }
- for (let [key, value] of entries(obj)) {
- console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
- }
- Object.values()
只返回对象自身的可遍历的属性,比如value是个对象,那么就要将该对象内的enumerate设置为true,才能遍历到。
- const obj = Object.create({}, {p: {value: 42}});
- Object.values(obj) // []
- -------------------------------------------------
const obj = Object.create({}, {p:
{
value: 42,
enumerable: true
}
});
Object.values(obj) // [42]
- Object.entries()
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
解构赋值与扩展运算符
略,之前已有总结。
[JS] ECMAScript 6 - Object : compare with c#的更多相关文章
- [JS] ECMAScript 6 - Variable : compare with c#
前言 范围包括:ECMAScript 新功能以及对象. 当前的主要目的就是,JS的学习 --> ECMAScript 6 入门 let 命令 js 因为let, i的范围限制在了循环中. var ...
- [JS] ECMAScript 6 - Class : compare with c#
Ref: Class 的基本语法 Ref: Class 的基本继承 许多面向对象的语言都有修饰器(Decorator)函数,用来修改类的行为.目前,有一个提案将这项功能,引入了 ECMAScript. ...
- [JS] ECMAScript 6 - Inheritance : compare with c#
这一章,估计是js最操蛋的一部分内容. 现代方法: 简介 Object.getPrototypeOf() super 关键字 类的 prototype 属性和__proto__属性 原生构造函数的继承 ...
- [JS] ECMAScript 6 - Prototype : compare with c#
开胃菜 prototype 对象 JavaScript 语言的继承则是通过“原型对象”(prototype). function Cat(name, color) { // <----构造函数 ...
- [JS] ECMAScript 6 - Array : compare with c#
扩展运算符(spread) 先复习下 rest 参数. (1) argument模式,但不够好. // https://blog.csdn.net/weixin_39723544/article/de ...
- [JS] ECMAScript 6 - Async : compare with c#
一段引言: Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大. 它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对 ...
- js types & primitive & object
js types & primitive & object js 数据类型 typeof null // "object" typeof undefined // ...
- JS 深度拷贝 Object Array
JS 深度拷贝 Object Array function cloneObj(o) { var isArray = o instanceof Array; var isObject = o insta ...
- JS如何遍历Object中的所有属性?
JS如何遍历Object中的所有属性? var params = ""; for(var i in baseParams){ params += "&" ...
随机推荐
- javac编译出现需要标识符问题解决
因为没有写public static void mian(String[] args) 在类里面只有属性和方法,内部类.不能直接写System.out.println():
- centos7 重置root 密码
重置Centos 7 Root密码的方式和Centos 6完全不同.让我来展示一下到底如何操作. 1 - 在启动grub菜单,选择编辑选项启动 2 - 按键盘e键,来进入编辑界面 3 - 找到Linu ...
- POI设置excle单元格样式
Java利用POI生成Excel强制换行 使用POI创建一个简单的 myXls.xls 文件 常用的包为 org.apache.poi.hssf.usermodel.*; ...
- AB Test 是什么
关于AB Test是什么 一种灰度发布方式. ps:什么是灰度发布 每个灰度对象都是0%(白色)到100%(黑色)的中间值,灰度发布是指在黑白之间,能够平滑过度的一种发布方式. 实现方式 让一部分用户 ...
- 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为每个原始数据类型提供了封装类. 其中原始数据类型封 ...
- 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 ...
- Amazon.com 美国亚马逊 直邮中国 手把手教程(转)
什么值得买已经发布2014最新版美亚直邮攻略 海淘攻略:美国亚马逊 直邮服务 手把手教程(2014最新版) ,调整幅度较大,值友们可以移步到新攻略中查看. 相比德国.英国亚马逊,美国亚马逊的大部分商品 ...
- 018-Go将磁盘目录实现简单的静态Web服务
package main import( "net/http" ) func main(){ http.Handle("/", http.FileServer( ...
- Ubuntu16.04下安装搭配Python3.6相关配置软件方法
1 安装Python3.6.4 此处推荐直接安装Anaconda3,来实现Python3.6.4的环境配置. Anaconda3下载链接:https://www.anaconda.com/downlo ...
- PPTP 在Centos 7.1 建立方法与失效处理
# yum install pptp pptp-setup pptpsetup --create vpn --server (serverName) --username (Username) - ...