ES6-对象

281 阅读6分钟

ES6

扩展对象的功能性

对象类别

分为三类:

普通(Ordinary)对象:具有JS对象所有的默认内部行为
特异(Exotic)对象:具有某些与默认行为不符的内部行为
标准(Standard)对象:ES6规范中定义的对象,既可以是普通对象,也可以是特异对象

对象字面量的语法扩展

字面量的定义

字面量(literal)用于表达源代码中一个固定值的表示法(notatio),整数/浮点数/字符串等等都是字面量.例如:

let a=1;//a是变量,1是字面量
let stooge = { //stooge是一个对象
  "frist-name" = "Julie",// 等号左为属性名,右侧为属性值
  last_name = "beck"//属性名如果是合法的标识符,可省略引号
};

属性初始值简写

/**简写前*/
function createPerson(name,age){
    return {
        name:name,
        age:age
    }
}

/**简写后*/
function createPerson(name,age){
    return {
        name,
        age
    }
}

这种语法可以消除属性名称和局部变量的重复书写.当对象字面量里只有熟悉名称时,JS引擎会从可访问的作用域中查找同名变量,并将变量的值赋给对象字面量内的同名属性,有助于消除命名错误.

对象方法简写

例子:

/**简写前*/
let person = {
    name:"xlx",
    sayName:function(){
        console.log(this.name);
    }
};

/**简写后*/
let person = {
    name:"xlx",
    sayName(){
        console.log(this.name);
    }
};

简写的对象方法拥有ES5中的对象方法所具有的全部特性,但是两者之间还是有一个明显的区别:ES6中的简写方法可以使用 super 关键字.

可计算属性名

ES5使用.记法访问属性时,字面量字符串属性名不可以包含特殊字符,如空格.也不可以使用变量作为属性名,例如:

let person = {"last name":"1"};
let lastName = "last name";
console.log(person.lastName);// undefined
console.log(person.last name);//抛异常
console.log(person.'last name');//抛异常

需要将.替换为[]:

/**当属性名为变量时,使用[]计算属性名,或者直接使用字符串字面量作为属性名*/
let person = {};
let lastName = "last name";
person["first name"] = "xlx";
person[lastName] = "wan";
console.log(person["first name"]);
console.log(person[lastName]);

但是,这样使用是有局限的,在定义对象字面量的时候,不支持使用变量属性名,或者字符串字面量和变量组合,需要计算才能得到属性名的情况.

ES6中,可以在定义对象字面量时直接使用变量属性名,语法也是[],例如:

let lastName = "last name";
let person = {
    "first name":"xlx",
    [lastName]:"wan"
}
console.log(person["first name"]);
console.log(person[lastName]);

或者使用变量和字符串字面量组合,需要计算才能得到属性名的情况:

let suffix = "name";
let person = {
    ["first " + suffix]:"xlx",
    ["last " + suffix]:"wan"
}
console.log(person["first name"]);
console.log(person["last name"]);

新增方法

ES的一个设计目标是:不再创建新的全局函数,也不在Object.prototype上创建新的方法.当开发者想要向标准中添加更多的新方法时,他们会选用一个现有对象,让方法可用.结果,当没有合适的对象时,全局的Object对象收到了越来越多的对象方法.ES6为了缓解这种情况,帮助开发者更好的完成任务,在Object对象引入了一些新的默认方法.

Object.is()

引入Object.is()的目的是弥补===运算符的不准确运算.这个方法接受两个参数,如果这两个参数类型相同且值相同,则返回true.

===运算符的不准确性体现在:

/**在JS中,+0和-0是两个完全不同的实体,但是===会返回true*/
console.log(+0 === -0);//true

对于Object.is()方法来说,大部分情况下都是和===相同,区别只在于+0-0被识别为不相等并且NaNNaN等价.是否选择使用Object.is()取决于那些特殊情况如何影响代码.

Object.assign()

Object.assign()方法可以接受任意数量的源对象,并按照指定的顺序将属性复制到接收对象中,如果多个源对象中存在同名属性,则后面的会覆盖前面的,如:

