资讯专栏INFORMATION COLUMN

ECMAScript6(8):对象的扩展

pingan8787 / 459人阅读

基本扩展

允许使用已有对象赋值定义对象字面量,并且只写变量名即可

var name = "Bob";
var getName = function(){console.log(this.name);};

var person = {name, getName};
//相当于
//var person = {
//name: "Bob",
//getName: function(){console.log(this.name);}
//}
person.getName();   //"Bob"

可以像定义存取器那样定义方法

var o = {
  _age: 10,
  _score: 60,
  age(num){
    if(num > 0) {
      this._age = num;
      return this;
    }
    return this._age;
  },
  get score(){
    return this._score;
  }
};

console.log(o.age());    //10
o.age(15);
console.log(o.age());    //15
console.log(o.score);    //60
o.score = 100;           //TypeError

注意,以下代码是等同的:

var obj = {
  class () {}       //并不会因为 class 是关键字而解析错误
};
//等价于
var obj = {
  "class": function() {}
};

如果一个方法是 Generator 函数,需要在前面加 *:

var obj = {
  time: 1,
  *gen(){
    yield "hello " + time;
    time++;
  }
}

属性名表达式

js 本来可以这样 obj["k"+"ey"] 访问一个对象属性,现在也可以这样定义属性了:

var key1 = "name";
var key2 = "age";

var o = {
  [key1]: "Bob",
  [key2]: 18,
  ["first" + key1]: "Ellen"
};
o.name;    //"Bob"
o.age;     //18
o.firstname;   //"Ellen"

注意:该方法不能和上一小节使用已有标识符定义对象字面量的方法混合使用,否则会报错;

//错误用法
var foo = "bar";
var bar = "abc";
var baz = {[foo]};  //报错

方法的 name 属性

函数有 name 属性,方法也就有 name 属性。一般方法 name 返回函数名(不包括对象名),对于存取器方法,没有 name 属性:

var o = {
  _age: 10,
  _score: 60,
  _name: "Bob",
  _firstname: "Ellen",
  set age(num){
    if(num > 0) {
      this._age = num;
      return this;
    }
  },
  get age(){
    return this._age;
  },
  get score(){
    return this._score;
  },
  name(n){
    if(!n) return this._name + " " + this._firstname;
    this._name = n;
    return this;
  },
  set firstname(n){
    if(n) this._firstname = n;
    return this;
  }
};
console.log(o.name.name);      //"name"
console.log(o.age.name);       //undefined
console.log(o.score.name);     //undefined
console.log(o.firstname);      //undefined,所以 set 函数更不会有 name 属性

如果对象的方法是个 symbol,name 属性为空字符串 ""

var sym1 = new Symbol("description of sym1");
var sym2 = new Symbol();
var o = {
  [sym1](){},
  [sym2](){},
};
o[sym1].name;    //""
o[sym2].name;    //""

静态方法

Object.is(a,b): 比较a,b两个值是否严格相等,相当于 ===, 但有一点不一样:

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

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

Object.assign(target, source1,source2,...): 将每个 source 对象自身的可枚举属性复制到 target 对象上,不包括原型链上的属性和不可枚举属性。只有有一个参数不是对象,就会抛出 TypeError 错误。遇到同名属性,排在后面的会覆盖前面的:

var target = {a:1,b:2};
var source1 = {a:3,c:3};
var source2 = {a:2,d:0};
Object.assign(target, source1, source2);
console.log(target);      //{a: 2, b: 2, c: 3, d: 0}

对于属性名是 symbol 的可枚举属性也会被复制:

Object.assign({a:"b"}, {[Symbol("c")]:"d"});    //{a: "b", Symbol(c): "d"}

对于同名属性存在嵌套对象,外层会被直接替换:

Object.assign({a:{b:"c",d:"e"}}, {a:{b:"hello"}});     //{a:{b:"hello"}}

可以用 Object.assign处理数组,但会视其为对象:

Object.assign([1,2,3], [4,5]);     //[4, 5, 3]

技巧:为对象添加属性方法

Object.assign(String.prototype, {
  newProperty: "value",
  newFunction: function(){}
})

