ES6之对象的扩展

属性和方法的简洁写法

属性的简洁写法

  • 当属性名和变量名一致的时候,可以只写一个
foo' = 123;
//ES5写法
const bar = {
	foo: foo'
}
//ES6写法
const bar = {
	foo
}
  • 例如当我们使用vue的时候,不是经常见主文件这么写吗?这里用的就是ES6的对象属性简洁写法
new Vue({
	app,
	router,
	vuex,
})
  • 又例如,使用CommonJs导出变量的时候
module.exports = { getItem, setItem, clear };
// 等同于
module.exports = {
  getItem: getItem,
  setItem: setItem,
  clear: clear
};

方法的简洁写法

//ES5
const bar = {
	methods: function (){..}
}
//ES6
const bar = {
	methods () {...}
}
  • 注意方法是generator函数的时候,前面要加上*号
  • 注意对象里面的方法不建议使用箭头函数,第一是ES6规范不是这么规定的,第二个是因为箭头函数要绑定this,所以我们用不同的对象调用这个函数(虽然对于对象里面的方法,这种情况比较少),可能会导致this的改变,如果用了箭头函数就会出错,我想这也是ES6对象里面的方法不提倡用箭头函数的原因。

第一个例子
如下所示,不管是正常的对象内函数写法还是箭头函数写法都会输出jack,但是他们的原理是不相同的。

var name = 'tom';
var obj = {
  name : "jack",
  say () {
    console.log(this.name);
  }
  sayit: () => {
	console.log(this.name)
	}
}
var fun = obj.say;
var fun1 =obj.sayit;
fun(); // tom
fun1(); // tom
  • 正常写法输出tom是因为fun定义在全局,所以调用fun的时候,会输出全局的name变量。
  • 第二个箭头函数写法,this绑定在window,下面这句话引自《深入理解ES6》。

箭头函数没有 this/super/arguments/new.target 的绑定,这些值是由外围最近一层非箭头函数决定。

有同学可能会疑惑既然箭头函数this绑定最近的环境,为什么是window而不是obj,这里可以参考https://segmentfault.com/q/1010000011100440此问题下的回答,里面的解释是这样的。

箭头函数的this和普通函数的this可以看成完全两个概念的东西,不用传统的this去理解。我对外围的理解是,这个外围指的是()=>{}整体的外围,比如你的代码中:name属性的外围是什么?print1的外围是什么?。所以 ()=>{console.log(this.name);}的外围已经出了obj,从而进入window

所以箭头函数也会输出tom,而不是jack

第二个例子
第二个例子能够让我们更加理解箭头函数this和普通函数this指向.

正常函数是的this是执行的时候决定的,所以我们obj调用say方法的时候,this指向obj,this.name就是jack。但是箭头函数绑定了window的this,而window没有name这个属性,所以报错了。

var obj = {
  name : "jack",
  say () {
    console.log(this.name);
  }
  sayit: () => {
	console.log(this.name)
	}
}
obj.say();//jack
obj.sayit();//报错

表达式和变量表达对象属性和对象方法

let propKey = 'foo';
let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123,
  [propKey](){....},
  ['a' + 'bc'] (){....}
};
//等价于
let obj = {
  foo: true,
  abc 123,
  foo(){.....},
  abc(){....}
};
  • 注意,属性名表达式与简洁表示法,不能同时使用,会报错
// 报错
const foo = 'bar';
const bar = 'abc';
const baz = { [foo] };

// 正确
const foo = 'bar';
const baz = { [foo]: 'abc'};
  • 属性名/方法名甚至可以是对象,不过默认情况下会自动将对象转为字符串[object Object],这一点要特别小心。
const keyA = {a: 1};
const keyB = {b: 2};

const myObject = {
  [keyA]: 'valueA',
  [keyB]: 'valueB'
};

myObject // Object {[object Object]: "valueB"}

上面代码中,[keyA]和[keyB]得到的都是[object Object],所以[keyB]会把[keyA]覆盖掉,而myObject最后只有一个[object Object]属性。

方法的name属性

