kekobin/blog

Javascript零碎之基础二

kekobin opened this issue · 0 comments

Object

Object常见静态方法总览:
(1)对象属性模型的相关方法

  • Object.getOwnPropertyDescriptor():获取某个属性的描述对象。
  • Object.defineProperty():通过描述对象,定义某个属性。
  • Object.defineProperties():通过描述对象,定义多个属性。

(2)控制对象状态的方法

  • Object.preventExtensions():防止对象扩展。
  • Object.isExtensible():判断对象是否可扩展。
  • Object.seal():禁止对象配置。
  • Object.isSealed():判断一个对象是否可配置。
  • Object.freeze():冻结一个对象。
  • Object.isFrozen():判断一个对象是否被冻结。

(3)原型链相关方法

  • Object.create():该方法可以指定原型对象和属性,返回一个新的对象。
  • Object.getPrototypeOf():获取对象的Prototype对象。

Object.create(proto,[propertiesObject])

  • proto:新创建对象的原型对象
  • propertiesObject:可选。要添加到新对象的可枚举(新添加的属性是其自身的属性,而不是其原型链上的属性)的属性。

image

由上图可见,proto 为null、Object、Object.prototype时,使用Object.create创建新对象的结构都是不一样的。不同点在于"是否继承与Object"、"还是继承于Object.prototype"。

为什么vue中普遍使用 Object.create(null) 初始化新对象,而不是{}呢?

  • 使用create(null)创建的对象,没有任何属性,即可以把它当作一个非常纯净的map来使用,可以自己定义hasOwnProperty、toString方法,而不必担心会将原型链上的同名方法覆盖掉。
  • 可以节省hasOwnProperty带来的一丢丢性能损失并且可以偷懒少些一点代码。因为在我们使用for..in循环的时候会遍历对象原型链上的属性,使用create(null)就不必再对属性进行检查了,当然,我们也可以直接使用Object.keys[]。

Object.keys(),Object.getOwnPropertyNames()

对于一般的对象来说,Object.keys()和Object.getOwnPropertyNames()返回的结果是一样的。只有涉及不可枚举属性时,才会有不一样的结果。Object.keys方法只返回可枚举的属性,Object.getOwnPropertyNames方法还返回不可枚举的属性名。

var a = ['Hello', 'World'];

Object.keys(a) // ["0", "1"]
Object.getOwnPropertyNames(a) // ["0", "1", "length"]

属性描述对象

JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等。这个内部数据结构称为“属性描述对象”(attributes object)。每个属性都有自己对应的属性描述对象,保存该属性的一些元信息。

下面是属性描述对象的一个例子。

{
  value: 123, // 默认为undefined
  writable: false, // 默认为true
  enumerable: true,
  configurable: false,
  get: undefined,
  set: undefined
}
  • enumerable: 表示该属性是否可遍历,默认为true。如果设为false,会使得某些操作(比如for...in循环、Object.keys())跳过该属性。
  • configurable: 表示可配置性,默认为true。如果设为false,将阻止某些操作改写该属性,比如无法删除该属性,也不得改变该属性的属性描述对象(value属性除外)。也就是说,configurable属性控制了属性描述对象的可写性。
  • get: 表示该属性的取值函数(getter),默认为undefined。
  • set: 表示该属性的存值函数(setter),默认为undefined。

Object.getOwnPropertyDescriptor(obj, propertyName)

获取属性描述对象。它的第一个参数是一个对象,第二个参数是一个字符串,对应该对象的某个属性名。

而 Object.getOwnPropertyDescriptors(obj) 功能一样,只不过获取obj所有属性的描述。

image

注意,Object.getOwnPropertyDescriptor方法只能用于对象自身的属性,不能用于继承的属性。

Object.getOwnPropertyNames() 和 Object.keys()

前者返回参数对象自身的全部属性的属性名,不管该属性是否可遍历;后者只返回可遍历的属性。

image

Object.defineProperty(),Object.defineProperties()

  • Object.defineProperty方法允许通过属性描述对象,定义或修改一个属性,然后返回修改后的对象,它的用法如下。
Object.defineProperty(object, propertyName, attributesObject)
var obj = Object.defineProperty({}, 'p', {
  value: 123,
  writable: false,
  enumerable: true,
  configurable: false
});

obj.p // 123

obj.p = 246;
obj.p // 123
  • Object.defineProperties 定义或修改多个属性,然后返回修改后的对象
