Es6对象新方法

2018-12-20  本文已影响0人  牛大嘴
Object.freeze()
该方法可以冻结一个对象,冻结对象指的是不能向这个对象,添加属性、删除属性、修改属性、以及不能修改该对象已有属性的可枚举型、可写性、可配置性,该方法返回被冻结的对象。
演示:
var json = {
  name:'秦司令'
};
var obj = Object.freeze(json);
obj.age = 20;        // 不生效
obj.name = 'abc';   //不生效
delete obj.name    // 不生效
console.log(obj.name);  //秦司令 
console.log(obj.age)  // undefined

上面代码给大家展示了一下操作 就是防止对象的属性被修改 , 对象属性的可枚举性、可写性、可配置性、下面我们会说到。


Object.is()
Es5比较两个值是否相等,只有两个运算符 ,一个等于运算符( == ) , 一个严格等于运算符( === ) 。它们都有缺点,前者会自动转换类型 , 后者的NaN不等于自身,以及+0 等于 -0。javascript缺乏一种运算,在所有环境中,你们的值是一样的就应该相等。
Es6提出同值相等算法,用来解决这个问题。
Object.is就是部署这个算法的新方法。它用来比较俩个值是否严格相等,与严格运算符(===) 的行为基本一致。
演示:
Object.is('foo','foo') // true
Object.is({},{}) // false

上面代码中两个空对象不相等,因为它们的引用地址不同。
不同之处只有两个: 一是 +0 不等于 -0 , 二是 NaN 等于自身

// Es5写法
+0  === -0 // true
NaN === NaN  //false

//Es6提出的新算法方法
Object.is(+0,-0) //false
Object.is(NaN,NaN) // true

上面代码Object.is Es6新提出的算法 让 NaN 等于自身


Object.assign()
该方法用于对象合并,将源对象(source)的所有可枚举属性,复制到target目标对象里。
演示:
var json = {a:1};
var source = {b:2};
Object.assign(json,source)  // {a:1,b:2}

Object.assign方法的第一个参数是目标对象,其它的都是源对象,该方法返回值返回第一个参数。

注意,如果目标对象和源对象的属性有重复的话,那么源对象的属性覆盖目标对象的属性。

var json = {a:1,b:1}
var source = {b:2,c:3}
Object.assign(json,source) // {a:1,b:2,c:3}

如果只有一个参数,Object.assign会直接返回该参数

Object.assign({name:'秦司令'}) // {name:'秦司令'}

如果该参数不是对象,而先会转换成对象。

Object.assign(2) // Number({}) 

由于undefined和null无法转换成对象,所以如果它们作为参数,就会报错。

Object.assign(undefined) //报错
Object.assign(null)  //报错

当然,如果undefined和null不在首参数就不会报错,因为它们不在首参数,处理方法就不同,如果无法转换成对象就会跳过。

Object.assign({},123,{name:'秦司令'}) // {name:'秦司令'}

其它类型的值 数字,布尔,字符串,不在首参数内也不会报错 。但是,除了字符串会以数组的形式,拷贝到目标对象里,其它值都不行,只有字符串可以。

Object.assign({},'str') // {0:'s',1:'t',2:'r'} 
Object.assign({},123) // {}
Object.assign({},true) // {}

上面代码中,数值,布尔值,字符串 结果只有字符串被拷贝进去,这是因为只有字符串有包装类对象,会产生可枚举属性。

请看下个列子

Object(true)  //{ [[PrimitiveValue]]:true }
Object(123)  // { [[PrimitiveValue]]:123 }
Object('str') // {0:'s',1:'t',2:'r',length:3, [[PrimitiveValue]]:'str' }

上面代码看对象的返回值 [[PrimitiveValue]] 这个是原始属性 assign 是无法拿到的,只有字符串有可枚举属性,所以上面只能拷贝字符串。

Object.assign拷贝属性也是有限制的,只拷贝源对象的自身属性(不包含继承属性),也不拷贝不可枚举属性(enumerable:false)。

Object.assign({name:'秦司令'},
  Object.defineProperty({},'age',{
    enumerable:false,
    value:20  
  })
)

上面代码中,Object.assign要拷贝这个对象属性,但是没有被拷贝进去,因为这个属性里面设置了不可枚举属性,enumerable:false。

属性名为Symbol值的也会被拷贝进去

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

注意点

浅拷贝
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性发生改变,那么Object.assign的目标对象也跟着改变。
var json = {a:{b:1}}
var obj = Object.assign({},json)
json.a.b = 2
obj.a.b // 2

上面代码发生引用的原因是,json.a的属性值是一个对象,Object.assign拷贝到的是这个对象的引用,这个对象的任何变化,都会映射到目标对象上

数组的处理
Object.assign用来处理数组,会把数组视为对象处理。
Object.assign([1,2,3],[4,5,6]) // [4,5,6]

上面代码中,Object.assign把数组视为属性名为 0 1 2的对象,因此源数组的0号属性 4 覆盖了 目标属性的 1 值 , 以此类推 最后全部覆盖。