技巧:克隆对象

Object.assign({},origin);

技巧:为对象添加属性方法

Object.assign(target, ...source);

技巧:为对象添加属性方法

const DEFAULT_OPTION = {   //默认值
  a: 1,
  b: 2
};
function processContent(newOption){
  return Object.assign({}, DEFAULT_OPTION, newOption);
}
//设置属性应该是基本类型,否则会因为深拷贝出问题
对象属性的可枚举性与遍历

以下6个操作会忽略不可枚举的属性

for...in循环

Object.keys()

JSON.stringify()

Object.assign()

Reflect.enumerate()

扩展运算符 ...

以下4个方法不忽略不可枚举属性

Object.getOwnPropertyNames()

Object.getOwnPropertySymbols()

Reflect.ownKeys()

以上9个方法中,只有2个会操作包含继承到的属性

for...in循环

Reflect.enumerate()

以上9个方法中,只有1个方法可以获得 Symbol 属性

Object.getOwnPropertySymbols()

除此之外需要强调的是 ES6 中,所有 class 的原型方法都是不可枚举的:

Object.getOwnPropertyDescriptor(class{foo(){}}.prototype, foo).enumerable;  //false

ES6 起,有了7中遍历属性的方法:

for...in: 循环遍历对象自身和继承到的可枚举属性,不包括 Symbol 属性

Object.keys(obj): 返回包含自身可枚举属性的属性名数组,不包含 Symbol 属性

Object.getOwnPropertyNames(obj): 同上,但包括不可枚举属性

Object.getOwnPropertySymbols(obj): 返回自身所有 Symbol 属性名的数组,包括不可枚举属性

Reflect.ownKey(obj): 返回自身所有属性名数组,包括不可枚举属性和 Symbol 属性名

Reflect.enumerate(): 返回一个 Iterator, 用来遍历对象自身及继承到的可枚举属性,不包括 Symbol 属性;和 for...in 一样

for...of: 只能遍历具有 Iterator 接口的对象,具体作用范围由 iterator 决定,遍历没有 iterator 的对象会报错

以上方法除了 for...of 以外,遍历顺序为:

首先遍历所有属性名为数字的属性,按数字大小排序;

其次遍历所有属性名为字符串的属性,按属性生成时间排序;

最后遍历所有属性名为 Symbol 的属性,按属性生成时间排序;

对象的__proto__属性

这是个很老很老的属性,在大家想期待下,ES6终于把它写进去了,嗯?...是写进附录了。这个属性用来读写当前的对象的原型对象obj.constructor.prototype

从本质上来讲,__proto__ 是定义在Object.prototype 上的一个存取器函数:

function isObject(a){
  return Object(a) === a;
}
Object.defineProperty(Object.prototype, "__proto__", {
  get(){
    let _thisObj = Object(this);
    return Object.getPrototypeOf(_thisObj);
  },
  set(proto){
    if(this == null) throw new TypeError();
    if(!isObject(this) || !isObject(proto)) return undefined;
    let status = Object.setPrototypeOf(this, proto);
    if(! status) throw new TypeError();
  }
});

但是,还是不建议使用这个东西,毕竟看它这名字就是个内部属性,因为它有了不加,但不保证所以终端都能用,所以ES6推荐用下面这两个属性:

Object.setPrototypeOf(obj, newProto);   //写
Object.getPrototypeOf(obj);   //读

简单举一个例子:

function Rectangle(){}
var rec = new Rectangle();
Object.getPrototypeOf(rec) === Rectangle.prototype;    //true
Object.setPrototypeOf(rec, Object.prototype);
Object.getPrototypeOf(rec) === Rectangle.prototype;    //false

当然如果你把一个对象的原型设置成了 null, 也是可以的,只是,它不具备任何你听说过的方法了:

var o = Object.setPrototypeOf({}, null);
//等价于 var o = {"__proto__": null};
Object.getPrototypeOf(o);                //null
o.toString();                    //TypeError: o.toString is not a function
对象的扩展运算符

这是 ES7 的一个提案, babel可以使用器部分功能使用。

rest参数

用法和数组中很类似,这里不再过多赘述,直接看几个例子吧:

