新增的对象字面量语法
成员速写
对象的成员如果它的键名称与值变量的名称相同,则可以简写:
var name = "张三";
var obj = {
name
};
方法速写
当对象的成员是一个方法时,可以省略冒号和function关键字
var obj = {
sayHello(){
console.log("hello");
}
}
注意:这种简写的函数并不是箭头函数
计算属性名
对象成员的属性名如果来自一个表达式,则需要使用计算属性名
计算属性名需要使用[]进行包括
var obj = {
[1+2*3]: "abc" // 7: "abc"
};
Object的新增API
-
Object.is()
用于判断两个数据是否相等
它和"==="的比较基本相同,除了以下两点:
- Object.is(NaN, NaN)得到true
- Object.is(+0, -0)得到false
-
Object.assign(obj1, obj2)
将obj2自身的所有可枚举的属性混合到obj1之中,然后返回obj1
const obj1 = {} const obj2 = { a: 1 }; Object.defineProperty(obj2, "b", { value: 2, enumerable: true }); Object.defineProperty(obj2, "c", { value: 3, enumerable: false }); Object.prototype.d = 4; const obj3 = Object.assign(obj1, obj2); console.log(obj3); // { a: 1, b: 2 } console.log(obj1 === obj3); // true -
Object.freeze(obj)
浅层冻结对象或数组
被冻结的对象或数组的直接子属性是不能被重新赋值的,也无法增加或删除直接子属性
const obj = { a: 1, b: { c: 1 } }; Object.freeze(obj); obj.a++; // 无效 obj.d = 1; // 无效 delete obj.a; // 无效 obj.b.c++; // 成功 delete obj.a.c; // 成功 -
Object.isFrozen(obj)
判断对象或数组是否被冻结
类语法
传统的构造函数存在的问题:
- 属性和原型方法定义分离,降低了代码可读性
- 原型成员可以被枚举
- 构造函数仍可以作为普通函数使用
构造函数写法:
function User(firstName, lastName){
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${firstName} ${lastName}`;
}
User.isUser = function(u){
return !!u && !!u.fullName
}
User.prototype.sayHello = function(){
console.log(`Hello, my name is ${this.fullName}`);
}
类语法:
// 类写法
class User{
constructor(firstName, lastName){
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${firstName} ${lastName}`;
}
static isUser(u){
return !!u && !!u.fullName
}
sayHello(){
console.log(`Hello, my name is ${this.fullName}`);
}
}
这种使用class关键字声明的构造函数,也称为类
类和传统构造函数的差异:
- 类的原型方法将自动被设置为不可枚举
- 类的构造器只能通过new关键字进行调用
细节
-
类的声明不会被提升,且不会成为全局对象的属性
和let和const声明的变量一样,类存在暂时性死区
-
类中的所有代码均是在严格模式下执行的
-
类的原型上的方法都是不可枚举的
-
类的原型上的方法都不能被作为构造函数使用
-
类的构造器必须使用new关键字进行调用
-
类可以不手动设置构造器,若不手动设置,则JS会帮你设置,只是构造器中什么代码都没有
可计算的成员名
class Animal{
constructor(){
// ...
}
["a" + "b"](){
// ...
}
}
const animal = new Animal()
animal.ab();
getter和setter
class User{
constructor(name, age){
this.name = name;
this._age = age;
}
// 当获取name属性时,会调用该函数,该函数没有参数
get age(){
return this._age;
}
// 当设置name属性时,会调用该函数,该函数只有一个参数
set age(newAge){
if(typeof newAge !== "number"){
throw new Error("property age must be a number");
}
if(newAge < 0){
newAge = 0;
}else if(newAge > 100){
newAge = 100;
}
this._age = newAge;
}
}
var user = new User("张三", 18);
/**
User {
name: "张三",
_age: 18,
age: 18
}
*/
user.age = 2000;
console.log(user.age);
这种写法等同于使用Object.defineProperty()给user定义了一个age属性,并且该属性的属性描述符中加入getter和setter
静态成员
类中使用static关键字声明的成员即为静态成员,或称类成员
class Chess{
constructor(){
// ...
}
static width = 50;
static height = 50;
static method(){ }
}
Chess.width; // 50
Chess.method();
字段初始化器
ES7中支持了字段初始化器,即可以在构造器外给实例本身添加成员
class User{
name = "张三";
age = 18;
}
其实上面的代码最终还是会转化为:
class User{
constructor(){
this.name = "张三";
this.age = 18;
}
}
类表达式
在JS中,类本质上就是一个函数,既然有函数表达式,就会有类表达式,类表达式也可以出现在任何函数表达式可以出现的地方
var A = class {
constructor(){}
//...
}
var a = new A();
扩展
class Test {
construct() { }
method1 = () => { // 这其实就是一个字段初始
return this;
}
static method2 = () => {
return this;
}
}
var test = new Test();
console.log(test.method1() === test); // true
console.log(Test.method2() === Test); // true
类的继承
class Animal{
// ...
}
class Cat extends Animal{
// ...
}
使用extends可以让一个类继承另一个类,被继承的类叫做父类,主动继承的类叫做子类
继承发生后,子类原型的隐式原型将指向父类的原型
extends后,
Cat.prototype.__proto__为Animal.prototype
注意:继承是一对多的关系,一个子类最多只能继承自一个父类
super关键字
使用super关键字,可以在子类的构造器中调用父类的构造器
class Animal {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
class Cat extends Animal {
constructor(name, age, type) {
super(name, age);
this.type = type;
}
}
var cat = new Cat("咪咪", 2, "花猫");
console.log(cat);
注意:
-
super作为函数使用时,只能出现在构造器中 -
子类调用
super时,父类构造器中的this会自动指向子类的实例class Animal { constructor(name, age) { this.name = name; this.age = age; } } class Cat extends Animal { constructor(name, age, type) { super(name, age); this.type = type; } }这相当于:
function Animal(name, age) { this.name = name; this.age = age; } function Cat(name, age, typee) { Animal.call(this, name, age); this.type = type; } Object.setPrototypeOf(Cat.prototype, Animal.prototype); -
如果子类中设置了构造器,则必须要在子类构造器的最开始手动调用
super如果子类没有设置构造器,则JS会自动帮你加入构造器,并且该构造器的参数和父类构造器一致,并且还会在该构造器的开始位置调用super并传入所有参数
class Animal { constructor(name, age) { this.name = name; this.age = age; } } class Cat extends Animal { }这等效于:
class Animal { constructor(name, age) { this.name = name; this.age = age; } } class Cat extends Animal{ constructor(name, age){ super(name, age); } }
super关键字也可作为父类的原型进行使用
class Animal {
constructor(name, age) {
this.name = name;
this.age = age;
}
print(){
console.log(this.name);
console.log(this.age);
}
}
class Cat extends Animal{
constructor(name, age, type){
super(name, age);
this.type = type;
}
print(){
super.print();
console.log(this.type);
}
}
var cat = new Cat("咪咪", 2, "花猫");
cat.print();
注意:
- super关键字作为原型对象使用时,可以出现在原型方法以及构造器中
- 使用super关键字调用父类原型的方法,其this指向仍为子类的实例