参考书《ECMAScript 6入门》
http://es6.ruanyifeng.com/

对象的扩展

1.属性名的简洁表示法 : ES6允许在代码中直接写变量,变量名是属性名,变量值是属性值。
  let key = "value";
  let obj = {key};//obj {key : "value"}
2.方法的简写表示法
  let obj = {
        method : function(x,y){
          return {x,y};//{x:x,y:y} 属性简写法在方法中的使用
         }
      }
  同
  let obj = {
        method(x,y){
          return {x,y};
        }
      }
3.属性表达式: obj[表达式]="value"
  a.用{}定义对象时定义属性的两种方式
  var obj = {
        length : 3,
        name : "test"
     }//ES5的方式
  let obj = {
        ["leng" + "th"] : 3,
        ["na"+"me"] : "test"
     }//ES6的方式
  function test2(){
     return true;
  }
  function ["test" + 2](){
     return true;
  }报错
  let ["test" + 4] = function(){}//报错
  let ["test" + 4] = new Function(){}//报错
  let obj = {
        ["test" + 4](){
            return true;
        }
     }正确
  obj //{test4: ƒ}
  b.读取对象中属性的方式
    obj.name
    obj['name']
  c.使用表达式定义属性的方式不可以与属性简写法混用
    let a = 1;
    let obj = {
          ["name"+1] : {[a]}//报错
    }
    let a = 1;
    let obj = {
          ["name"+1] : {a}//正确
    }
  d.当表达式定义的属性名是一个对象时,表达式运行结果是[Object object],不是对象对应的属性名
    let size = 3;
    let clothes = {size};
    let obj2 = {
          ["test" + obj] : "testagain"
    }
    obj2//{test[object Object]: "testagain"}
4.对象的name属性:对象里的方法的name属性返回方法所对应的方法名
  a.如果是对象里的get方法或者set方法,则对象方法的name属性返回get/set + 函数名
    let cat = {
           age:1,
           set mini(a){
             this.age = age;
           }
           get mini(){
             return this.age;
           }
        }
    cat.mini.name //undefined
    Object.getOwnPropertyDescriptor(cat,'mini').get.name //'get mini'
    Object.getOwnPropertyDescriptor(cat,'mini').set.name//'set mini'
  b.bind方法创造的对象的函数,name属性返回的是"bound" + 方法名
    let obj = {
        test(){}
    }
    obj.test.bind().name //"bound test"
  c.使用new Function方法创建的函数,name属性返回的是"anonymous" + 方法名
    (new Function()).name //"anonymous"
    (new Function()).bind().name // "bound anonymous"
5.Object.is() //严格相等判断
  相等判断
  "==" //ES5
  1 == '1' //true 即使1与'1'类型并不相同还是会判断它们相等,这是因为==引起了数据转换
  "==="//ES5
  1 === '1' //false 类型不同判断为false,比==规则更严格
  undefined === undefined //true
  null === null //true
  NaN === NaN //false ===漏洞
  +0 === -0 //true ===漏洞
 
  Object.is(a,b) //ES6 判断a,b是否相等
  Object.is(1,2) //false
  Object.is(1,2) //false
  Object.is(1,'1') //false
  Object.is(undefined,undefined)//true
  Object.is(null,null)//true
  在NaN与+0,-0的判断上比===更准确,其他与 === 一致
  Object.is(NaN,NaN) //true
  Object.is(+0,-0) //false