规则都一样,见ES6之函数的扩展

const person = {
  sayName() {
    console.log('hello!');
  },
};

person.sayName.name   // "sayName"

属性的可枚举性和遍历

对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。

let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
//  {
//    value: 123,
//    writable: true,
//    enumerable: true,
//    configurable: true
//  }

描述对象的enumerable属性,称为”可枚举性“,如果该属性为false,就表示某些操作会忽略当前属性。

  • for…in循环:只遍历对象自身的和继承的可枚举的属性。
  • Object.keys():返回对象自身的所有可枚举的属性的键名。
  • JSON.stringify():只串行化对象自身的可枚举的属性。
  • Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。

实际上,引入“可枚举”(enumerable)这个概念的最初目的,就是让某些属性可以规避掉for…in操作

属性的遍历

  • for…in
    for…in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

  • Object.keys(obj)
    Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

  • Object.getOwnPropertyNames(obj)
    Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

  • Object.getOwnPropertySymbols(obj)
    Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

  • Reflect.ownKeys(obj)
    Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。
首先遍历所有数值键,按照数值升序排列。
其次遍历所有字符串键,按照加入时间升序排列。
最后遍历所有 Symbol 键,按照加入时间升序排列

Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]

super关键字

ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象。
注意,super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。

const obj = {
  foo: super.foo,
  foo1:function(){..},
  foo2:()=>{...}
}

以上三种方法都会报错,因为即便super是在方法里面使用的,但是这个方法是定义在一个属性上的,所以回报错。

  • super.foo等同于Object.getPrototypeOf(this).foo(属性)
  • Object.getPrototypeOf(this).foo.call(this)(方法)。

第一个例子,返回的是super.x是一个属性,所以返回的是原型的属性x的值hello。第二个例子,使用的是super上的方法super.foo(),原型上这个方法又调用this.x这个属性值,此时这个this绑定的是现在所处上下文的this,所以打印出的是world。

const proto = {
  x: 'hello'
};

const obj = {
  x: 'world',
  foo() {
    return super.x;
  }
};

Object.setPrototypeOf(obj, proto);
obj.foo() // "hello"
const proto = {
  x: 'hello',
  foo() {
    console.log(this.x);
  },
};

const obj = {
  x: 'world',
  foo() {
    super.foo();
  }
}

Object.setPrototypeOf(obj, proto);

obj.foo() // "world"

对象的扩展运算符

ES2018里面把扩展运算符也运用到了对象里面,用法和数组的扩展运算符类似

  • 拷贝对象(浅拷贝)
    并且拷贝的也只是自身的属性,不能拷贝原型上的属性。
//第一种
let obj = { a: 1,b: 2} ;
let x  = {...obj};
x//{a:1, b:2}
//第二种
let obj = { a: 1,b: 2} ;
let {a, ...obj1}  = obj;
obj1//{b:2}

另一个例子

const o = Object.create({ x: 1, y: 2 });
o.z = 3;

let { x, ...newObj } = o;
let { y, z } = newObj;
x // 1
y // undefined
z // 3

上面代码中,变量x是单纯的解构赋值,所以可以读取对象o继承的属性;变量y和z是扩展运算符的解构赋值,只能读取对象o自身的属性,所以变量z可以赋值成功,变量y取不到值

  • ES6 规定,变量声明语句之中,如果使用解构赋值,扩展运算符后面必须是一个变量名
let { x, ...{ y, z } } = o; //错

Object.is()

用于比较两个值是否相等,最出名的无非是比较两个NAN是否相等

ES5
console.log(NAN == NAN) // false
console.log(Number.isNAN(NAN)) // true
ES6
console.log(Object.is(NAN,NAN))//true
console.log(+0 == -0)//true
console.log(object.is(+0,-0))//false

Object.asign()

用于合并对象,赋值对象

比如可以用于合并默认参数

function ajax(options){
	let default = {
		........
	};
	let obj = object.asign({}, default, options)
}

复制对象

obj1 = {....};
obj2 = object.asign({},obj1};

object.keys()/values()/entries()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值