目录
以往ES6文章
前言
对象属性的简洁表示法
一个实际例子
简介写法在打印对象时也很有用
注意
对象属性名表达式
用表达式做属性名
用表达式定义方法名
注意
对象方法的name属性
对象属性的可枚举性和遍历
可枚举性
属性的遍历
属性比那里次序规则
super关键字
注意
对象的链判断运算符
链判断运算符的用法
链判断运算符的机制
短路机制
delete运算符
括号的影响
报错场合
右侧不得为十进制数值
NULL判断符
NULL判断符的作用
NULL的问题
以往ES6文章
ES6标准---【一】【学习ES6看这一篇就够了!!】-CSDN博客
ES6标准---【二】【学习ES6看这一篇就够了!!】-CSDN博客
ES6标准---【三】【学习ES6看这一篇就够了!!!】-CSDN博客
ES6标准---【四】【学习ES6标准看这一篇就够了!!!】_es6 有arguments 吗-CSDN博客
ES6标准---【五】【看这一篇就够了!!!】-CSDN博客
前言
ES6标准对“对象”进行了扩展
对象属性的简洁表示法
ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法
- 直接写入变量时:变量名做键,变量值做键值
- 直接写入函数时,无需添加“:”(冒号)
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同于
const baz = {foo: foo};
function f(x, y) {
return {x, y};
}
// 等同于
function f(x, y) {
return {x: x, y: y};
}
f(1, 2) // Object {x: 1, y: 2}
一个实际例子
let birth = '2000/01/01';
const Person = {
name: '张三',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello() { console.log('我的名字是', this.name); }
};
简介写法在打印对象时也很有用
let user = {
name: 'test'
};
let foo = {
bar: 'baz'
};
console.log(user, foo)
// {name: "test"} {bar: "baz"}
console.log({user, foo})
// {user: {name: "test"}, foo: {bar: "baz"}}
如果console.log直接输出“user”和“foo”两个对象时,就是两组键值对,可能会混淆
但是如果把它们放在大括号里面输出,就变成了对象的简洁表示,每组键值对前面会打印对象名,这样就比较清晰了
注意
简写的对象方法不能用作构造函数,会报错
const obj = {
f() {
this.foo = 'bar';
}
};
new obj.f() // 报错
对象属性名表达式
JavaScript定义对象的属性,有两种方法:
- 直接用标识符作属性名
- 用表达式做属性名(需要将表达式放在方括号内)
// 方法一
obj.foo = true;
// 方法二
obj['a' + 'bc'] = 123;
用表达式做属性名
ES6允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在“方括号”内
let propKey = 'foo';
let obj = {
[propKey]: true,
['a' + 'bc']: 123
};
- 用变量做属性名时,可以使用变量名或变量值来检索对象
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] };
// 正确
const foo = 'bar';
const baz = { [foo]: 'abc'};
- 属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串“[obejct Object]”
const keyA = {a: 1};
const keyB = {b: 2};
const myObject = {
[keyA]: 'valueA',
[keyB]: 'valueB'
};
myObject // Object {[object Object]: "valueB"}
对象方法的name属性
对象方法也是函数,因此也有name属性
const person = {
sayName() {
console.log('hello!');
},
};
person.sayName.name // "sayName"
- 如果对象的方法使用了取值函数(getter)和存值函数(setter),则name的返回值是“方法名前加上get 和 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
(new Function()).name // "anonymous"
var doSomething = function() {
// ...
};
doSomething.bind().name // "bound doSomething"
对象属性的可枚举性和遍历
可枚举性
对象的每个属性都有一个描述对象,用来控制该属性的行为
- Obejct.getOwnPropertyDesciptor:获取该属性的描述对象
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
// {
// value: 123,
// writable: true,
// enumerable: true,
// configurable: true
// }
描述对象的“enumrable”属性,称为“可枚举性”,如果该属性为false,就表示某些操作会忽略当前属性
有四个操作会忽略enumerable为false的属性
- for...in循环:只遍历对象自身的和可继承的可枚举的属性
- Obejct.keys():返回对象自身的所有可枚举的属性的键名
- JSON.stringfy():只串行化对象自身的可枚举的属性
- Object.assign():忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性
属性的遍历
ES6中一共有五种方法可以遍历对象的属性
- for...in:循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)
- Object.keys(obj):返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)的键名
- Object.getOwnPropertyNames(obj):返回一个数组,包含对象自身的所有属性(不含Symol属性)的键名
- Obejct.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有Symbol属性的键名
- Reflect.ownKeys(obj):返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是否是Symbol或字符串,也不管是否可枚举
<body>
<script>
let obj = {
"name": "yy",
"age": 123
}
for(let i in obj) {
console.log(i);
}
console.log("Object.keys:",Object.keys(obj));
console.log("Object.getOwnPropertyNames:",Object.getOwnPropertyNames(obj));
console.log("Object.getOwnPropertySymbols:",Object.getOwnPropertySymbols(obj));
console.log("Reflect.ownKeys:",Reflect.ownKeys(obj));
</script>
</body>
效果:
属性比那里次序规则
ES6中的五种方法遍历对象的键名,都遵守同样的属性遍历的次序规则:
- 首先遍历所有数值键,按照数值升序排列
- 其次遍历所有字符串键,按照加入事件升序排列
- 最后遍历所有Symbol键,按照加入时间排列
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]
super关键字
super指向当前对象的原型对象(父亲)
const proto = {
foo: 'hello'
};
const obj = {
foo: 'world',
find() {
return super.foo;
}
};
Object.setPrototypeOf(obj, proto);
obj.find() // "hello"
注意
super关键字指向原型对象时,只能用在对象的方法之中,用在其他地方都会报错
// 报错
const obj = {
foo: super.foo
}
// 报错
const obj = {
foo: () => super.foo
}
// 报错
const obj = {
foo: function () {
return super.foo
}
}
对象的链判断运算符
ES6增加了“?.”运算符,直接在链式调用的时候判断,左侧的对象是否为NULL或undefined,如果是就不再往下运算,而是返回undefined
链判断运算符的用法
链判断运算符有三种用法:
- obj?.prop:对象属性
- obj?.[expr]:对象属性
- obj(function)?.(...args):函数或对象方法的调用
a?.b
// 等同于
a == null ? undefined : a.b
a?.[x]
// 等同于
a == null ? undefined : a[x]
a?.b()
// 等同于
a == null ? undefined : a.b()
a?.()
// 等同于
a == null ? undefined : a()
注意点:
- 如果a?.b()里面a.b不是函数,或不可调用,那么会报错
- 如果a?.()也是如此,如果a不是null或undefined,也不是函数,那么会报错
链判断运算符的机制
短路机制
如果对象是undefined或null,那么不会执行后面的表达式
a?.[++x]
// 等同于
a == null ? undefined : a[++x]
delete运算符
如果对象是undefined或null,会直接返回undefined,而不会进行delete运算
delete a?.b
// 等同于
a == null ? undefined : delete a.b
括号的影响
如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响
(a?.b).c
// 等价于
(a == null ? undefined : a.b).c
报错场合
// 构造函数
new a?.()
new a?.b()
// 链判断运算符的右侧有模板字符串
a?.`{b}`
a?.b`{c}`
// 链判断运算符的左侧是 super
super?.()
super?.foo
// 链运算符用于赋值运算符左侧
a?.b = c
右侧不得为十进制数值
foo?.3:0会被解析成foo ? .3 : 0(三元运算符),因此规定?.不能紧跟一个十进制数字
NULL判断符
ES6引入一个新的NULL判断符“??”,它的行为类似“||”,但是只有运算符左侧的值为NULL或undefined时,才会返回右侧的值
NULL判断符的作用
与链判断运算符“?>”配合使用,为null或undefined的值设置默认值
const animationDuration = response.settings?.animationDuration ?? 300;
NULL的问题
“??”与“&&”或“||”不分优先级高低,两个或三个同时使用时,需要使用括号表明优先级,否则会报错
// 报错
lhs && middle ?? rhs
lhs ?? middle && rhs
lhs || middle ?? rhs
lhs ?? middle || rhs
//正确
(lhs && middle) ?? rhs;
lhs && (middle ?? rhs);
(lhs ?? middle) && rhs;
lhs ?? (middle && rhs);
(lhs || middle) ?? rhs;
lhs || (middle ?? rhs);
(lhs ?? middle) || rhs;
lhs ?? (middle || rhs);