Lucius's Blog

JavaScript之四 - 对象

对象中包含一系列属性,这些属性是无序的。每个属性都有一个字符串key和对应的value。(无序,key为字符串)

1、对象结构

拥有[[proto]]、[[class]](属于哪个类)、[[extensible]](是否允许添加新的属性)、writable、enumerable、configurable、value、get/set

2、new/原型链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function foo() {}
foo.prototype.z = 3;

var obj = new foo();
obj.y = 2;
obj.x = 1;

obj.x; // 1
obj.y; // 2
obj.z; // 3

typeof obj.toString; // "function"
"z" in obj; // true
obj.hasOwnProperty("z"); //false

解析:

  • var obj = new foo(); obj的[[proto]]则会指向foo.prototype, 而foo的prototype则会指向Object.prototype,Object.prototype则会指向null

  • foo.prototype.z = 3; 在foo.prototype添加一个属性z

  • obj.z 会先从obj查找是否有z这个属性,然后再查找obj的[[proto]],即foo.prototype

  • obj.toString 该方法是在Object.prototype中,即Object.prototype.toString();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
obj.z = 5;

obj.hasOwnProperty("z"); // true
foo.prototype.z; // 3
obj.z; // 5

obj.z = undefined;
obj.z; // undefined

delete obj.z; // true
obj.z; // 3

delete obj.z; // true
obj.z; // 3

解析:

  • obj.z = 5; 赋值不会因为obj没有z属性而继续往prototype找是否有这个属性,从而修改prototype的属性,什么意思呢?就是obj没有z属性,则只会在obj添加新的z属性,反而foo.prototype.z的值不会被修改。

  • obj.hasOwnProperty("z"); foo.prototype.z; obj.z; z的属性是添加到obj上的,foo.prototype.z是不会受到影响的,依旧保留。

  • obj.z = undefined; obj.z; foo.prototype.z; 同理

  • delete obj.z; obj.z; delete obj.z; obj.z; delete 删除的只是obj的属性,而不会影响foo.prototype的属性,但当obj的z被删除后,obj.z查找步骤又跟之前一样会到foo.prototype原型链去查找。

3、Object.create

1
2
3
4
5
6
7
var obj = Object.create({x: 1});
obj.x; // 1
typeof obj.toString; // "function"
obj.hasOwnProperty("x"); // false

var obj = Object.create(null);
obj.toString; // undefined

解析:

  • var obj = Object.create({x:1}); 利用Object.create,则会创建一个新的对象,其原型链则指向参数。即obj -> {x:1} -> Object.prototype -> null

  • obj.x; // 1 调用原型链上的参数

4、属性操作

4.1 属性读写

1
2
3
var obj = {x:1};
obj.x; // 读
obj.x = 2; // 写

4.2 异常

1
2
3
4
var obj = {x:1};
obj.y; // undefined
var yx = obj.y.z; // TypeError : Cannot read property "z" of undefined
obj.y.z = 2; // TypeError : Cannot set property "z" of undefined,假如 obj.y = 1; obj.y.z=2; 则不会报错

4.3 删除

1
2
3
delete Object.prototype; // false, 这是因为Object.prototype的configurable为false
var descriptor = Object.getOwnPropertyDescriptor(Object, "prototype");
descriptor.configurable; // false,这样就无法delete Object.prototype

总结:

  • 是无法删除基本类型的,例如:var a = 1; delete a; // false

  • 是无法删除函数声明的,例如:function func(){}; delete func; // false

  • 可以删除隐式的全局变量,例如:a = 1; window.a; // 1 delete a; //true

  • 可以删除eval定义变量,例如:eval("var x = 1"); delete x; //true

4.4 检测

  • "x" in obj; 不仅仅会在该对象找,还会沿原型链上找

  • obj.hasOwnProperty("x") 只会在该对象找,不会到沿原型链上找

  • obj.propertyIsEnumeratable 是否可枚举

  • Object.defineProperty(obj, propertyName, propertyArgsObject);

1
2
3
Object.defineProperty(obj, "x", {enumeratable: false, value: 1000});
obj.propertyIsEnumerable("x"); // false
obj.hasOwnProperty("x"); // true

5、getter/setter

用法:

1
2
3
4
5
6
7
8
9
10
11
var obj = {
_x:1,

get x() {
return _x;
},

set x(val) {
_x = val;
}
};

栗子:

1
2
3
4
5
6
7
8
9
10
11
12
function foo(){}
Object.defineProperty(foo.prototype, "z", {get: function(){return 1;}});

var obj = new foo();
obj.z; // 1
obj.z = 10;
obj.z; // 1

Object.defineProperty(obj, "z", {value: 100, configurable: true});
obj.z; // 100
delete obj.z; // true
obj.z; // 1

