JS--对象的属性以及ES6下对象的语法糖

1,383 阅读7分钟

对象

对象的内容是一组键值对,值可以是数据或者函数。

 let persion = new Object();
    persion.name = 'Eden';
    persion.sayName = function () {
      console.log(this.name)
    }
  }

  let persion = {
    name: 'Eden',
    sayName() {
      console.log(this.name)
    }
    <!--用这两种方式创建的对象是等价的,他们的属性和方法都一样  -->

属性的特征(内部特征不可用js直接访问)

数据属性

数据属性包含一个保存值的位置,值会从这个位置读取,也会写入这个位置。 数据属性有四个特性描述他们的行为。

  • [[Confingurable]]:表示是否可以通过delete删除并重新定义,是否可以把它修改为浏览器属性,默认情况下,所有直接定义在对象上的属性该值都为true,如前面例子。
  • [[Enumerable]]:属性是否可以通过for-in循环返回,默认情况下,所有直接定义在对象上的属性该值都为true,如前面例子。
  • [[Writable]]:属性的值是否可被修改,默认情况下,所有直接定义在对象上的属性该值都为true,如前面例子。
  • [[Value]]:包含属性实际的值,就是前面提到读取和写入属性值的位置,默认为undefined,前面的例子中,name的[[Value]]特性会被设置为'Eden'。

如果要改变属性的默认特性,就要使用Object.definePropety(),该方法接收三个参数,对象,属性名称,和一个描述符对象。如下所示:

 let persion = {};
    Object.defineProperty(persion, 'name', {
      writable: false,
      value: 'Eden'
    })
    console.log(persion.name) //Eden
    persion.name = 'Rax'
    console.log(persion.name) //Eden

此外,当一个属性被定义为不可配置之后(Confingurable:false),就不能改为可以配置的了,再次调用Object.defineProperty()并修改任何非wirtable的属性都会跑出错误。 在调用Object.defineProperty()时,Confingurable,Enumerable,Writable值如果不指定,则都默认为false.

访问器属性

访问器属性不包含数据值,他们包含一个getter函数和一个setter函数,在读取访问器属性时,或调用getter函数,这个函数会返回一个有效的值。在写入访问器属性时,会调用setter函数并传入新的值。访问器属性有四个特性描述他们的行为。

  • [[Confingurable]]:表示是否可以通过delete删除并重新定义,是否可以把它修改为数据属性,默认情况下,所有直接定义在对象上的属性该值都为true。
  • [[Enumerable]]:属性是否可以通过for-in循环返回,默认情况下,所有直接定义在对象上的属性该值都为true,如前面例子。
  • [[Get]]:获取函数,在读取属性时调用,默认值为undefined。
  • [[Set]]:设置函数,在写入属性时调用,默认值为undefined。
    访问器属性不能直接定义,必须使用Object.defineProperty(),如下:
let book = {
      year_: 2017,
      edition: 1
    }
    Object.defineProperty(book, 'year', {
      get() {
        return this.year_
      },
      set(newValue) {
        if (newValue > 2017) {
          this.year_ = newValue;
          this.edition += newValue - 2017
        }
      }
    })
    book.year = 2018;
    console.log(book.edition) //2

定义多个属性值

Object.defineProperties(book, {
      year_: {
        value:2017
      },
      edition: {
        value:1
      },
      year: {
        get() {
          return this.year_;
        },
        set(newValue) {
          if (newValue > 2017) {
          this.year_ = newValue;
          this.edition += newValue - 2017
        }
        }
      }
    })

这段代码直接定义了两个数据属性year_和edition,还有一个访问器属性year,与上面例子的区别在于数据属性Confingurable,Enumerable,Writable的特性值都为false。

读取属性的特征。

Obiect.getOwnPropertyDescroptor(),可获取指定属性的属性描述符。 Obiect.getOwnPropertyDescroptor(book,'year_')
Obiect.getOwnPropertyDescroptors()会在每个自有属性上调用Obiect.getOwnPropertyDescroptor(),并在一个新对象中返回他们。 Obiect.getOwnPropertyDescroptors(book)

合并对象(把源对象的所有本地属性一起复制到目标对象上)

Object.assign(),该方法接收一个目标对象和多个源对象作为参数,每个源对象中可枚举(Object.proprtyIsEnumerable()return true)和自有(Object.hasOwnPropety()return true)属性复制到目标对象,对每个符合条件的属性,这个方法会使用源对象[[Get]]取得属性的值,然后使用目标对象上的[[Set]]设置属性的值。

    // 单个源对象
    let result, dest, a;
    dest = {};
    a = { id: 'aid' }
    result = Object.assign(dest, a)
    // Object.assign修改目标对象,也会返回修改后的目标对象。
    console.log(result === dest) //true
    console.log(a === dest) //false

    // 多个源对象
    dest = {};
    result = Object.assign(dest, { a: 'aid' }, { b: 'bid' })

   // 设置函数与获取函数 (对象上的方法,不是浏览器属性上的方法)
     dest = {
      set id(value) {
        console.log('id:', value);
      }
    }
    a = {
      get id() {
        return 'aid'
      }
    }
    Object.assign(dest, a)
    console.log(dest); //{}因为设置函数没有赋值操作

Object.assign对每个源对象执行的是浅复制,如一多个源对象有相同的属性,则使用最后一个复制的值,不能在两个对象之间转移获取函数和设置函数,源对象访问器上的获取函数会作为一个静态值复制给目标对象。

对象引用

浅复制意味着只会复制对象的引用

let  dest, src;
    dest = {}
    src = {
     a:{}
    }
    Object.assign(dest, src)
    console.log(dest.a === src.a);//true

如果赋值出错,则操作会终止并退出,但是在报出错误之前目标对象上已经完成的修改会继续存在。

对象标志及相等判定

边界值情况,===操作符判断出错

    console.log(+0 === -0);//true
    console.log(+0 === 0);//true
    console.log(-0 === 0);//true
    console.log(NaN === NaN);//false

    // // 可以使用Object.is()
    console.log(Object.is(+0, -0));//false
    console.log(Object.is(+0, 0));//true
    console.log(Object.is(-0, 0));//false
    console.log(Object.is(NaN, NaN));//true

    // 检查超过两个值的可以用递归属性
    const checkRqual = (x, ...rest) => {
      console.log(rest[0]);
      return Object.is(x, rest[0]) &&
        (rest.length < 2 || checkRqual(...rest))
    }
    console.log(checkRqual(0, +0, 0, 0)); //true
    console.log(checkRqual(0, +0, -0, 0)); //false

增强对象的语法(ES6语法糖)

属性值简写

// 当属性名和变量一致时
    let name = 'Eden'
    let persion = {
      name: name
    }
  }

  // 可适用以下方式简写,如果没有找到同名变量,则会抛出ReferenceError
  let persion = {
    name
  }

