Young87

SmartCat's Blog

So happy to code my life!

游戏开发交流QQ群号60398951

当前位置:首页 >跨站数据

ES6学习笔记:对象

对象分类

ES6 规范明确定义了对象的每种类别:

  • 普通对象:拥有js对象所有默认的内部行为
  • 奇异对象:其内部行为在某些方面有别于默认行为

  • 标准对象:如Array、Date等。标准对象可以是普通的,也可以是奇异的
  • 内置对象:由js运行环境提供的对象。所有的标准对象都是内置对象。

对象字面量

ES6用几种方式扩展了对象字面量:

属性值简写

在以前,对象字面量是键值对的集合,属性值在初始化的时候可能会有重复:

function createPerson(name, age) {
    return {
        name: name,
        age: age
    };
}

这里属性名与属性值(which is 变量名)是相同的,需要重复书写。

在ES6中,当对象的一个属性名与本地变量名相同时,可以简单书写名称而忽略冒号与值:

function createPerson(name, age){
    return {
        name,
        age
    }
}

当对象字面量中的属性只有名称时,js引擎会在周边作用域查找同名变量。若找到则将该变量的值赋给同名属性。

方法简写

普通的为对象添加方法的方式是:

var person = {
    name: "Nicholas",
    sayName: function() {
        console.log(this.name);
    }
};

ES6允许省略冒号和function关键字:

var person = {
    name: "Nicholas",
    sayName() {
        console.log(this.name);
    }
};

这种方法简写和非简写的相比有一个小区别:方法简写内部能使用super。

需计算属性名

当属性名为非合法字符或者变量时,可以使用方括号访问该属性:

lastName = 'last name';
person[lastName] = 'zakas';

console.log(person[lastName]);

在ES6中,允许在对象字面量中也使用需计算属性名,也就是需要进一步计算才能得到的属性名。同样通过方括号表示:

lastName = 'last name';
var person={
    'first name':'nic',
    [lastName]:'zakas'
}
console.log(person[lastName]);

其中也可包含表达式:

var suffix = " name";
var person = {
    ['first'+suffix]: 'nic',
    ['last'+suffix]: 'zakas'
}

console.log(person["first name"]);      
console.log(person["last name"]);       

这些属性名被计算为 “first name” 与 “last name” ,而这两个字符串此后可以用来引用对应属性。

使用方括号表示法,任何能放在对象实例方括号内的东西,都可以作为需计算属性名用在对象字面量中。

新方法

ES6在Object对象上引入了两个静态方法:

Object.is()

严格相等运算符在比较两个值时也有缺陷:

+0 === -0 //true
NaN === NaN //false

因此,ES6引入了Object.is()来弥补这两个怪异点。
此方法接受两个参数,二者类型相同且值也相等时返回true。

Object.is(+0,-0)  //false
Object.is(NaN,NaN)  //true
Object.is(5, "5")  //false

Object.is()与===相比只是修复了以上两个怪异点,剩下的结果都是相同的。

Object.assign()

Object.assign() 方法用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

Object.assign(target, ...sources)
var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
  • Object.assign 会跳过那些值为 null 或 undefined 的源对象
  • Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象身上
  • 如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖。如果多个源对象有相同的属性,那么后面的源对象会覆盖前面的。
var receiver = {};
Object.assign(receiver,
    {
        type: "js",
        name: "file.js"
    },
    {
        type: "css"
    }
);

receiver.type;  //css
  • Object.assign 不支持深拷贝, 其拷贝的只是属性值,若源对象的属性值是指向一个对象的引用,它也只拷贝那个引用值。
  • Object.assign 可以拷贝访问器属性。但它并不是在目标对象上创建访问器属性,而是将变成目标对象的数据属性。
var target = {};
var src = {
    get name(){
        return "file.js"
    }
}

Object.assign(target,src);

var descriptor = Object.getOwnPropertyDescriptor(target , "name");

console.log(descriptor.value);      // "file.js"
console.log(descriptor.get);        // undefined

重复的对象字面量属性

在ES5的严格模式下,若出现重复的属性名则会抛出错误:

"use strict";

var person = {
    name: "Nicholas",
    name: "Greg"        // 在 ES5 严格模式中是语法错误
};

而在ES6中则没有这个限制,无论严格还是非严格模式,都可以使用重复的对象字面量属性,此时后面的属性的值将会成为该属性的实际值

"use strict";

var person = {
    name: "Nicholas",
    name: "Greg"        // 在 ES6 严格模式中不会出错
};

console.log(person.name);       // "Greg"