解析:

  • Object.defineProperty(foo.prototype, "z", {get: function(){return 1;}}); 给foo的原型链创建属性z,并给其属性声明get方法

  • obj.z 调用原型链z

  • obj.z = 10 由于原型链有getter方法,表明该属性在原型链上只读不可写,同时也不会给原型链上创建新属性

  • obj.z 依旧调用原型链getter方法

  • Object.defineProperty(obj, "z", {value: 100, configurable: true}); 在obj上创建新属性z,并设置configurable为true,值为100

  • obj.z // 100 调用obj本身的属性z

  • delete obj.z 因为configurable为true,因此可以删除

  • obj.z obj上的z被删除了,所以会重新从原型链上查找z属性

1
2
3
var o = {};
Object.defineProperty(o, "x", {value: 1}); // writable = false, configurable = false;
var obj = Object.create(o);

解析: 没啥解释

6、属性标签

1
2
3
4
5
6
7
8
9
// Object.getOwnPropertyDescriptor(obj, propertyName);

var person = {};
Object.defineProperty(person, "name", {configurable: false, writable: false, enumerable: true, value: "Test"});
Object.keys(person); // ["name"]
person.name; // Test;
person.name = "Test2";
person.name; // Test
delete person.name; // false

解析:

-Object.keys(person) 获取该对象所有可枚举的属性

  • Object.getOwnPropertyDescriptor(obj, propertyName) 获取对象描述器,第一个参数是获取描述的对象,第二个参数是该对象的属性,若不存在则返回undefined,反之则返回该对象的属性配置

  • Object.defineProperty(person, "name", {configurable: false, writable: false, enumerable: true, value: "Test"}) 定义属性的配置,第一个参数是对象,第二个属性是不存在于该对象的属性,第三个则定义该对象的属性描述器对象

c(configurable)/w(writable) c:true/w:true c:true/w:false c:false/w:true c:false/w:false
修改属性的值 ×
通过属性赋值、修改属性的值 × ×
delete该属性返回true × ×
修改getter/setter方法 × ×
修改属性标签(除了writable从true修改为false总是允许) × ×

注意:假如我们想改属性的值,configurable与writable为false的话就没办法修改了,但是假如writable为false,而configurable为true的话,我们可以变相的通过configurable修改属性的值,如Object.defineProperty(xxx, x, {value: 1})或者通过configurable来修改属性的writable的值使其能被修改,如Object.defineProperty(xxx, x, {writable: true});

7、对象标签

对象标签有三种:[[proto]]、[[class]]、[[extensible]]

  • [[proto]]: 原型对象

  • [[class]]: 表明该对象是哪种类型的类

  • [[extensible]]: 对象是否能再被添加新的属性,如AS的dynamic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var obj = {x: 1, y: 2};
Object.isExtensible(obj); // true
Object.preventExtensions(obj);
Object.isExtensible(obj); // false
obj.z = 1;
obj.z; // undefined,添加失败
Object.getOwnPropertyDescriptor(obj, "x");
// Object {value: 1, writable: true, enumerable: true, configurable: true}

Object.seal(obj);
Object.getOwnPropertyDescriptor(obj, "x");
// Object {value: 1, writable: true, enumerable: true, configurable: false}
Object.isSeal(obj); // true

Object.freeze(obj);
Object.getOwnPropertyDescriptor(obj, "x");
// Object {value: 1, writable: false, enumerable: true, configurable: false}
Object.isFrozen(obj);

解析:

  • Object.preventExtensions(obj) 阻止obj扩展新属性,但是不会影响到旧的属性

  • Object.seal(obj) 设置configurable为false

  • Object.freeze(obj) 设置configurable跟writable为false

注意:以上的方法只会限制当前对象,并不会影响到原型链对象,若想影响原型链的对象,就得对原型链对象做同样的操作

8、序列化

8.1

1
2
3
4
5
6
7
8
var obj = {x: 1, y: true, z: [1, 2, 3], nullVal: null};
JSON.stringify(obj); // "{"x":1,"y":true,"z":[1,2,3],"nullVal":null}"

obj = {val: undefined, a: NaN, b: Infinity, c: new Date()};
JSON.stringify(obj); // "{"a":null,"b":null,"c":"2016-08-10T15:45:03.197Z"}"

obj = JSON.parse("{'x': 1}");
obj.x; // 1

总结:

  • JSON.stringify(obj); 序列化

  • JSON.parse("{'x': 1}") 反序列化,注意属性是以””引起来

  • undefined 的属性不会被序列化出来

  • NaN或Infinity 则会被序列化成null

  • new Date() 则会序列化成UTC时间格式

8.2 自定义

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
x: 1,
y: 2,
z: {
o1: 1,
o2: 2,
toJSON: function() {
return this.o1 + this.o2;
}
}
};

JSON.stringify(obj); // "{"x":1,"y":2,"z":3}"

总结:自定义z属性,其中toJSON为固定写法,若不是toJSON则为"{"x":1,"y":2,"z":{"o1":1,"o2":2}}"

我只是试下能不能被赞赏😳