李厚春吧 关注:3贴子:238

【对象的扩展】语法说明

只看楼主收藏回复

介绍对象的扩展的各种规则,语法。


IP属地:河南1楼2017-05-19 16:42回复
    1.可用简洁表示法。
    var birth = '1990/2/22';
    var Person = {
    birth,
    //birth: birth,
    name: '汪汪'
    hello() {console.log('我的名字是',this.name)}
    //hello: function() {console.log('我的名字是',this.name)}
    }
    es6允许在对象之中直接写变量。属性名为变量名,属性值为变量值。
    另一个例子:
    function (x, y) {
    return {x,y}
    }
    相当于:
    function (x, y) {
    return {x:x, y:y}
    }


    IP属地:河南2楼2017-05-19 16:44
    回复
      2。属性名表达式。
      JavaScript语言定义对象的属性,有两种方法。
      // 方法一obj.foo = true;// 方法二obj['a' + 'bc'] = 123;
      上面代码的方法一是直接用标识符作为属性名,方法二是用表达式作为属性名,这时要将表达式放在方括号之内。
      但是,如果使用字面量方式定义对象(使用大括号),在 ES5 中只能使用方法一(标识符)定义属性。
      var obj = { foo: true, abc: 123};
      ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。
      let propKey = 'foo';let obj = { [propKey]: true, ['a' + 'bc']: 123};
      下面是另一个例子。
      var lastWord = 'last word';var a = { 'first word': 'hello', [lastWord]: 'world'};a['first word'] // "hello"a[lastWord] // "world"a['last word'] // "world"
      表达式还可以用于定义方法名。
      let obj = { ['h' + 'ello']() { return 'hi'; }};obj.hello() // hi
      注意,属性名表达式与简洁表示法,不能同时使用,会报错。
      // 报错var foo = 'bar';var bar = 'abc';var baz = { [foo] };// 正确var foo = 'bar';var 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]属性。


      IP属地:河南3楼2017-05-19 16:57
      回复
        3.方法的name属性
        函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。
        const person = { sayName() { console.log('hello!'); },};person.sayName.name // "sayName"
        上面代码中,方法的name属性返回函数名(即方法名)。
        如果对象的方法使用了取值函数(getter)和存值函数(setter),则name属性不是在该方法上面,而是该方法的属性的描述对象的get和set属性上面,返回值是方法名前加上get和set。
        const obj = { get foo() {}, set foo(x) {}};obj.foo.name// TypeError: Cannot read property 'name' of undefinedconst descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');descriptor.get.name // "get foo"descriptor.set.name // "set foo"
        有两种特殊情况:bind方法创造的函数,name属性返回bound加上原函数的名字;Function构造函数创造的函数,name属性返回anonymous。
        (new Function()).name // "anonymous"var doSomething = function() { // ...};doSomething.bind().name // "bound doSomething"
        如果对象的方法是一个 Symbol 值,那么name属性返回的是这个 Symbol 值的描述。
        const key1 = Symbol('description');const key2 = Symbol();let obj = { [key1]() {}, [key2]() {},};obj[key1].name // "[description]"obj[key2].name // ""
        上面代码中,key1对应的 Symbol 值有描述,key2没有。


        IP属地:河南4楼2017-05-19 17:18
        回复
          4. Object.is()
          ES5比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
          ES6提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
          Object.is('foo', 'foo')// trueObject.is({}, {})// false
          不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
          +0 === -0 //trueNaN === NaN // falseObject.is(+0, -0) // falseObject.is(NaN, NaN) // true
          ES5可以通过下面的代码,部署Object.is。
          Object.defineProperty(Object, 'is', { value: function(x, y) { if (x === y) { // 针对+0 不等于 -0的情况 return x !== 0 || 1 / x === 1 / y; } // 针对NaN的情况 return x !== x && y !== y; }, configurable: true, enumerable: false, writable: true});


          IP属地:河南5楼2017-05-19 17:35
          收起回复
            5.Object.assign()
            吧源对象所有可枚举属性添加到目标对象里。
            如果源对象存在重名属性 后面覆盖前面
            源对象里除了字符串会议数组形式拷贝入目标对象里外,其他都不生效。
            var target = {};
            var s1 = '李厚春';
            var s2 = true;
            var s3 = 10;
            console.log(Object.assign(target, s1, s2, s3))
            对于null 和undefined 如果出现在目标对象位置,报错。出现在源对象位置,不报错。但是会跳过。


            IP属地:河南6楼2017-05-19 17:58
            回复
              Object.assign()的注意点:
              Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
              var obj1 = {a: {b: 1}};var obj2 = Object.assign({}, obj1);obj1.a.b = 2;obj2.a.b // 2
              上面代码中,源对象obj1的a属性的值是一个对象,Object.assign拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。
              对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。
              var target = { a: { b: 'c', d: 'e' } }var source = { a: { b: 'hello' } }Object.assign(target, source)// { a: { b: 'hello' } }
              上面代码中,target对象的a属性被source对象的a属性整个替换掉了,而不会得到{ a: { b: 'hello', d: 'e' } }的结果。这通常不是开发者想要的,需要特别小心。
              有一些函数库提供Object.assign的定制版本(比如Lodash的_.defaultsDeep方法),可以解决浅拷贝的问题,得到深拷贝的合并。
              注意,Object.assign可以用来处理数组,但是会把数组视为对象。
              Object.assign([1, 2, 3], [4, 5])// [4, 5, 3]
              上面代码中,Object.assign把数组视为属性名为0、1、2的对象,因此源数组的0号属性4覆盖了目标数组的0号属性1。


              IP属地:河南7楼2017-05-19 18:04
              回复
                Object.assign()方法的常见用途:
                一:为对象添加属性
                class Point { constructor(x, y) { Object.assign(this, {x, y}); }}
                上面方法通过Object.assign方法,将x属性和y属性添加到Point类的对象实例。
                二:为对象添加方法
                Object.assign(SomeClass.prototype, { someMethod(arg1, arg2) { ··· }, anotherMethod() { ··· }});// 等同于下面的写法SomeClass.prototype.someMethod = function (arg1, arg2) { ···};SomeClass.prototype.anotherMethod = function () { ···};
                上面代码使用了对象属性的简洁表示法,直接将两个函数放在大括号中,再使用assign方法添加到SomeClass.prototype之中。
                三: 克隆对象
                function clone(origin) { return Object.assign({}, origin);}
                上面代码将原始对象拷贝到一个空对象,就得到了原始对象的克隆。
                不过,采用这种方法克隆,只能克隆原始对象自身的值,不能克隆它继承的值。如果想要保持继承链,可以采用下面的代码。
                function clone(origin) { let originProto = Object.getPrototypeOf(origin); return Object.assign(Object.create(originProto), origin);}
                四:合并多个对象
                将多个对象合并到某个对象。
                const merge = (target, ...sources) => Object.assign(target, ...sources);
                如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。
                const merge = (...sources) => Object.assign({}, ...sources);
                五:为属性指定默认值
                const DEFAULTS = { logLevel: 0, outputFormat: 'html'};function processContent(options) { options = Object.assign({}, DEFAULTS, options); console.log(options); // ...}
                上面代码中,DEFAULTS对象是默认值,options对象是用户提供的参数。Object.assign方法将DEFAULTS和options合并成一个新对象,如果两者有同名属性,则option的属性值会覆盖DEFAULTS的属性值。
                注意,由于存在浅拷贝的问题,DEFAULTS对象和options对象的所有属性的值,最好都是简单类型,不要指向另一个对象。否则,DEFAULTS对象的该属性很可能不起作用。
                const DEFAULTS = { url: { host: 'example.com', port: 7070 },};processContent({ url: {port: 8000} })// {// url: {port: 8000}// }
                上面代码的原意是将url.port改成8000,url.host不变。实际结果却是options.url覆盖掉DEFAULTS.url,所以url.host就不存在了。


                IP属地:河南8楼2017-05-19 19:04
                回复
                  Object.assign()的用法:
                  一: 为对象增加属性
                  //为对象添加属性
                  class Point {
                  constructor(x, y) {
                  Object.assign(this, {x, y})
                  }
                  }
                  // Object.assign方法,将x属性和y属性添加到Point类的对象实例。
                  二: 为对象增加方法
                  //为对象添加方法
                  Object.assign(SomeClass.prototype, {
                  someMethod(arg1, arg2) {
                  },
                  anotherMethod() {
                  }
                  })
                  //上面相当于下面的写法
                  SomeClass.prototype.someMethod = function (arg1, arg2) {};
                  SomeClass.prototype.anotherMethod = function () {};
                  三:克隆对象
                  // 原始对象克隆到一个空对象,就得到了原始对象的克隆
                  function clone(origin) {
                  Object.assing({}, origin)
                  }
                  // 上面的方法只能克隆对象自身的值,不能克隆继承的值。
                  // 如果要保持继承连,就用下面的方法
                  function clone(origin) {
                  let originProto = Object.getPrototypeof(origin);
                  return Object.assign(Object.create(originProto), origin)
                  }
                  // Object.create() 使用指定的原型对象创建一个新的对象
                  四:合并多个对象
                  // 合并多个对象
                  const merge = (target, ...sources) => Object.assign(target, ...sources)
                  // 如果要返回一个新对象,可以这样做
                  const marge = (...sources) => Object.assign({},...sources)
                  五:给属性指定默认值
                  //可以给属性指定默认值
                  constDEFAULTS = {
                  logLevel: 0,
                  outputFormat: 'html'
                  }
                  function processContent(options) {
                  options = Object.assign({}, DEFAULTS, options)
                  console.log(options)
                  }
                  //DEFAULT是默认的,options是用户传入的数据,options会覆盖默认里的值。
                  //浅拷贝,所以DEFAULTS和options里要是简单类型,不要指向另一个对象,
                  //否则,DEFAULT里的默认值可能就不起作用而被 options里的属性覆盖掉。


                  IP属地:河南9楼2017-05-19 19:07
                  回复
                    6.属性的可枚举性
                    Object.getOwnPropertyDescriptor()
                    或许对象的某属性的描述对象。
                    let obj = {foo: 123}
                    let str = Object.getOwnPropertyDescriptor(obj, 'foo');
                    console.log(str)
                    //{value: 123, writable: true, enumerable: true, configurable: true}
                    ES5有三个操作会忽略enumerable为false的属性。
                    for...in循环:只遍历对象自身的和继承的可枚举的属性
                    Object.keys():返回对象自身的所有可枚举的属性的键名
                    JSON.stringify():只串行化对象自身的可枚举的属性
                    ES6新增了一个操作Object.assign(),会忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。
                    这四个操作之中,只有for...in会返回继承的属性。实际上,引入enumerable的最初目的,就是让某些属性可以规避掉for...in操作。比如,对象原型的toString方法,以及数组的length属性,就通过这种手段,不会被for...in遍历到。
                    Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable// falseObject.getOwnPropertyDescriptor([], 'length').enumerable// false
                    上面代码中,toString和length属性的enumerable都是false,因此for...in不会遍历到这两个继承自原型的属性。
                    另外,ES6规定,所有Class的原型的方法都是不可枚举的。
                    Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable// false
                    总的来说,操作中引入继承的属性会让问题复杂化,大多数时候,我们只关心对象自身的属性。所以,尽量不要用for...in循环,而用Object.keys()代替。


                    IP属地:河南10楼2017-05-19 21:13
                    收起回复
                      7.属性的遍历
                      有五种属性遍历方法:
                      一: for...in
                      遍历自身和继承的所有可枚举属性。不包过Symbol属性
                      二: Object.keys()
                      返回自身的(不含继承的)所有可枚举属性,不包括Symbol属性
                      三: Object.getOwnPropertyNames(obj)
                      返回一个数组,它包含对象自身的所有属性,包含不可枚举的属性,但是不包括Symbol属性
                      四:Object.getOwnpropertySymbols(obj)
                      返回一个数组,包含对象的所有可枚举的Symbol属性
                      五:Reflect.ownKeys(obj)
                      返回一个数组,包含对象的所有属性,不管属性名是Symbol还是字符串,也不管是否可枚举。
                      这五种方法遵循同样的遍历准则:
                      首先遍历所有属性名为数值的属性,按数字排列
                      其次遍历所有属性名为字符串的属性,按生成时间排列
                      最后遍历所有属性名为Symbol的属性,按生成时间排列。
                      下面是个小例子:
                      let obj = {
                      [Symbol()]:0,
                      b:0,
                      10: 0,
                      2: 0,
                      a: 0
                      }
                      var res = Reflect.ownKeys(obj);
                      console.log(res)
                      //["2", "10", "b", "a", Symbol()]


                      IP属地:河南11楼2017-05-19 21:48
                      收起回复
                        8 __proto__ Object.setPrototypeOf() Object.getPrototypeOf()
                        __proto__没写入的es6正文,而是写在目录里。是因为本质上,他是一个内部属性,不是一个正式的对外公开的API,不过浏览器广泛支持。
                        但是还是建议不要用__proto__,而是用Object.setPrototypeOf()(写操作) Object.getPrototypeOf() (读操作) 以及 Object.create()(生成操作)来替代他
                        在实现上__proto__调用的是Object.prototype.__proto__。具体实现参考阮一峰的es6入门。
                        如果一个对象本身部署了__proto__属性,则该属性的值就是对象的原型。
                        Object.getPrototypeOf({ __proto__: null })// null


                        IP属地:河南12楼2017-05-19 22:11
                        回复
                          关于Object.setPrototypeOf()
                          用法和__proto__相同,设置一个对象的原型(prototype)对象,返回参数对象本身。
                          let obj = {x : 12};
                          let proto = {};
                          Object.setPrototypeOf(obj, proto);
                          proto.y = 20;
                          proto.z = 30;
                          console.log(obj.z)
                          // 结果为30


                          IP属地:河南13楼2017-05-19 22:29
                          收起回复
                            关于 Object.getPrototypeOf() 和上面的set配套使用。用来读取一个对象的原型对象
                            function Rectangle() { // ...}var rec = new Rectangle();Object.getPrototypeOf(rec) === Rectangle.prototype// trueObject.setPrototypeOf(rec, Object.prototype);Object.getPrototypeOf(rec) === Rectangle.prototype// false
                            如果参数不是对象,会被自动转化为对象。
                            // 等同于 Object.getPrototypeOf(Number(1))Object.getPrototypeOf(1)// Number {[[PrimitiveValue]]: 0}// 等同于 Object.getPrototypeOf(String('foo'))Object.getPrototypeOf('foo')// String {length: 0, [[PrimitiveValue]]: ""}// 等同于 Object.getPrototypeOf(Boolean(true))Object.getPrototypeOf(true)// Boolean {[[PrimitiveValue]]: false}Object.getPrototypeOf(1) === Number.prototype // trueObject.getPrototypeOf('foo') === String.prototype // trueObject.getPrototypeOf(true) === Boolean.prototype // true


                            IP属地:河南14楼2017-05-19 22:32
                            回复
                              9.object.keys() object.values() object.entries()
                              ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
                              var obj = { foo: 'bar', baz: 42 };Object.keys(obj)// ["foo", "baz"]
                              ES2017 引入了跟Object.keys配套的Object.values和Object.entries,作为遍历一个对象的补充手段,供for...of循环使用。
                              let {keys, values, entries} = Object;let obj = { a: 1, b: 2, c: 3 };for (let key of keys(obj)) { console.log(key); // 'a', 'b', 'c'}for (let value of values(obj)) { console.log(value); // 1, 2, 3}for (let [key, value] of entries(obj)) { console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]}Object.values()
                              Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
                              var obj = { foo: 'bar', baz: 42 };Object.values(obj)// ["bar", 42]
                              返回数组的成员顺序,与本章的《属性的遍历》部分介绍的排列规则一致。
                              var obj = { 100: 'a', 2: 'b', 7: 'c' };Object.values(obj)// ["b", "c", "a"]
                              上面代码中,属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是b、c、a。
                              Object.values只返回对象自身的可遍历属性。
                              var obj = Object.create({}, {p: {value: 42}});Object.values(obj) // []
                              上面代码中,Object.create方法的第二个参数添加的对象属性(属性p),如果不显式声明,默认是不可遍历的,因为p的属性描述对象的enumerable默认是false,Object.values不会返回这个属性。只要把enumerable改成true,Object.values就会返回属性p的值。
                              var obj = Object.create({}, {p: { value: 42, enumerable: true }});Object.values(obj) // [42]
                              Object.values会过滤属性名为 Symbol 值的属性。
                              Object.values({ [Symbol()]: 123, foo: 'abc' });// ['abc']
                              如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。
                              Object.values('foo')// ['f', 'o', 'o']
                              上面代码中,字符串会先转成一个类似数组的对象。字符串的每个字符,就是该对象的一个属性。因此,Object.values返回每个属性的键值,就是各个字符组成的一个数组。
                              如果参数不是对象,Object.values会先将其转为对象。由于数值和布尔值的包装对象,都不会为实例添加非继承的属性。所以,Object.values会返回空数组。
                              Object.values(42) // []Object.values(true) // []Object.entries
                              Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
                              var obj = { foo: 'bar', baz: 42 };Object.entries(obj)// [ ["foo", "bar"], ["baz", 42] ]
                              除了返回值不一样,该方法的行为与Object.values基本一致。
                              如果原对象的属性名是一个 Symbol 值,该属性会被忽略。
                              Object.entries({ [Symbol()]: 123, foo: 'abc' });// [ [ 'foo', 'abc' ] ]
                              上面代码中,原对象有两个属性,Object.entries只输出属性名非 Symbol 值的属性。将来可能会有Reflect.ownEntries()方法,返回对象自身的所有属性。
                              Object.entries的基本用途是遍历对象的属性。
                              let obj = { one: 1, two: 2 };for (let [k, v] of Object.entries(obj)) { console.log( `${JSON.stringify(k)}: ${JSON.stringify(v)}` );}// "one": 1// "two": 2
                              Object.entries方法的另一个用处是,将对象转为真正的Map结构。
                              var obj = { foo: 'bar', baz: 42 };var map = new Map(Object.entries(obj));map // Map { foo: "bar", baz: 42 }
                              自己实现Object.entries方法,非常简单。
                              // Generator函数的版本function* entries(obj) { for (let key of Object.keys(obj)) { yield [key, obj[key]]; }}// 非Generator函数的版本function entries(obj) { let arr = []; for (let key of Object.keys(obj)) { arr.push([key, obj[key]]); } return arr;}


                              IP属地:河南15楼2017-05-19 23:00
                              回复