对象类别

ES6规范清晰定义了每一个类别的对象。

  • 普通(Ordinary)对象

    具有JS对象所有的默认内部行为
  • 特异(Exotic)对象

    具有某些与默认行为不符的内部行为
  • 标准(Standard)对象

    ES6规范中定义的对象,例如Array,Date等。标准对象既可以是普通对象,也可以是特异对象。
  • 内建对象

    脚本开始执行时存在于JS执行环境中的对象,所有标准对象都是内建对象。

对象字面量语法扩展

属性初始化的简写

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

当一个对象的属性与本地变量同名时,不必再写冒号和值,简单的只写属性名即可。

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

对象方法的简写语法

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

ES6中语法更简洁,消除了冒号和function关键字。

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

简写方法可以使用 super 关键字。

可计算属性名(Computed Property Name)

在ES5及早期版本中,如果想要通过计算得到属性名,就需要用方括号代替点标记。

var person = {},
lastName = "last name"; person["first name"] = "JiaJia";
person[lastName] = "Liu"; console.log(person["first name"]); // "JiaJia"
console.log(person[lastName]); // "Liu"

此外,在对象字面量中,可以直接使用字符串字面量作为属性名称。

var person = {
"first name": "JiaJia"
}; console.log(person["first name"]); // "JiaJia"

这种模式适用于属性名提前已知或可被字符串字面量表示的情况。

如果属性是通过计算得到的,例如存在变量中,则无法使用在ES5中使用对象字面量定义该属性。

在ES6中,可在对象字面量中使用可计算属性名称。

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

新增方法

Object.is() 方法

ES6引入 Object.is() 方法来弥补全等运算符(===)的不准确运算。

这个方法接受两个参数,如果这两个参数类型相同且具有相同的值,则返回true。

console.log(+0 == -0); // true
console.log(+= === -0); // true
console.log(Object.is(+0, -0)); // false console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true console.log(5 == 5); // true
console.log(5 == "5"); // true
console.log(5 === 5); // true
console.log(5 === "5"); // false
console.log(Object.is(5, 5)); // true
console.log(Object.is(5, "5")); // false

对于 Object.is() 方法来说,其运行结果在大部分情况下与 === 运算符相同,唯一的区别在于 +0 和 -0 被识别为不相等并且 NaN 与 NaN 等价。

Object.assign() 方法

Object.assign() 方法可以接受任意数量的源对象,并按指定的顺序将属性复制到接收对象中。

如果多个源对象具有同名属性,则排位靠后的源对象会覆盖排位靠前的。

var receiver = {};

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

访问器属性

Object.assign() 方法不能将提供者的访问器属性复制到接受对象中。

提供者的访问器属性最终会转变为接受对象中的一个数据属性。

var receiver = {},
supplier = {
get name() {
return "file.js";
}
}; Object.assign(receiver, supplier); var supplierDescriptor = Object.getOwnPropertyDescriptor(supplier, "name");
console.log(supplierDescriptor.value); // undefined
console.log(supplierDescriptor.get); // ƒ name() { return "file.js"; } var receiverDescriptor = Object.getOwnPropertyDescriptor(receiver, "name"); console.log(receiverDescriptor.value); // "file.js"
console.log(receiverDescriptor.get); // undefined

重复的对象字面量属性

ES5严格模式中加入了对象字面量重复属性的校验,当同时存在多个同名属性时会抛出错误。

"use strict";

var person = {
name: "JiaJia",
name: "DLPH"
}

ES6中重复属性检查被移除,无论是严格模式还是非严格模式,代码不再检查重复属性,对于每一组重复属性,都会选取最后一个取值。

"use strict";

var person = {
name: "JiaJia",
name: "DLPH"
} console.log(person.name); // "DLPH"

自有属性枚举顺序

ES5中未定义对象属性的枚举顺序,由JS引擎厂商自行决定。

ES6中严格规定了对象的自有属性被枚举时的返回顺序。

这会影响到 Object.getOwnPropertyName() 方法及 Reflect.ownKeys 返回属性的方式,Object.assign() 方法处理属性的顺序也将随之改变。

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

  1. 所有数字键按升序排序
  2. 所有字符串键按照它们被加入对象的顺序排序
  3. 所有 symbol 键按照它们加入对象的顺序排序
var obj = {
a: 1,
0: 1,
c: 1,
2: 1,
b: 1
}; obj.d = 1;
obj[1] = 1; console.log(Object.getOwnPropertyNames(obj).join("")); // "012acbd"

数值键,尽管在对象字面量中的顺序是随意的,但在枚举时会被重新组合和排序;