自有属性的枚举顺序

ES6严格定义了对象自有属性在被枚举时返回的顺序:

  1. 所有的数字类型键,按升序排列
  2. 所有的字符串类型键,按被添加到对象的顺序排序列
  3. 所有的Symbol类型键,按添加顺序排序
var obj = {
    a: 1,
    0: 1,
    c: 1,
    2: 1,
    b: 1,
    1: 1
};

obj.d = 1;

console.log(Object.getOwnPropertyNames(obj).join(""));     // "012acbd"

这种枚举顺序只影响了Object.getOwnPropertyNames()。但for-in、Object.keys的枚举顺序没有规定。

原型

修改对象的原型

一般来说,对象的原型在初始化之后就保持不变了。
ES5提供了Object.getPrototypeOf()方法来获得对象的原型。
ES6添加了Object.setPrototypeOf(),允许你修改对象的原型。

Object.setPrototypeOf(obj,newPrototype)
let person = {
    getGreeting() {
        return "Hello";
    }
};

let dog = {
    getGreeting() {
        return "Woof";
    }
};

// 原型为 person
let friend = Object.create(person);
console.log(friend.getGreeting());                      // "Hello"
console.log(Object.getPrototypeOf(friend) === person);  // true

// 将原型设置为 dog
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting());                      // "Woof"
console.log(Object.getPrototypeOf(friend) === dog);     // true

对象原型的值实际桑被存在内部一个[[Prototype]]属性上,Object.getPrototypeOf() 方法会返回此属性存储的值,而 Object.setPrototypeOf() 方法则能够修改该值。

super

super能够引用原型上的方法。
若想引用原型的方法,可能需要这么做:

let person = {
    getGreeting() {
        return "Hello";
    }
};

let friend = {
    getGreeting() {
        return Object.getPrototypeOf(this).getGreeting.call(this)+"hi";
    }
};

Object.setPrototypeOf(friend,person);
console.log(friend.getGreeting());

super是指向当前原型对象的一个指针,可以像下面这样简化上面的功能:

let friend = {
    getGreeting() {
        return super.getGreeting()+"hi";
    }
};

这里super会自动进行this的绑定,而不用我们显式地绑定。

注意!!super只能应用在简写方法之内!!

你能使用 super 引用来调用对象原型上的任何方法,只要这个引用是位于简写的方法之内。试图在方法简写之外的情况使用 super 会导致语法错误:

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

super可以解决多级继承的问题,且super的引用是非动态的,总是能指向正确的对象。

let person = {
    getGreeting() {
        return "Hello";
    }
};

// 原型为 person
let friend = {
    getGreeting() {
        return super.getGreeting() + ", hi!";
    }
};
Object.setPrototypeOf(friend, person);

// 原型为 friend
let relative = Object.create(friend);

console.log(person.getGreeting());                  // "Hello"
console.log(friend.getGreeting());                  // "Hello, hi!"
console.log(relative.getGreeting());                // "Hello, hi!"

在调用relative.getGreeting()时,实际调用的是其原型friend的getGreeting方法,而此方法中的super.getGreeting() 总是指向 person.getGreeting() ,而不管有多少对象继承了此方法。

总结下super的具体过程:

  • 在对象上调用Object.getPrototypeOf()来获取对原型的引用
  • 在该原型上查找同名函数
  • 创建this绑定并调用该方法。

总结

ES6 为对象字面量做了几个改进:

  • 速记法属性定义 能够更轻易地将作用域内的变量赋值给对象的同名属性;
  • 需计算属性名 允许你将非字面量的值指定为属性的名称;
  • 方法简写 让你在对象字面量中定义方法时能省略冒号和 function 关键字
  • ES6 还舍弃了对象字面量中重复属性名的检查

Object.assign() 方法使得一次性更改单个对象的多个属性变得更加容易
Object.is() 方法对任何值都会执行严格相等比较,它有效成为了 === 的一个更安全的替代品。

在枚举属性时,数字类型的键总是会首先出现,并按升序排列,此后是字符串类型的键,最后是符号类型的键,后两者都分别按添加顺序排列。

感谢 ES6 的 Object.setPrototypeOf() 方法,现在能够在对象已被创建之后更改它的原型了。

最后,你能用 super 关键字来调用对象原型上的方法,所调用的方法会被设置好其内部的 this 绑定,以自动使用该 this 值来进行工作。

除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog

上一篇: Spring Cloud Sleuth进阶实战

下一篇: 如何在IDEA启动多个Spring Boot工程实例

精华推荐