==对原型、super关键字的理解还是模模糊糊,super指向当前对象的原型对象,怎么使用呢?为什么在constructor里面要用super呢?看看后面再回头看能不能理解吧。(20180126)==
对象的简写
对象中可以直接写变量,此时属性名为变量名,属性值为变量值
let a = 'z';
let b = {a};
除了属性,方法也可以简写:
let obj = {
test() {
alert(1)
}
};
// 等同于
let obj = {
test: function () {
alert(1)
}
};
简写可以用来便捷的定义函数返回值:
function test(x, y) {
return {x, y};
// 等同于
// return {x: x, y: y}
}
属性名表达式
在使用字面量定义对象时,ES5中,属性名只能是字符串,不能是变量,ES6里可以使用变量:
let a = 'test';
let obj = {
[a]: 1, // test : 1
['a' + 'bc']: 123 // abc: 123
};
Object.is()
与===
基本相同,除了对NaN
的判定以及+0
和-0
的判定
NaN === NaN // false
Object.is(NaN, NaN) // true
+0 === -0 // true
Object.is(0, -0)// false
Object.assign()
Object.assign()
合并的是自身的(不拷贝继承属性)可枚举的(enumerable: true
)属性
let a = {};
let b = {};
Object.defineProperty(a, 'test', {
value: 123,
enumerable: false})
a; // {test: 123}
b = Object.assign(b, a);//{}
性名为Symbol
值的属性,也会被Object.assign
拷贝。
Object.assign()
是一层深拷贝,嵌套拷贝(即源对象的某个属性是对象或数组)则是浅拷贝
对同名属性进行替换, 而不是添加:
const target = { a: { b: 'c', d: 'e' } }
const source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }
Object.assign
可以用来处理数组,但是会把数组视为对象。
Object.assign([1, 2, 3], [4, 5])
// 等同于
// Object.assign({0:1, 1:2, 2:3}, {0:4, 1:5})
// 结果
// [4, 5, 3]
Object.assign()
的用途
1、为对象添加属性:
class Point {
constructor(x, y) {
Object.assign(this, {x, y})
}
}
let a = new Point(1, 2);
// Point {x: 1, y: 2}
上面方法通过Object.assign
方法,将x
属性和y
属性添加到Point类的对象实例。
2、为对象添加方法:
function Test(name) {
this.name = name
}
Object.assign(Test.prototype, {
sayHello: function() {
alert('Hello ' + this.name)
},
method2: function(){},
// More Methods
});
// 等同于
// Test.prototype.sayHello = function () {
// alert('Hello ' + this.name)
// };
let a = new Test('Tom')
3、克隆对象
function clone(origin) {
return Object.assign({}, origin);
}
这种方式克隆的对象,只能克隆自身可枚举属性,不能克隆原型链的值
如果要克隆原型链的值,需要结合使用getPrototypeOf
方法
function clone(origin) {
let originProto = Object.getPrototypeOf(origin);
return Object.assign(Object.create(originProto), origin);
}
4、指定对象属性默认值
let defaultVal = {
name: 'tom',
age: 18
};
let option = {};
let obj = Object.assign({}, defaultVal, option);
如果 option
有对应值,则会将默认值覆盖
但是注意,使用这种方式属性值最好都是简单类型,不能是另外一个对象,否则会将其余没有默认值的属性删除:
const DEFAULTS = {
url: {
host: 'example.com',
port: 7070
},
};
processContent({ url: {port: 8000} })
// {
// url: {port: 8000}
// }
属性的可枚举性
对象的每一个属性都有一个描述对象(Descriptor),用来控制该属性的行为
Object.getOwnPropertyDescriptor()
可以获取指定属性的描述对象
let a = {name:'123'};
Object.getOwnPropertyDescriptor(a, 'name');
// {
// configurable: true
// enumerable: true
// value: "123"
// writable: true
//}
enumerable
就是可枚举性,如果为 false
,某些遍历操作会忽略该属性
for...in
Object.keys()
Object.assign()
JSON.stringify()
Object.getOwnPropertyNames
会遍历自身可枚举和不可枚举的属性
另外,ES6规定,所有Class的原型的方法都是不可枚举的
class Test {
sayHello() {
alert(1)
}
}
console.log(Object.getOwnPropertyDescriptor(Test.prototype, 'sayHello').enumerable); // false
属性的遍历
除了已经了解的for...in
、Object.keys
和Object.getOwnPropertyNames
之外:
还有:
Object.getOwnPropertySymbols
:返回一个数组,包含对象自身所有的Symbol属性的键名Reflect.ownKeys(obj)
:返回一个数组,包含对象自身所有键名,不管键名是Symbol或字符串,也不管是否可枚举
遍历次序:
1. 首先遍历数值键,按数值升序排列
2. 然后遍历字符串键,按照加入时间升序排列
3. 最后遍历Symbol键,按照加入时间升序排列
let obj = {
[Symbol('z')]: 'ff',
'x': 'ccc',
1: 'aaa',
0: 'bbb',
'y': 'ccc',
[Symbol('m')]: 'ff'
};
console.log(Reflect.ownKeys(obj));
// [0,1,'x','y', Symbol('z'), Symbol('m')]
Object.getOwnPropertyDescriptors
Object.getOwnPropertyDescriptor
不加s
,返回对象中某个具体属性的描述对象Object.getOwnPropertyDescriptors
加上s
,返回对象中所有属性的描述对象组成的数组
Object.setPrototypeOf()
和 Object.getPrototypeOf()
__proto__
指向当前对象的 prototype
function test(){}
let a = new test()
a.__proto__ === test.prototype; // true
无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的 Object.setPrototypeOf()
(写操作)、Object.getPrototypeOf()
(读操作)、Object.create()
(生成操作)代替。
Object.setPrototypeOf
方法的作用与__proto__
相同,用来设置一个对象的prototype
对象,返回参数对象本身。
Object.getPrototypeOf()
用于读取一个对象的原型对象。
function test() {}
let a = new test()
Object.getPrototypeOf(a).constructor.name;//"test"
super()
关键字
关键字super
,指向当前对象的原型对象
注意,super
关键字表示原型对象时,只能用在对象的方法之中(并且是对象方法的简写法中),用在其他地方都会报错。
Object.keys()
、Object.values()
、Object.entries()
Object.keys()
返回一个数组,成员是对象自身(不含继承的)可枚举的的属性的键名Object.values()
返回一个数组,成员是对象自身(不含继承的)可枚举的的属性的键值(ES2017引入)Object.entries()
返回一个数组,成员是对象自身(不含继承的)可枚举的的属性的键名和键值,利用解构赋值取值 (ES2017引入)
let obj = {
a: 'test',
b: 'ffff'
};
Object.entries(obj).forEach((value, index) = > {
let[key, val] = value;
console.log(key); // a, b
console.log(val); // 'test', 'ffff'
});
for (let[key, val] of Object.entries(obj)) {
console.log(key); // a, b
console.log(val); // 'test', 'ffff'
}
上述方法都不含Symbol
作为键名的属性
Object.create()
Object.create(proto[, propertiesObject])
proto
指定新创建对象的原型对象propertiesObject
指定新创建对象的自身的可枚举的属性
创建一个对象, 默认创建对象的可枚举行性是false
let obj = Object.create({}, {
p: {
value: 123
}
});
console.log(obj); //{p: 123}
console.log(Object.getOwnPropertyDescriptor(obj, 'p').enumerable); //false
对象的解构赋值
ES2018将...
扩展运算符引入了对象
let {x, ...y} = {x:1, a:3, b:4}
y;//{a: 3, b: 4}
解构赋值必须是最后一个参数,否则会报错。
注意,解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。
let obj = { a: { b: 1 } };
let { ...x } = obj;
obj.a.b = 2;
x.a.b // 2
另外,扩展运算符的解构赋值,不能复制继承自原型对象的属性。
对象的扩展运算符
对象的扩展运算符...
用于取出参数对象的可遍历属性,拷贝到当前对象中
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
等同于使用Object.assign
方法。
let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);
完整拷贝
要实现对象本身属性和继承属性的拷贝,可以采用下面的写法:
// 写法一
const clone1 = {
__proto__: Object.getPrototypeOf(obj),
...obj
};
// 写法二
const clone2 = Obejct.assign(Object.create(Object.getPrototypeOf(obj)), obj)
// 写法三
const clone3 = Obect.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))
上面代码中,写法一的__proto__
属性在非浏览器的环境不一定部署,因此推荐使用写法二和写法三。
let a = {
age: 18
};
let obj = {
name: 'jay'
};
obj.__proto__ = a;
let c = {
__proto__: Object.getPrototypeOf(obj),
...obj
};
let d = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
let e = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))
扩展运算符还可以用于合并对象:
let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);
扩展元素符在对象内出现时,永远是后面的覆盖前面的属性
let a = {
x: 100,
z: 99
};
let b = {...a, x: 1,y: 2,}; // {x:1, y:2, z:99}
let c = {
x: 100,
z: 99
};
let d = {x: 1,y: 2,...a}; //{x:100, y:2, z:99}
- 当扩展运算符在前面而自定义属性在后面时,可以用来修改对象部分属性
- 当扩展运算符在后面而自定义属性在前面时,可以用来设置新对象的默认属性
如果扩展运算符的参数是null
或undefined
,这两个值会被忽略,不会报错。