Skip to content

Latest commit

 

History

History
388 lines (295 loc) · 9.1 KB

07对象.md

File metadata and controls

388 lines (295 loc) · 9.1 KB

对象

如何创建一个对象?

想必很多朋友面试的时候碰到过这问题

// 声明形式
var obj = {
  key: 'value';
}
// 绝大多数内置对象也用这个形式来创建对象
// 构造形式
var obj = new Object();

obj.key = 'value';
// 很少使用这种形式来创建对象
// 只能逐个添加key/value

上述两种形式生成的对象是一样的

// 构造函数形式
function Fun(){
  this.key = 'value';
};

var obj = new Fun();
// 连接原型链形式 -- 后面的原型链文章会详细讲,这里先了解
var obj = Object.create(null); // 创建一个对象,这个对象的[[prototype]]关联到null,返回这个对象给obj

obj.key = 'value';

如何获取对象中的值?

对象有两种取值方式

  • 属性访问:通过.操作符来取值。
  • 键访问:通过[]来取值

对象中,属性名永远是字符串,其它值作为属性名,会被转换为字符串

两种取值方式的区别:

  • .的方式,属性名需要满足标识符命名规范(obj.ke-y就不符合规范)
  • [] 可以接受任意UTF-8/Unicode字符串作为属性名

属性名可以是变量吗?

使用表达式来设置属性名,这种形式称为可计算属性名

let lastName = 'Wang'

var obj = {
  ['erGou' + lastName]: '男',
  ['xiaoHua' + lastName]: '女'}

let gender = obj['erGou' + lastName];
tips:如果添加的属性名”看起来“像一个数字,他会变成下标

属性描述符是个啥?

属性有writableconfigurableenumerable三个描述符,可以通过Object.defineProperty()方法来修改描述符的值

可以通过Object.getOwnPropertyDescriptor(obj, "key");来获取属性的描述符信息

writable 是否可读

决定是否可以修改属性值。

非严格模式下修改属性值不报错,但修改失败。
严格模式下修改属性值报错,抛出TypeError。

var obj = {
  num1: 1
};

Object.defineProperty(obj, 'num1', {
  writable: false
});

obj.num1 = 2;
console.log(obj.num1); // 1

configurable 是否可配置

决定是否可以使用Object.defineProperty()方法来修改属性描述符。

  • configurable的值为false,修改描述符,会产生一个TypeError错误
  • configurable的值为false的属性无法删除
  • configurable修改为false是单向操作,无法再改回true

例外:(嗯,就很无语....) 即便configurable的值为false,还可以把writabletrue改为false,但不能从false改为true

var obj = {
  num1: 1
};

Object.defineProperty(obj, 'num1', {
  configurable: false
});

// 情况1 修改configurable值
Object.defineProperty(obj, 'num1', {
  configurable: true
}); // Uncaught TypeError: Cannot redefine property: num1

// 情况2 删除configurable值为false的属性
delete obj.num1; // false 不会报错,但无法删除

// 情况3 例外的情况,可以修改writable为false但无法修改回来
Object.defineProperty(obj, 'num1', {
  writable: false
}); // 可以修改成功

Object.defineProperty(obj, 'num1', {
  writable: true
}); // Uncaught TypeError: Cannot redefine property: num1

结合writable: falseconfigurable: false可以创建一个真正的常量属性(不可配置、删除、修改)。

enumerable 是否可枚举

控制属性是否会出现在枚举中,值为false不会出现在枚举中,但仍然可以正常访问

var obj = {
  num1: 1,
  num2: 2,
  num3: 3
};

Object.defineProperty(obj, 'num1', {
  enumerable: false
});

for(key in obj) {
  console.log(key);
} // num2、num3

如何判断一个属性是否可枚举呢?
使用Object.propertyIsEnumerable(key) ,返回true/false。只检查对象,不检查原型链

批量修改属性描述符的方法

Object.preventExtensions() 禁止扩展新属性

禁止对象添加新属性
可以删除已有属性

非严格模式下添加新属性,静默失败。
严格模式下添加新属性,抛出TypeError错误。

Object.seal() 密封