取值函数的处理
Object.assign只能进行值的复制,如果是要复制一个取值函数,那么将求值后再复制
const source = {
  get foo(){
    return 1
  }
}
const target = {};
Object.assign(target,source)  // {foo:1}

上面代码中,source对象的foo属性是一个取值函数,Object.assign不会复制这个取值函数,只会拿到值以后,将这个值复制过去


常见用途

Object.assign方法有很多好处。
一、为对象添加属性
class Point{
  constructor(x,y){
    Object.assign(this,{x,y})
  }
}
new Point(1,2)

上面方法通过Object.assign方法,将x属性和y属性添加到Point类的对象实例。

二、给对象添加方法
function Fn(){}
Object.assign(Fn.Prototype,{
  add(){
  
  }
})
new Fn()

上面代码使用Object.assign方法给对象添加方法,这样写法简洁表达式

三、克隆对象
function clone(obj){
  return Object.assign({},obj)
}

上面代码将原始对象拷贝到一个空对象,就得到了原始对象的克隆。

不过,采用这种方法克隆,只能克隆原始对象自身的值,不能克隆它继承的值,如果想要克隆它的原型,请看下列代码。

function clone(){
  var proto = Object.getPrototypeOf(obj)
  return Object.assign(Object.create(proto),obj)
}
// Object.create方法是操作__proto__ 
四、合并多个对象

将多个对象合并到某个对象

const mer = (target,...source) => Object.assign(target,...source)
//上面的写法 相当于下面
function Demo(target,...source){
  return Object.assign(target,...source)
}

Object.getOwnPropertyDescriptors()
Es5的Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象(descriptor) , Es2017引入了Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。
const obj = {
  foo:123,
  get bar(){
    return 'aaa'
  }
}
Object.getOwnPropertyDescriptors(obj)
//{ foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

上面代码中,Object.getOwnPropertyDescriptors()方法返回一个对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该对象的描述对象。

该方法的引入,主要是为了解决Object.assign不能正常拷贝 set属性 和 get 属性的取值函数。

const source = {
    set foo(value){
      console.log(value)
    }
}
var obj = Object.assign({},source);
Object.getOwnPropertyDescriptor(obj,'foo')
// { value: undefined,
//   writable: true,
//   enumerable: true,
//   configurable: true }

上面代码中,source对象的foo属性的值是一个赋值函数,Object.assign方法将这个属性拷贝给target1对象,结果该属性的值变成了undefined,这是因为Object.assign方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法。

这时,Object.getOwnPropertyDescriptors()方法配合Object.defineProperties方法,就可以实现正确拷贝。

const source = {
  set foo(value){
    console.log(value)
  }
}
var target = {}
Object.defineProperties(target,Object.getOwnPropertyDescriptors(source)) 
Object.getOwnPropertyDescriptor(target,'foo')

上面的代码,将属性描述对象添加到目标对象里面。

下面讲解一下Object.defineProperties该方法

var json = {
  get foo(){
    return 123
  }
};
Object.defineProperties({},Object.getOwnPropertyDescriptors(json)) ;
//上面的写法,等同于下面的写法
Object.defineProperties({},{
  foo:{
     get(){
        return 123;
     },
     set(value){
        return value;
     }
  }
});

上面代码, json是一个对象,里面是一个取值函数 ,然后下面通过对象的属性描述方法给它添加到Object.defineProperties该方法里面。

另外,Object.getOwnPropertyDescriptors()方法的另一个用处,是配合Object.create() 方法,将对象属性克隆到一个新对象,这是浅拷贝。

var obj = {
  name:'秦司令'
};
Object.create(Object.getPrototypeOf(obj),Object.getOwnPropertyDescriptors(obj));

__ proto __ 属性(前后各两个下划线),用来读取或设置当前对象的prototype对象。目前,所有浏览器(包括IE11) 都部署了这个属性。
演示:
//Es5的写法
function demo(){}
var obj  =  {
    method:function(){}
}
obj.__ proto __ = demo
new demo() 

// Es6的写法

var obj  = Object.create(demo)

上面代码中,Es6的写法Object.create该方法直接是设置__ proto __ 属性的

该属性没有写入Es6的正文,而是写入了附录,原因是__ proto __前后的双下划线,说明它本质上是一个内部属性,而不是一个正式对外的API,只是由于浏览器广泛支持,才被加入Es6。标准明确规定,只有浏览器必须部署这个属性,其它运行环境不一定要部署。

Object.getPrototypeOf()
该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。
演示:
var obj = {}
Object.getPrototypeOf(obj) 

下面是一个列子。

function Fn(){}
var f = new Fn()
Object.getPrototypeOf(f) === Fn.prototype  //true
Object.setPrototypeOf(f,Object.prototype)
Object.getPrototypeOf(f) === Fn.prototype //false

如果参数不是对象,会自动转换为对象