6.Object.assign() : 用于将源对象的所有可枚举属性复制到目标对象上
  a.此方法可以有多个参数,首参数一定是目标对象,不可以为null或者undefined,会报错;其余参数是源对象,当其余参数不是对象时,只有字符串类型会生效,其他数值,布尔类型都不会生效,遇到不能转化成对象的null或者undefined,则会跳过,不出错。
 
  Object.assign(null,{name : "test"}) //报错 Cannot convert undefined or null to object
  Object.assign(null,{name : "test"})//报错 Cannot convert undefined or null to object
  Object.assign(1,{name : "test"}); //Number {1, name: "test"} 首参数不是对象时,会先转成对象
 
  Object.assign({},undefined); //{}
  Object.assign({},null);//{}
 
  b.当其余参数不是对象时,只有字符串类型会以数组形式生效,其他数值,函数以及布尔类型都不会生效,
 
  Object.assign({},'abc');//{0: "a", 1: "b", 2: "c"}
  Object.assign({},100);// {}
  Object.assign({},true);//{}
  let f = function(){}
  Object.assign({},f);//{}
 
  d.用途
    (1)可以用来复制对象(浅拷贝)
        let target = {name: "target"}
        let source = {size: 13}
        let newobj = Object.assign(target,source);//得到新的target {name: "target", size: 13}
        newobj //{name: "target", size: 13}
        target //{name: "target", size: 13}
        target.size = 0;
        newobj.size // {name: "target", size: 0}
        newobj.size = 1;
        target //{name: "target", size: 1}  newobj与target是一个对象,任何一方的改动都会影响到另一方
        
        //浅拷贝
        let target = {name:"target"}
        let source = {cat:{name: "nn",age: "3"}}
        Object.assign(target,source); //{name:"target",cat:{name: "nn",age: "3"}}
        source.cat.name = "big";
        target.cat.name;// "big"
    (2)如果源对象中有和目标对象同名的属性,则源对象中的此属性会全部覆盖目标对象中的此属性
         let target = {
               cat:{
                  name: "nn",
                  age: "3",
                  color: "black"
              }
            }
        let source = {
               cat: {
                 name:"bigdeck"
               }
            }
        Object.assign(target,source);//{cat: cat: {name: "bigdeck"}} /source覆盖target的整个cat属性值,并不会只覆盖cat.name
    (3)如果目标对象和源对象都是数组,则会将数组当做对象来处理
       Object.assign(['a','b','c'],[1,3]); //[1,3,'c']
       这是因为处理时视为对象处理Object.assign({1:'a',2:'b',3:'c'},{1:1,2:3});
    (4)如果要复制的值是一个取值就、函数,则会先执行取值函数后赋值
       let obj = {
           cat : "nn",
           get cat(){
             return "cat";
           },
           set cat(a){
             this.cat = "a";
           }
       }
      Object.assign({},obj);//{cat: "cat"}
    (5)可以为对象添加属性
    Object.assign({},{name:"test"});//{name:"test"}
    class Cat{
       constructor(age,color){
          return Object.assign(this,{age,color});
       }
    }
    var c1 = new Cat(13,"white")
    c1 //cat {age: 13, color: "white"}
    (6)可以为对象添加函数
    Object.assign(c1,{run(){return "run fast";}});
    c1//Cat {age: 13, color: "white", run: ƒ}
    Object.assign(c1.__proto__,{run2(){return "run fast";}});
    c1.__proto__ //{run2: ƒ, constructor: ƒ}
    c1 //Cat {age: 13, color: "white", run: ƒ}
    (7)克隆对象
    Object.assign({},c1);//Cat {age: 13, color: "white", run: ƒ}
    Object.assign(Object.create(Object.getPrototypeOf(c1)),c1);//Cat {age: 13, color: "white", run: ƒ}
    (8)合并多个对象
     Object.assign({},{name:"test"},{age:13},{size:10});//{name: "test", age: 13, size: 10}
     let merge = (target,...sources) => Object.assign(target,sources);
     merge({},{name:"test"},{age:13},{size:10});//{name: "test", age: 13, size: 10}
     merge({},[{name:"test"},{age:13},{size:10}]);//{0: {name: "test"}, 1: {age: 13}, 2: {size: 10}}
    (9)为属性指定默认值
     const DEFAULT = {
        name : "test"
     }
     function init(){
        var c = Object.assign({},DEFAULT,options);
        return c;
     }
     init({name:"test2"});//利用了Object.assign同名属性后面的会全覆盖前面的同名属性的特点实现了給属性指定默认值(如果后面的对象没有与默认对象同名的属性,则使用默认对象中的属性)
    
7.属性的可枚举性与遍历
(1)可枚举性
Object.getOwnPropertyDescriptor()可以拿到对象的某个属性的描述对象
Object.getOwnPropertyDescriptor(c1,'age')
{
  configurable:true,
  enumerable:true,//此属性为true,表示被此对象描述的的属性是可枚举属性;false则为不可枚举属性
  value:13,
  writable:true
}
有四个操作会忽略enumerable为false的属性
for...in循环:只遍历对象自身的和继承的可枚举属性
Object.keys():返回对象自身的所有可枚举属性的键名
JSON.stringify():只串行化对象自身的可枚举的属性
Object.assign():忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性

//父类
function Animal(){
  this.name = 'Animal';
  this.age = 13;
  this.size = 100;
}
Object.defineProperties(Animal,{'gender':{value:"boy",enumerable:false},'color':{value:"red",enumerable:true}});
//原型链继承
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
Cat.prototype.name = 'cat';
Cat.ownSize = 10;
//new一个实例
var cat = new Cat();
Object.defineProperties(cat,{'sizecat':{value:1,enumerable:false},'colorcat':{value:"black",enumerable:true}});
for(var i in cat){
  console.log(i+" : "+cat[i]);
}
//colorcat : black自身的可枚举属性
//name : 继承自Cat的可枚举属性
//age : 13 继承字Animal的可枚举的属性
//size : 100继承自Animal的可枚举的属性
//constructor : function Cat(){}构造器
Object.keys(cat)//['colorcat'] cat自身可枚举的属性
JSON.stringify(cat)//'{"colorcat":"black"}' cat自身可枚举的属性
Object.assign({},cat);//{colorcat: "black"} cat自身可枚举的属性
(2)属性的遍历
for...in:只遍历对象自身的和继承的可枚举属性
Object.keys(obj):返回对象自身的所有可枚举属性的键名
Object.getOwnPropertyNames(obj):返回一个数组,包含对象自身的所有属性的键名,这个所有属性包含不可枚举属性但是不包含symbol属性的键名
Object.getOwnPropertyNames(cat);
// ["sizecat", "colorcat"] 'sizecat'对象自身的不可枚举属性,'colorcat'对象自身的可枚举属性
Object.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有Symbol属性的键名
Object.getOwnPropertySymbols(cat);
//[] cat没有Symbol属性
Reflect.ownKeys(obj):返回一个数组,包含对象自身的所有属性的键名,不管这个属性是Symbol还是字符串,也不管是否可枚举
Reflect.ownKeys(cat);
//["sizecat", "colorcat"] 'sizecat'对象自身的不可枚举属性,'colorcat'对象自身的可枚举属性
属性的遍历顺序
首先遍历所有数值键,按照数值升序排列
其次遍历所有的字符串键,按照时间加入顺序升序排列
最后遍历所有的Symbol键,按照时间加入顺序升序排列
var obj = {1:2,"test":"again","a":"testing",2:3}
obj["objSymbol"] = Symbol()
Reflect.ownKeys(obj);//["1", "2", "test", "a", "objSymbol"]