let receiver = {};
Object.assign(receiver,
    {
        type:"js",
        name:"xlx"
    },
    {
        type:"css"
    }
);
console.log(receiver.name);//xlx
console.log(receiver.type);//css

这种实现对象组合模式在各种第三方库都很常见,比如extend()mix().这些方法使用赋值操作符=来赋值相关属性,不能赋值访问器属性到接收对象中.Object.assign()方法不是ES6的重大补充,但他为这种普遍功能提供了一个正式的方法.

访问器属性:
访问器属性包含一对getset函数,读取访问器属性,调用get,返回有效值.写入访问器属性,调用set,设置新值.

let person = {"sex":"1"};
Object.defineProperty(person,"name",{
  get: function(){
      return this.sex;
  },
  set: function(){
      this.sex = "2"; 
  }
})

重复的对象字面量

let person = {
    name:"xlx",
    name:"wy"
}

ES5的严格模式中,这样的重复会触发语法错误.但是在ES6中去掉了重复属性检查,无论是否是严格模式,都不再检查重复,对于重复的属性,会使用最后的覆盖前值.

自有属性枚举顺序

自有属性枚举顺序的基本规则:

  • 所有数字键按升序排序
  • 所有字符串键按照他们被加入对象的顺序排序
  • 所有symbol键按照他们被加入对象的顺序排序 对于JavaScript来说枚举顺序无关紧要,但是ES6的严格定义使得枚举程序不管在什么情况下都能按照预期的情况执行.

增强对象原型

改变对象原型

相对于在ES5中不能在对象被实例化之后改变其原型的情况来说,ES6添加了Object.setPrototypeOf()方法来增强对象的能力.这个方法可以改变任意指定对象的原型,接收两个参数,被改变原型的对象及被指向的新原型的对象.

let person = {
    getGreeting(){
        return "hello";
    }
};
let dog = {
    getGreeting(){
        return "Woof";
    }
};
let friend = Object.create(person);
console.log(friend.getGreeting());//hello
console.log(Object.getPrototypeOf(frieng) === person);//true

Object.setPrototypeOf(friend,dog);
console.log(friend.getGreeting());//Woof
console.log(Object.getPrototypeOf(frieng) === dog);//true

对象原型的值保存在[[Prototype]]中,上面所提到的getPrototypeOfsetPrototypeOf并不是访问对象原型的唯一方式.

简化原型访问的super

es5中的对象原型访问?

// es5中的对象原型访问
let person = {
    getGreeting(){
        return "hello";
    }
};
let dog = {
    getGreeting(){
        return "woof";
    }
};
// 重写对象实例方法,同时在重写时需要引用对象原型的同名方法
let friend = {
    getGreeting(){
        return Object.getProtoTypeOf(this).getGreeting().call(this) + ",hi!";
    }
};
// 对象原型设置为 person
Object.setProtoTypeOf(friend,person);
console.log(friend.getGreeting());// hello,hi!
console.log(Object.getProtoTypeOf(friend) === person);//true
// 对象原型设置为dog
Object.setProtoTypeOf(friend,dog);
console.log(friend.getGreeting());// woof,hi!
console.log(Object.getProtoTypeOf(friend) === dog);//true

补充说明call:
call()使用一个指定的this和单独给出的一个或者多个参数来调用一个函数.在上面的例子中getGreeting().call(this)就是在调用friend的原型person时设置person内部使用的thisfriend,只是这里在person内部没有使用到this.

一个更加清晰的例子,看明白就知道了call的作用:

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name);//cheese

call()允许为不同的对象分配和调用属于一个对象的函数/方法.
call()提供新的this值给当前调用的函数/方法.你可以使用call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这 个方法).

es6中引入super之后对原型对象访问的简化?

//简化friend对象的getGreeting方法
let friend = {
    getGreeting(){
        return super.getGreeting() + ",hi!";
    }
}

注意: 只有在上述例子中声明方法的方式中使用super是正确的,如果在匿名函数声明中使用是非法的.

//语法错误
let friend = {
    getGreeting:function(){
        return super.getGreeting() + ",hi!";
    }
}