Object.getPrototypeOf(1)
// 转换过程 Object.getPrototypeOf(Number(1))
// Number{ [[PrimitiveValue]]: 0 }
Object.getPrototypeOf('foo')
//转换过程 Object.getPrototypeOf(String('foo'))
// String{length:0,[[PrimitiveValue]]: ' ' }

Object.getPrototypeOf(1) == Number.prototype  // true
Object.getPrototypeOf('foo') == String.prototype // true

如果参数是undefined和null,它们无法转换为对象,会直接报错

Object.getPrototypeOf(null)   //报错
// typeError: Cannot convert null or undefined to Object
Object.getPrototypeOf(undefined)  //报错
// typeError: Cannot convert null or undefined to Object

如果一个对象本身部署了__ proto __属性,该属性的值就是对象的值。

Object.getPrototypeOf({__proto__:null}) // null 

Object.setPrototypeOf()
Object.setPrototypeOf方法的作用与__ proto __ 相同, 用来设置一个对象的prototype对象,返回参数对象本身。它是Es6正式推荐设置原型的对象的方法。
演示:
//格式
Object.setPrototypeOf(object,prototype)
//用法
const obj = Object.setPrototypeOf({},null) //设置这对象没有原型

//该方法等同于下面的函数
function setPrototypeOf(obj,proto1){
  obj.__proto__ = proto1
  return obj
}

下面是一个例子

let proto = {}
let obj = {name:'秦司令'}
Object.setPrototypeOf(obj,proto)

proto.x = 10;
proto.y = 20;

obj.name // 秦司令
obj.x // 10
obj.y // 20

上面代码将proto对象设为obj的原型,所以从obj对象可以读取proto对象的原型属性。

如果第一个参数不是对象,会自动转换为对象,但是由于返回的还是第一个参数,这个操作不会产生任何效果。

Object.setPrototypeOf(1,{})  === 1
Object.setPrototyoeOf('name' , {}) == 'name'
Object.setPrototypeOf(true,{}) == true

由于undefined和null无法转换为对象,所以第一个参数是undefined或null的话,会直接报错。

Object.setPrototypeOf(undefined,{}) //报错
Object.setPrototypeOf(null,{}) //报错
Object.keys()
Es5引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不包含继承的) 所有可遍历(enumerable)的属性键名。
var obj = {name:'秦司令',age:20};
Object.keys(obj)  //["name","age"]

Es2017引入了跟Object.keys配套的Object.values和Object.entries,作为遍历一个对象的补充手段,供for....of循环使用

var obj = {a:1,b:2,c:3};
for(let key of Object.keys(obj)){
  console.log(key)  // a b c
}
for(let values of Object.values(obj)){
  console.log(values) // 1 2 3
}
for(let [k,v] of Object.entries(obj)){
  console.log([k,v]) // [name,'秦司令',age:20]  
}
Object.values()
Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
var obj = {name:'秦司令',age:20};
Object.values(obj) // ["秦司令",20]

返回数组的成员顺序

var obj = {100:'a',2:'b',18:'c'}
Object.values(obj) // [b,c,d] 

上面代码中,属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是b,c,d

Object.values只返回自身可遍历属性

var json = Object.create({},{p:{value:123}})
Object.values(json)  //[]

上面代码中,Object.create方法的第二个参数添加的对象属性(属性p),如果不显式声明,默认是不可遍历的,因为p的属性描述对象的enumerable默认是false,Object.values不会返回这个属性,只要把enumerable改成true,Object.values就返回这个属性p值

var obj = Object.create({},{p:{
    value:2,
    enumerable:true 
}})
Object.values(obj)  //[2]

Object.values会过滤属性名为Symbol值的属性

Object.values({[Symbol()]:123,name:'秦司令'})
// ['秦司令']

如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。

Object.values('fooa')
// ['f','0','0','a']

上面代码中,字符串会先转换成一个类似数组的对象,字符串的每个字符,就是该对象的一个属性,因此,Object.values返回每个属性的键值,就是各个字符组成的一个数组

如果参数不是对象,Object.values会先将其转换为对象。由于数值和布尔值的包装类对象,都不会为实例添加非继承的属性,所以,Object.valus会返回空数组

Object.values(43) //[]
Object.values(true) // []
Object.entries()
Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
var obj = {foo:'bar',baz:43}
Object.entries(obj)
// [["foo","bar"],["baz",43]]

如果原对象的属性名是一个Symbol值,该属性会被忽略

Object.entries({[Symbol()]:123,foo:'abc'}) 
//[["foo","abc"]]

Object.entries的基本用途是遍历对象的属性

let obj = {one:1,tow:2}
for(let [k,v] of Object.entries(obj)){
  console.log(`${JSON.stringify(k)}:${JSON.stringify(v)}`)
}
// "one":1  
// "tow":2

Object.entries另一用处是将对象转换成Map结构

var obj = {foo:'bar',name:'秦司令'}
var map = new Map(Object.entries(obj))
// Map{foo:'bar',name:'秦司令'}

如有问题,望大神指点

上一篇下一篇

猜你喜欢

热点阅读