8.Object.getOwnPropertyDescriptor()与Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptor()返回某个对象属性的描述对象(此属性非继承属性)
Object.getOwnPropertyDescriptor(cat,'name')//undefined 因为name是继承属性
Object.getOwnPropertyDescriptor(cat,'colorcat')// {value: "black", writable: false, enumerable: true, configurable: false} 因为colorcat是自身属性

Object.getOwnPropertyDescriptors() 返回指定对象所有的自身属性(非继承属性)的描述对象。
Object.getOwnPropertyDescriptors(cat);
//
{
  catSymbol:{value: Symbol(), writable: true, enumerable: true, configurable: true}
  colorcat:{value: "black", writable: false, enumerable: true, configurable: false}
  sizecat:{value: 1, writable: false, enumerable: false, configurable: false}
}
9._proto_属性,getPrototypeOf()与setPrototypeOf()
(1)_proto_属性:用于读取或设置当前对象的prototype对象
var d = {name:"test"}
//读取对象的__proto__
d.__proto__ //{constructor: ƒ Object(), __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
//设置对象的__proto__
var c = {1:0};
d.__proto__ = c //返回的是c  {1: 0}
(2)Object.getPrototypeOf(param):用于读取一个对象的原型对象。如果参数不是对象,则转化成对象;如果不能转化成对象,如undefined或者null,则会报错。
Object.getPrototypeOf(d) //{1: 0}
Object.getPrototypeOf(1)//Number {0, constructor: ƒ, toExponential: ƒ, toFixed: ƒ, toPrecision: ƒ, toString: ƒ, …}
Object.getPrototypeof(undefined)//报错 Uncaught TypeError: Object.getPrototypeof is not a function
(3)Object.setPrototypeOf(obj1,obj2):用来设置当前对象的prototype对象,返回参数对象本身。
//将obj2设置为obj1的原型对象。如果第一个参数不是对象,则转化为对象,但因为返回的是第一个参数,所以没有变化;如果第一个参数不能转化成对象,如undefined或者null,则会报错。
Object.setPrototypeOf(d,Number); //Function {name: "test"}   
Object.getPrototypeOf(d);//ƒ Number() { [native code] }
10.super关键字:指向当前对象的原型对象
//super关键字在表示对象的原型时,只能用在对象的方法中,用在其他方法中都会报错。
let obj = {
    name:super.name//报错 super用在属性中
}
let obj = {
    method:function(){
      return super.name;//报错 super用在方法中,然后赋值给属性
    }
}
let obj = {
    method = ()=> super.name;//报错 super用在方法中,然后赋值给属性
}

let obj1 = {
    name:"obj1"
}

let obj2 = {
    name:"obj2",
    method(){
      return super.name;//正确 obj1
    }
}
Object.setPrototypeOf(obj2, obj1);
obj2.method //"obj1"
11.Object.keys(),Object.values()与Object.entries()
Object.keys():返回一个数组,数组成员是对象自身(不包含可继承)所有可遍历属性的键名
Object.values():返回一个数组,数组成员是对象自身所有可遍历属性的键值
Object.entries():返回一个数组,数组成员是对象自身所有可遍历的键值对
12.对象的扩展运算符
对象的扩展运算符...可以用于对象的解构赋值
(1)扩展运算符
可用于赋值对象
let obj = {name : "test",age : 13}
let obj3 = {...obj}
obj3 // {name: "test", age: 13}
如果参数是null或者undefined,将会忽略不报错
{...null}//{}
{...undefined}//{}
(2)解构赋值:解构赋值时,使用扩展运算符的参数必须是最后一个参数
 let {x,y,...z} = {x : "test",y : 13,size:23,color:"black"}
 x = "test"
 y = 13
 z = {size:23,color:"black"}
 let {...x,y,z} = {x : "test",y : 13,size:23,color:"black"} //报错