对象的扩展
简洁的属性表达
-
ES6 允许直接写入变量和函数,作为对象的属性和方法;
-
属性值、方法简写
JavaScriptJavaScript// ES5 function getObj(x, y) { return {x: x, y: y}; } // 等同于 ES6 function getObj(x, y) { return {x, y}; } getObj(1, 2) // {x: 1, y: 2}
var person = {}; function getName () { return person.name } function setName () { person.name = '李四' } function clear () { person = {}; } // ES5 写法 module.exports = { getName: getName setName: setName, clear: clear }; // ES6写法 module.exports = { getName, setName, clear };
属性名表达式
-
ES5 定义字面量对象;
var obj = { name: 'ricepudding', age: 7 }
-
ES6 对对象的属性进行了扩展,可以覆盖更多的场景,属性可以使用变量的形式来定义;
JavaScriptJavaScriptJavaScript// 字面量对象中的属性是可以放在中括号中,中括号中的可以是变量,也可以是表达式 var key = 'name'; var obj = { [key]: 'ricepudding', ['a' + 'ge']: 7 }
// 属性也可以是一个带空格的字符串,在取值时在中括号中可以直接使用字符串,也可以使用变量 var str = 'first name'; var obj = { ['I love ricepudding']: 'ES6 Wiki', [str]: 'Jack' } console.log(obj['I love ricepudding']) // ES6 Wiki console.log(obj[str]) // Jack console.log(obj['first name']) // Jack
// 表达式还可以用于定义对象上的方法名 var person = { name: 'Jack', ['get' + 'Name']() { console.log(this.name) } }; console.log(person.getName()) // Jack
-
注意:
JavaScriptJavaScriptJavaScript// 属性名表达式与属性简洁表示,不能同时使用,会报错 // 报错 var name = 'Jack'; var age = 18; var person = { [name] }; // Uncaught SyntaxError: Unexpected token '}' var name = 'Jack'; var person = { [name]: 18 }; // {Jack: 18}
// 属性名必须是字符串类型的,如果属性表达式是一个对象,则会先调 toString() 先将对象转为字符串,然后才进行使用 var key1 = {name: 'ricepudding'}; var key2 = {age: 7}; var obj = { [key1]: 'value content 1', [key2]: 'value content 2', } console.log(obj); // {[object Object]: "value content 2"}
// 如果属性的表达式是数组时,则和对象不一样。数组在 toString() 后会变成一个字符串,所以对象属性的表达式就是一个字符串 var keys = ['ricepudding', '7']; var obj = { [keys]: 'value content', }; console.log(key.toString()); // "ricepudding,7" console.log(obj); // {ricepudding,7: "value content"} console.log(obj[keys]); // "value content"
方法的 name 属性
-
ES6 增加了函数的 name 属性,函数直接调用 name 会返回函数名,字面量对象上的方法也是函数,因此也有 name 属性;
var person = { name: "Jack", getName() { console.log(this.name); }, }; person.getName.name // "getName"
-
有两种特殊情况:
- Function 构造函数创造的函数,name 属性返回 “anonymous”;
- bind 方法创造的函数,name 属性返回 “bound” 加上原函数的名字;
(new Function()).name // "anonymous" var doSomething = function() { }; doSomething.bind().name // "bound doSomething"
-
如果对象的方法是一个 Symbol 值,那么 name 属性返回的是带中括号的 Symbol 的描述内容;
const key1 = Symbol('description content'); const key2 = Symbol(); let obj = { [key1]() {}, [key2]() {}, }; obj[key1].name // "[description content]" obj[key2].name // ""
可选链操作符(?.)
-
可选链操作符使用 ?. 来表示,可以判断操作符之前属性是否有效,从而链式读取对象的属性或返回 undefined;
obj?.prop // 对象上的属性 obj?.[expr] // 对象上的属性可以是一个表达式 arr?.[index] // 对数组使用时,可以接数组的位置 func?.(args) // 对函数使用时,可以接收传入的参数
-
使用场景
JavaScriptJavaScriptJavaScriptJavaScript// 可选链和表达式 let firstName = obj?.['first' + 'Name'];
// 可选链与函数调用 var person = {}; var name = person.getName?.();
// 可选链不能用于赋值 let obj = {}; obj?.name = 'ricepudding'; // Uncaught SyntaxError: Invalid left-hand side in assignment
// 可选链访问数组元素 var arr = []; let item = arr?.[5];
空值合并运算符(??)
-
?? 运算符称为 空值合并运算符,用来判断一个值是否为 null 或 undefined,与 短路或 || 的使用方式一样;
-
使用场景
const result1 = 0 ?? 10; const result2 = null ?? 10; console.log(result1); // 输出 0 console.log(result2); // 输出 10
空赋值运算符(??=)
-
??= 也被称为空赋值运算符,仅当值为 null 或 undefined 时,此赋值运算符才会赋值;
-
使用场景
var x = null; var y = 5; console.log(x ??= y); // => 5 console.log(x = (x ?? y)); // => 5
对象方法的扩展
Object.is()
-
Object.is() 会接收两个需要判断的参数,最后返回一个布尔值,如果相同则返回 true 否则返回 false;
-
基本使用
Object.is('ricepudding', 'ricepudding'); // true Object.is('ricepudding', 'rice'); // false Object.is(window, window); // true Object.is([], []); // false var foo = { a: 1 }; var bar = { a: 1 }; var obj = foo; Object.is(foo, foo); // true Object.is(foo, bar); // false Object.is(foo, obj); // true Object.is(null, null); // true // 特例 Object.is(0, -0); // false Object.is(0, +0); // true Object.is(-0, -0); // true Object.is(NaN, 0/0); // true
-
对比等号 (=)
JavaScriptJavaScriptJavaScript// == 是一个非严格相等的对比运算符,它只会对比两边的操作数是否相等,相等则会返回 true。如果对比的操作数类型不同,则会自动将值进行隐式转换为一种常见的类型 0 == -0 // true 0 == '0' // true 0 == false // true 0 == '' // true "" == false // true null == undefined // true 1 == '1' // true true == 'true' // false NaN == 'NaN' // false NaN == NaN // false {"name": "ricepudding"} == {"name": "ricepudding"} // false let a = {"name": "ricepudding"} let b = a console.log(a == b) // true
// === 运算符:=== 是严格相等的,被称作全等操作符,和 == 很相似,区别在于 === 不执行隐式类型转换。只有当两个操作值的值与类型都相等的前提下,才会返回 true +0 === -0 // true true === true // true null === null // true NaN === NaN // false, NaN永远不等于NaN 1 === '1' // false, 值类型不同:数值和字符串 true === 'true' // false null === undefined // false 'hello' === 'Hello' // false, 严格区分大小写
// Object.is() // 1. 当操作值都没有被定义时,这时它们的值是 undefined let a let b Object.is(a,b) // true // 2. Object.is() 也是严格区分大小写的 Object.is('Ricepudding', 'Ricepudding') // true Object.is('Ricepudding', 'ricepudding') // false // 3. 操作值的类型必须相同,无论是什么值,只要类型不一样就会返回 false Object.is(null, 'null') // false Object.is(undefined, 'undefined') // false // 4. 判断引用类型的值时,引用类型的地址相同时,则相等,与 == 和 === 判断的结果是一样的 let a = {"name": "ricepudding"} let b = a Object.is(a, b) // true Object.is({"name": "ricepudding"}, {"name": "ricepudding"}) // false Object.is(window, window) // true, 只有一个window全局变量 // 5. 操作数是 0、+0、-0 的比较,0 和 +0 是相等的,因为正号可以省略,但是 0 和 -0 是不相等的,这样就和 === 判断的结果不一样了 Object.is(0, +0) // true Object.is(0, -0) // false // 6. 当两个操作值是 NaN 时,使用 Object.is() 返回的结果是 true 这个和 === 返回的结果不一样,如果计算的结果是 NaN 的话,返回的结果也是 true Object.is(NaN, NaN) // true Object.is(NaN, 0/0) // true
Object.assign()
-
Object.assign() 把一个对象复制到另一个对象,在 ES5 中需要循环对象进行拷贝;
-
使用示例
JavaScriptJavaScriptJavaScriptJavaScriptJavaScriptJavaScript// 拷贝对象 let target = {}; let source = { a: 1, b: 2, c: 3 }; Object.assign(target, source); target.d = 4; console.log(target) // {a: 1, b: 2, c: 3, d: 4} console.log(source) // {a: 1, b: 2, c: 3}
// 合并对象 let target = { a: 1 }; let source1 = { b: 2 }; let source2 = { c: 3 }; Object.assign(target, source1, source2); console.log(target); // {a: 1, b: 2, c: 3}
// 覆盖前面的值 let target = { a: 1, b: 1 }; let source1 = { b: 2, c: 2 }; let source2 = { c: 3 }; Object.assign(target, source1, source2); console.log(target); // {a: 1, b: 2, c: 3}
// 浅拷贝(需要深拷贝则需要,需要递归地使用去 Object.assign() 来拷贝对象) var target = {}; var source = { a: 1, b: { c: 2, d: 3 } }; Object.assign(target, source); target.a = 5; target.b.c = 9; console.log(target) // {a: 5, b: {c: 9, d: 3}} console.log(source) // {a: 1, b: {c: 9, d: 3}}
// 基本类型的合并 console.log(Object.assign({}, "abc")); // { '0': 'a', '1': 'b', '2': 'c' } console.log(Object.assign({}, null)); // {} console.log(Object.assign({}, undefined)); // {} console.log(Object.assign({}, true)); // {} console.log(Object.assign({}, 10)); // {} console.log(Object.assign({}, Symbol("foo"))); // {} console.log(Object.assign({}, "abc", null, true, undefined, 10, Symbol("foo"))); // { '0': 'a', '1': 'b', '2': 'c' }
// 拷贝异常时会被打断:在拷贝时如果发生异常,则拷贝会被终止,并报错,前面已经被拷贝的不会被影响可以继续使用,但后面没有被拷贝的则不能被使用 var target = Object.defineProperty({}, "a", { value: 1, writable: false }); Object.assign(target, { b: 2 }, { a: 3 }, { c: 4 }); // Uncaught TypeError: Cannot assign to read only property 'a' of object ' console.log(target); // {b: 2, a: 1}
Object.keys()
-
基本实例
var obj = { name: 'hello', type: 'ES6 Wiki' } console.log(Object.keys(obj)); // ["name", "type"] var arr = ['a', 'b', 'c']; console.log(Object.keys(arr)); // console: ['0', '1', '2'] var obj = { 0: 'a', 1: 'b', 2: 'c' }; console.log(Object.keys(obj)); // ['0', '1', '2'] var obj = { name: 'hello', 10: 'a', 3: 'b', age: 7 }; console.log(Object.keys(obj)); // ["3", "10", "name", "age"]
-
自动排序问题
- 如果属性名的类型是 Number,那么 Object.keys 返回值是按照 key 从小到大排序;
- 如果属性名的类型是 String,那么 Object.keys 返回值是按照属性被创建的时间升序排序;
- 如果属性名的类型是 Symbol,那么逻辑同 String 相同;
Object.values()
-
Object.values() 方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用 for…in 循环的顺序相同 (区别在于 for-in 循环枚举原型链中的属性);
-
与 Object.keys() 相反一个是获取 key,一个是获取 value,其他的使用特性基本相同;
- 参数是数值或布尔值:Object.values() 和 Object.keys() 一样都会进行类型转换,所以传入的是数字或布尔值时,则返回一个空数组;
- 键排序问题:Object.values() 返回的数组是按 Object.keys() 顺序后的结果展示的,所以得到的值要和排序后的属性一一对应;
- 参数为字符串:传入的对象如果是一个字符串时,则会把字符拆成数组中的单个项;
Object.entries()
-
Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组;
-
示例
var obj = {a: 1, b: 2, c: 3}; console.log(Object.entries(obj)); // [['a', 1], ['b', 3], ['c', 3]] Object.entries(50); // [] Object.entries(false); // [] var obj = {10: 'a', 1: 'b', 7: 'c'}; console.log(Object.entries(obj)); // [["1", "b"], ["7", "c"], ["10", "a"]] console.log(Object.entries('abc')); // [["0", "a"], ["1", "b"], ["2", "c"]]
Object.fromEntries()
-
Object.fromEntries() 是 Object.entries() 的反转函数,这样理解起来就比较轻松,它接收的是一个可迭代的对象,类似 Array、Map 或者其它实现了可迭代协议的对象,结果返回一个新的对象;
-
示例:
JavaScriptJavaScript// 数组转化为对象 var arr = [ ['a', '0'], ['b', '1'], ['c', '2'] ]; var obj = Object.fromEntries(arr); console.log(obj); // {a: "0", b: "1", c: "2"}
// Map 转化为对象 var map = new Map(); map.set('name', 'hello'); map.set('age', 7); console.log(map); // Map(2) {"name" => "hello", "age" => 7} var obj = Object.fromEntries(map); console.log(obj); // {name: "hello", age: 7}
ES6+ 数组方法扩展
上一篇