字符串键紧随数值键,并按照定义的顺序依次返回;

随后动态加入的字符串键最后输出。

增强对象原型

改变对象的原型

正常情况下,无论是通过构造函数还是 Object.create() 方法创建对象,其原型是在对象被创建时指定的。对象原型在实例化后保持不变,直到ES5都是JS编程最重要的设定之一。

ES6中添加了 Object.setPrototypeOf() 方法,可以改变任意指定对象的原型,它接受两个参数:被改变原型的对象及替代第一个参数原型的对象。

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

从这个例子能感受到作者满满的恶意。

简化原型访问的 Super 引用

如果你想重写对象实例的方法,又需要调用与它同名的原型方法,则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

主要是这句代码

Object.getPrototypeOf(this).getGreeting.call(this)

使用 Object.getPrototypeOf(this) 来确保获取对象的原型;

使用 call(this) 确保原型方法中的 this。

功能是实现了,但是有些复杂。

ES6中引入了 super 关键字简化上述操作。

Super 引用相当于指向对象原型的指针,实际上也就是 Object.getPrototypeOf(this) 的值。

上述 friend 对象的定义可以简化成这样:

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

必须在使用简写方法的对象中使用 Super 引用,在其他方法声明中使用会导致语法错误。

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

Super引用在多重继承的情况下非常有用,因为在这种情况下,使用 Object.getPrototypeOf(this) 方法将会出现问题。