var obj = Object.defineProperties({}, {
  p1: { value: 123, enumerable: true },
  p2: { value: 'abc', enumerable: true },
  p3: { get: function () { return this.p1 + this.p2 },
    enumerable:true,
    configurable:true
  }
});

obj.p1 // 123
obj.p2 // "abc"
obj.p3 // "123abc"

注意,一旦定义了取值函数get(或存值函数set),就不能将writable属性设为true,或者同时定义value属性,否则会报错。

enumerable

如果一个属性的enumerable为false,下面三个操作不会取到该属性。

  • for..in循环
  • Object.keys方法
  • JSON.stringify方法

因此,enumerable可以用来设置“秘密”属性。

var obj = {};

Object.defineProperty(obj, 'x', {
  value: 123,
  enumerable: false
});

obj.x // 123

for (var key in obj) {
  console.log(key);
}
// undefined

Object.keys(obj)  // []
JSON.stringify(obj) // "{}"

configurable

configurable(可配置性)返回一个布尔值,决定了是否可以修改属性描述对象。也就是说,configurable为false时,value、writable、enumerable和configurable都不能被修改了。也决定了目标属性是否可以被删除(delete)。

存取器

除了直接定义以外,属性还可以用存取器(accessor)定义。其中,存值函数称为setter,使用属性描述对象的set属性;取值函数称为getter,使用属性描述对象的get属性。

var obj = Object.defineProperty({}, 'p', {
  get: function () {
    return 'getter';
  },
  set: function (value) {
    console.log('setter: ' + value);
  }
});

obj.p // "getter"
obj.p = 123 // "setter: 123"

JavaScript 还提供了存取器的另一种写法:

var obj = {
  get p() {
    return 'getter';
  },
  set p(value) {
    console.log('setter: ' + value);
  }
};

存取器往往用于,属性的值依赖对象内部数据的场合。

对象的拷贝

var extend = function (to, from) {
  for (var property in from) {
    if (!from.hasOwnProperty(property)) continue;
    Object.defineProperty(
      to,
      property,
      Object.getOwnPropertyDescriptor(from, property)
    );
  }

  return to;
}

extend({}, { get a(){ return 1 } })
// { get a(){ return 1 } })

控制对象状态

有时需要冻结对象的读写状态,防止对象被改变。JavaScript 提供了三种冻结方法,最弱的一种是Object.preventExtensions,其次是Object.seal,最强的是Object.freeze。

  • Object.preventExtensions(): 可以使得一个对象无法再添加新的属性。

image

  • Object.seal(): 使得一个对象既无法添加新属性,也无法删除旧属性。

image

Object.seal实质是把属性描述对象的configurable属性设为false,因此属性描述对象不再能改变了。
Object.seal只是禁止新增或删除属性,并不影响修改某个属性的值。

  • Object.freeze():可以使得一个对象无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量。

image

  • Object.isFrozen(): 用于检查一个对象是否使用了Object.freeze方法。
    用途是,确认某个对象没有被冻结后,再对它的属性赋值。
if (!Object.isFrozen(obj)) {
  obj.p = 'world';
}

Object.prototype.toString()

作用是返回一个对象的字符串形式,默认情况下返回类型字符串。常用语判断数据类型。

Object.prototype.toString.call(value)

例如:
image

封装方法:

var type = function (o){
  var s = Object.prototype.toString.call(o);
  return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};

['Null',
 'Undefined',
 'Object',
 'Array',
 'String',
 'Number',
 'Boolean',
 'Function',
 'RegExp'
].forEach(function (t) {
  type['is' + t] = function (o) {
    return type(o) === t.toLowerCase();
  };
});

type.isObject({}) // true
type.isNumber(NaN) // true
type.isRegExp(/abc/) // true

Array

reduce(),reduceRight()

reduce方法和reduceRight方法依次处理数组的每个成员,最终累计为一个值。它们的差别是,reduce是从左到右处理(从第一个成员到最后一个成员),reduceRight则是从右到左(从最后一个成员到第一个成员),其他完全一样。

image

a默认是数组的第一个元素,此时b为数组第二个元素。也可以像下面这样指定初始的a,则b初始是数组第一个元素:

image

JSON

JSON.stringify(obj, null, params)

第三个参数,用于增加返回的 JSON 字符串的可读性。如果是数字,表示每个属性前面添加的空格(最多不超过10个);如果是字符串(不超过10个字符),则该字符串会添加在每行前面。
image

参考

属性描述对象