var {x,y,...z}  = {x: 1, y: 2, a: 3, d: 4}; //x=1, y=2, z={a: 3, d: 4}

值得强调的是, 对象的rest参数形式执行的是浅拷贝,赋值得到的是原对象的引用:

let obj = {a: {b: 1}};
let {...x} = obj;
obj.a.b = 2;
console.log(x.a.b);    //2

此外 rest 不会复制不可枚举属性和继承自原型的属性:

var p = {a: 0};
var o = {b: 2};
var o = Object.defineProperty(o, "foo", {
  value: 2,
  configurable: true,
  enumerable: false,
  writable: true
});
Object.setPrototypeOf(o, p);
var u = { ...p };
console.log(u);    //{b:2}

扩展运算符

复制参数对象所有可遍历属性到当前对象中:

var o1 = {a: 1, b: 2};
var n = { ...o1 };    //n={a: 1, b: 2};
//相当于
var n = Object.assign({}, o1);

可以用扩展运算符合并多个对象, 排后的属性会覆盖之前的属性:

var source0 = {a:1,b:2};
var source1 = {a:3,c:3};
var source2 = {a:2,d:0};
Object.assign(target, source1, source2);
var target = {...source0, ...source1, ...source2};
console.log(target);      //{a: 2, b: 2, c: 3, d: 0}

注意一点:如果扩展运算符的参数对象有 get 方法,该方法会被执行:

var a = {o:1,p:2,m:4};
var withoutError = {
  ...a,
  get x(){
    throw new Error();
  }
};           //不报错
var withError = {
  ...a,
  ...{get x(){
    throw new Error();
  }}
};           //报错

如果扩展对象是 null 或 undefined,会被忽略,不报错

var o = { ...null,  ...undefined};    //不报错
console.log(o);    //{}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/97449.html

相关文章

  • ECMAScript6 新特性——“数值扩展

    摘要:二进制和八进制表示法提供了二进制和八进制数值的新的写法,分别用前缀或和或表示。用来检查是否为有穷以及是否为这两个新方法只对数值有效,非数值一律返回。引入了和这两个常量,用来表示这个范围的上下限。因为有精度限制,超过的次方的值无法精确表示。 1 二进制和八进制表示法 ES6提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。 console.log(0b10...

    Dean 评论0 收藏0
  • ECMAScript6(3):数值类型扩展

    摘要:数值类型扩展类型新增了如下特性支持二进制和八进制二进制用或开头八进制用或开头新加方法判断一个数字是否有限方法判断一个变量是否。值得注意的是如果将非数值传入这两个函数一律返回。对于无法转换为数值的参数返回符合函数。 数值类型扩展 Number 类型新增了如下特性: 支持二进制和八进制 二进制用 0b 或 0B 开头, 八进制用 0o 或 0O 开头: Number(0b1101); ...

    Martin91 评论0 收藏0
  • ECMAScript6 新特性——“对象扩展

    摘要:属性的简洁表示法允许直接写入变量和函数作为对象的属性和方法。,中有返回一个数组,成员是参数对象自身的不含继承的所有可遍历属性的键名。对象的扩展运算符目前,有一个提案,将解构赋值扩展运算符引入对象。 1 属性的简洁表示法 ES6允许直接写入变量和函数作为对象的属性和方法。 写入属性 var name = value; var obj = { name }; console.log...

    Clect 评论0 收藏0
  • ECMAScript6 新特性——“数组扩展

    摘要:原来的也被修改了数组实例的喝方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为的成员,然后返回该成员。数组实例的方法使用给定值,填充一个数组。 1 Array.from() Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括...

    Eminjannn 评论0 收藏0
  • ECMAScript6 新特性——“函数扩展

    摘要:返回空字符串,返回将一个具名函数赋值给一个变量,则和的属性都返回这个具名函数原本的名字。不可以使用对象,该对象在函数体内不存在。等到运行结束,将结果返回到,的调用帧才会消失。 1 函数参数的默认值 ES6允许为函数的参数设置默认值,即直接写在参数定义的后面: function log(x = message.,y = duration infomation.) { consol...

    Jiavan 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<