可计算属性

   // 使用变量的值做为属性
    const nameKey = 'name';
    const ageKey = 'age';
    let persion = {};
    persion[nameKey] = 'Eden';
    persion[ageKey] = 24;
    console.log(persion); //{name:'Eden',age:24}

//  有了可计算属性后,可在对象字面量中完成动态赋值,中括号包围的对象键做为js表达式而不是字符串
    const nameKey = 'name';
    const ageKey = 'age';
    let persion = {
      [nameKey]: 'Eden',
      [ageKey]:24
    }
    console.log(persion); //{name:'Eden',age:24}

可计算属性可以是复杂的表达式,可以时一个方法,在实例化时在求值

简写方法名

 // 给对象定义方法
    let persion = {
      sayName: function (name) {
        console.log(`My name is ${name}`);
      }
    }
    // 简写方法
    let persion = {
      sayName(name) {
        console.log(`My name is ${name}`);
  }

简写方法名与可计算属性可以相互兼容

对象解构

ES6新增语法,使用与对象匹配的结构来实现对象一个或多个的属性赋值。

  • 不使用解构时
    let persion = {
      name: 'Eden',
      age: 24
    }
    let persionName = persion.name,
      persionAge = persion.age;
  • 使用解构语法
    let persion = {
      name: 'Eden',
      age: 24
    }
    let { name: persionName, age: persionAge } = persion;
  • 如果变量直接使用属性名称,可以简写
    let persion = {
      name: 'Eden',
      age: 24
    }
    let { name, age } = persion;
  • 当引用属性不存在,则该变量的值为undefined
    let persion = {
      name: 'Eden',
      age: 24
    }
    let { name, job } = persion
    console.log(jib);//undefined
    // 解构时可定义默认值
    let { name, job = 'programmer' } = persion;
    console.log(jib);//programmer
  • 解构变量可以事先声明,此时赋值表达式必须包含在一对括号里
    let persion = {
      name: 'Eden',
      age: 24
    }
    let persionName, persionAge;
    ({ name: persionName, age: persionAge }) = persion;
  • 解构在内部使用函数ToObject(),这意味着在对象解构上下文中,原始值会被当作对象,null和undefined不能被解构
    let { length } = 'Eden'
    console.log(length); //4
    let { constructor } = 4
    console.log(constructor === Number);//true

嵌套解构

  • 通过解构来赋值对象属性
    let persion = {
      name: 'Eden',
      age: 24,
      job: {
        content:'programmer'
      }
    }
    let otherPersion = {};
    ({
      name: otherPersion.name,
      age: otherPersion.age,
      job: otherPersion.job
    } = persion)
    // 因为只是对象的引用被赋值,所以改变persion.job会影响otherPersion
    persion.job.content = 'teacher'
    console.log(otherPersion.job.content);//teacher
  • 赋值解构可以使用嵌套解构
    let persion = {
      name: 'Eden',
      age: 24,
      job: {
        content:'programmer'
      }
    }
    let { job: { content } } = persion
    console.log(content);//programmer
  • 无论时源对象还是目标对象,在外层属性没有定义的情况下不能使用嵌套解构
    let persion = {
      name: 'Eden',
      age: 24,
      job: {
        content: 'programmer'
      }
    }
    let otherPersion = {};
    // foo在源对象上是undefined
    ({
      foo: {
        bar: otherPersion.bar
      }
    } = persion) //TypeError

    //foo在目标对象上是undefined
    ({
      job: {
        content: otherPersion.job.content
      }
    } = persion)//TypeError

部分解构

如果一个表达式涉及多个赋值,开始赋值成功后面出错,则解构只会完成一部分,后面失败的属性值为undefined

参数上下文匹配

函数参数列表中也可以使用解构赋值

    let persion = {
      name: 'Eden',
      age: 24,
      job: {
        content: 'programmer'
      }
    }
    function printPersion({ name: persionName, age: persionAge }) {
      console.log(persionName, persionAge);
    }
    printPersion(persion) //Eden 24