禁止对象添加新属性
不能配置描述符(即把所有属性标记为configurable: false

效果:

  • 不能添加新属性
  • 不能配置描述符
  • 不能删除现有属性
  • 能修改属性值

Object.freeze() 冻结

禁止对象添加新属性
不能配置描述符(即把所有属性标记为configurable: false
不能修改属性值(即把所有属性标记为writable: false

效果:

  • 不能添加新属性
  • 不能配置描述符
  • 不能删除现有属性
  • 不能修改属性值

:value为引用类型的值,不受上述影响

Getter和Setter又是啥?

getter/setter是一个隐藏函数(有默认值),在我们取/存属性值时自动调用。(我们也可以改写getter/setter

设置getter/setter的方法

  • 可以直接在对象中写get/set方法,设置gettersetter
  • 也可以使用Object.defineProperty()来修改默认的gettersetter但只能应用在单个属性上
// 设置getter/setter的 方法1
var obj = {
  get num1() { // 设置getter
    return this._num1_;
  },
  set num1(num) { // 设置setter
    this._num1_ = num;
  }
}

obj.num1 = 2;

console.log(obj.num1); // 2
// 设置getter/setter的 方法2
var obj = {
  num1: 1
}

Object.defineProperty(obj, 'num2', {
  get: function() {
    return this._num2_;
  },
  set: function(num) {
    this._num2_ = num;
  },
  enumerable: true, // 在这种情况下创建新属性,enumerable默认为false
  configurable: true // 在这种情况下创建新属性,configurable默认为false
  // 在这种情况下创建新属性,没有writable属性,但是可写
});

obj.num2 = 2;

console.log(obj.num2); // 2

若只设置了getter并返回一个固定值,那么set操作会被忽略,即使设置了setter。 因为getter返回固定值,setter没有意义。 所以一般来讲,gettersetter是成对出现的。

如何判断一个属性在对象中是否存在?

这个问题是不是又很面熟...

判断一个属性是否在,有两种方法inhasOwnProperty,即使属性的enumerablefasle,也能被找到

in

会检查属性是否在对象[[Property]]

var obj = {
  num1: 1
};

obj.__proto__.num2 = 2; // 把num2挂载到原型链
// 一般使用Object.creat()来挂载

console.log('num1' in obj); // true
console.log('num2' in obj); // true

hasOwnProperty

只检查属性是否在对象,不检查[[Property]]

var obj = {
  num1: 1
};

obj.__proto__.num2 = 2; // 把num2挂载到原型链
// 一般使用Object.creat()来挂载

console.log(obj.hasOwnProperty('num1')); // true
console.log(obj.hasOwnProperty('num2')); // false

如何获取对象的全部属性?

有两种姿势,Object.keys(obj)Object.getOwnPropertyNames(obj)(他们都不检查原型链)。

Object.keys(obj)

返回所有可枚举属性的数组,只检查对象,不检查原型链

var obj = {
  num1: 1,
  num2: 2,
  num3: 3
};

obj.__proto__.num0 = 0;

Object.defineProperty(obj, 'num3', {
  enumerable: false
});

console.log(Object.keys(obj)); // [num1, num2]
// 一般开发使用此方法

Object.getOwnPropertyNames(obj)

返回所有属性的数组,只检查对象,不检查原型链

var obj = {
  num1: 1,
  num2: 2,
  num3: 3
};

obj.__proto__.num0 = 0;

Object.defineProperty(obj, 'num3', {
  enumerable: false
});

console.log(Object.keys(obj)); // [num1, num2, num3]

如何遍历对象?

for ...in

遍历所有可枚举属性(包括原型链)。
数组按照下标顺序遍历,遍历拿到的是下标
对象按照随机顺序遍历,遍历拿到的是key(属性名)。

// 数组 for...in
var arr = [1, 2, 3];

for(idx in arr) {
  console.log(idx);
} // 0 1 2
// 对象 for...in
var obj = {
  num1: 1,
  num2: 2
};

obj.__proto__.num3 = 3;

for(key in obj) {
  console.log(key);
} // num1 num2 num3

for ...of

遍历需要迭代器
数组自带迭代器,可直接遍历,遍历拿到的是值
对象需要自己实现迭代器来进行迭代。

// 数组 for...of
var arr = [1, 2, 3];

for(val of arr) {
  console.log(val);
} // 1 2 3
// 对象 for...of
var obj = {
  num1: 1,
  num2: 2,
  num3: 3
};

// 实现一个迭代器
Object.defineProperty(obj, Symbol.iterator, {
  enumerable: false,
  writable: false,
  configurable: true,
  value: function() {
    var idx = 0;
    var keys = Object.keys(this);
    return {
      next: () => {
        return {
          value: this[keys[idx++]],
          done: (idx > keys.length)
        };
      }
    };
  }
});

for(val of obj) {
  console.log(val);
} // 1 2 3