let person = {
getGreeting() {
return "Hello";
}
}; let friend = {
getGreeting() {
return Object.getPrototypeOf(this).getGreeting.call(this) + ", 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()); // 抛出错误
// Uncaught RangeError: Maximum call stack size exceeded

执行到 relative.getGreeting() 时抛出栈溢出的错误。

relative 没有 getGreeting() 方法,所以根据原型链执行的是 friend 中的 getGreeting() 方法。执行到 Object.getPrototypeOf(this) 时,由于当前 this 指向的是 relative,所以这里取到的还是 friend 对象,于是再次调用了 friend 的 getGreeting() 方法,成了一个死循环。最终导致了栈溢出。

Note by JiaJia

看到这里突然想到前面在#3函数中的尾调用优化。如果 friend 的 getGreeting() 方法的return 没有加上后面的一段字符串的化,是符合尾调用优化的条件的。这样一来,这里就真的成了一个死循环,还不会导致栈溢出错误。

正式的方法定义

ES6中正式将方法定义为一个函数,它会有内部的 [[HomeObject]] 属性来容纳这个方法从属的对象。

let person = {
// 是方法
getGreeting() {
return "Hello";
}
} // 不是方法
function shareGreeting() {
return "Hi!";
}

getGreeting() 方法的 [[HomeObject]] 属性值为 person;shareGreeting() 方法没有明确定义 [[HomeObject]] 属性。

Super 的所有引用都是通过 [[HomeObject]] 属性来确定后续的运行过程。

  1. 在 [[HomeObject]] 属性上调用 Object.getPrototypeOf() 方法来检索原型的引用;
  2. 搜寻原型找到同名函数;
  3. 设置 this 绑定并且调用相应的方法。

多重继承时改成使用 super 引用,就可以得到正确的结果。

let person = {
getGreeting() {
return "Hello";
}
}; 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!"

【读书笔记】【深入理解ES6】#4-扩展对象的功能性的更多相关文章

  1. python 进阶读书笔记1 -- 理解python一切皆对象

    理解python一切皆对象: 1.所有的类都是由type创建的 2.所有的类的基类都是object 3.type是类,也是实例,type的基类是object,type对象是由type创建的 4.obj ...

  2. WPF,Silverlight与XAML读书笔记(3) - 标记扩展

    hystar的.Net世界 博客园 首页 新闻 新随笔 联系 管理 订阅 随笔- 103  文章- 0  评论- 107  WPF,Silverlight与XAML读书笔记(3) - 标记扩展   说 ...

  3. 《深入理解ES6》笔记——扩展对象的功能性(4)

    变量功能被加强了.函数功能被加强了,那么作为JavaScript中最普遍的对象,不加强对得起观众吗? 对象类别 在ES6中,对象分为下面几种叫法.(不需要知道概念) 1.普通对象 2.特异对象 3.标 ...

  4. 20150206读书笔记<深入理解计算机系统>

    ●第一章 C是系统级编程的首选.C++显示支持抽象,属于应用级程序设计语言. 简单例子: 一个典型系统的硬件组成: 存储器的层次结构: 注:存储器层次结构的设计思想是,该层存储器作为下一层存储器的高速 ...

  5. Javascript高级程序设计--读书笔记之理解原型对象

    先上一段代码和关系图 function Person(){} Person.prototype.name = "Nic" Person.prototype.age = 22 Per ...

  6. 20150207读书笔记<深入理解计算机系统2-1>

    第二章 信息存储 (1)  多数计算机以一个字节作为最小可寻址的存储器单元. 机器级程序将存储器看成一个非常大的字节数组,称为虚拟存储器. 存储器的每个字节都由唯一的数字标识,称为它的地址. 所有可能 ...

  7. linq读书笔记2-查询内存中的对象

    上次我们说到了linq对数组内容的检索,自.net2.0以后,泛型成了很常见的一种应用技术,linq对泛型的检索也提供了完善的支持 如对list类型的支持,范例如下: class Program    ...

  8. 读书笔记 effective c++ Item 4 确保对象被使用前进行初始化

    C++在对象的初始化上是变化无常的,例如看下面的例子: int x; 在一些上下文中,x保证会被初始化成0,在其他一些情况下却不能够保证.看下面的例子: class Point { int x,y; ...

  9. 【Effective C++ 读书笔记】条款04:确定对象使用前已先被初始化

    永远在使用对象之前先将它初始化.对于无任何成员的内置类型,你必须手工完成此事. 至于内置类型以外的任何其他东西,初始化责任落在构造函数身上.规则很简单:确保每一个构造函数都将对象的每一个成员初始化. ...

  10. 读书笔记jvm探秘之二: 对象创建

    对象是面向对象设计语言无法回避的东西,可见其重要性,JAVA的对象相较于C++来说,不算很复杂,但是我们看到一句话背后往往有很多东西值得探讨(NEW关键字). 对象如何被创建? 首先一句简单的NEW语 ...

随机推荐

  1. 深入理解javascript函数进阶系列第四篇——惰性函数

    前面的话 惰性函数表示函数执行的分支只会在函数第一次调用的时候执行,在第一次调用过程中,该函数会被覆盖为另一个按照合适方式执行的函数,这样任何对原函数的调用就不用再经过执行的分支了.本文将详细介绍惰性 ...

  2. 自学Zabbix3.8-可视化Visualisation

    随着大量数据流入Zabbix,如果用户能够查看正在发生的事情,而不仅仅是数字,那么对于用户来说,这就变得容易得多了.这就是图的所在. 图允许快速地掌握数据流,关联问题,发现什么时候开始,或者做什么事情 ...

  3. MPSOC之9——host、embeded间tftp、nfs、ftp环境搭建

    tftp 可传输单个文件,不能传文件夹 需要通过命令传输文件,略显复杂 ==一般调试kernel时,用uboot通过tftp方式启动,不用每次都烧写存储介质== nfs 在host linux(ubu ...

  4. WebGL学习(3) - 3D模型

      原文地址:WebGL学习(3) - 3D模型   相信很多人是以创建逼真酷炫的三维效果为目标而学习webGL的吧,首先我就是

  5. jenkins学习之多项目构建

    多项目构建,即指的是同时构建多个源代码中的项目,我所知道的有两种方法,一种是在某个项目“构建完成后操作”中设置如下: 另外一种是借助于插件——Multijob plugin,如下: 使用方法其实比较简 ...

  6. 零基础学习webpack打包管理

    这些天在项目之余的时间学习了webpack打包项目的东西,非常荣幸的找到一些大神的文章来学习,死劲嚼了几天,终于略知一二.在以后的工作上还需继续学习,下面我将分享我这几天学到的一点东西,希望能让我一个 ...

  7. Java 管程解决生产者消费者问题

    同样是实验存档.//.. 依然以生产者消费者问题作为背景. 管程(=“资源管理程序”)将资源和对资源的操作封装起来,资源使用者通过接口操作资源就 ok,不用去考虑进程同步的问题. 管程: packag ...

  8. 视觉SLAM中相机详解

    视觉SLAM中,通常是指使用相机来解决定位和建图问题. SLAM中使用的相机往往更加简单,不携带昂贵的镜头,以一定的速率拍摄周围的环境,形成一个连续的视频流. 相机分类: 单目相机:只是用一个摄像头进 ...

  9. bzoj 1150: [CTSC2007]数据备份Backup

    Description 你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味 的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家 ...

  10. JS中事件绑定的三种方式

    以下是搜集的在JS中事件绑定的三种方式.   1. HTML onclick attribute     <button type="button" id="upl ...