Fork me on GitHub

自测练习之深入理解对象(2)


Object.prototype.toString方法

JavaScript对象原型的关系是一种树形结构,整个树形结构的根部就是Object.prototype。Object.prototype提供了一些可以在所有对象中使用的方法。比如说,toString方法可以将一个对象转换成其字符串表示形式,即返回反映这个对象的字符串。
自测练习4

1
2
3
function foo(obj){
return Object.prototype.toString.call(obj).slice(8,-1)
}//请说明函数foo的作用

对obj这个对象调用Object.prototype.toString,返回对象obj的具体类型(一对方括号,方括号中间是单词“object”和类型的名称),再对返回的[object ….]调用slice方法,切去前面的object和最后的’]’剩下一个代表obj的类型名称的字符串。
foo的作用:检测参数的对象类型,返回代表类型名的字符串。
比如:

1
2
3
4
5
6
7
foo("jerry"); //"String"
foo(123); //"Number"
foo(true); //"Boolean"
foo([1,2,3]); //"Array"
foo({}); //"Object"
foo(null); //"Null"
foo(); //"Undefined"

PS:
obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样,这是因为toString为Object的原型方法,而Array ,function等类型作为Object的实例,都重写(覆盖)了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的覆盖之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串(与调用join(“,”)的结果非常相似,即在数组的每个值之间插入一个逗号)…..),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object上原型toString方法。

我们可以验证一下,将数组的toString方法删除,看看会是什么结果:

1
2
3
4
5
6
var arr=[1,2,3];
console.log(Array.prototype.hasOwnProperty("toString"));//true
console.log(arr.toString());//1,2,3
delete Array.prototype.toString;//delete操作符删除实例属性
console.log(Array.prototype.hasOwnProperty("toString"));//false
console.log(arr.toString());//"[object Array]"

删除了Array的toString方法后,同样再采用arr.toString()方法调用时,不再有屏蔽Object原型方法的实例方法,因此沿着原型链,arr最后调用了Object的toString方法,返回了和Object.prototype.toString.call(arr)相同的结果。


Object.defineProperty 方法

定义对象可以使用构造函数或字面量的形式:

1
2
3
var obj = new Object; //obj = {}
obj.name = "张三" //添加描述
obj.say = function(){}://添加行为

除了以上添加属性的方法,还可以使用Object.defineProperty定义新属性或修改原有的属性。

语法:

1
Object.defineProperty(obj, prop, descriptor)

参数说明:
obj:必需。目标对象
prop:必需。需要定义或修改属性的名字
descriptor:必需。目标属性所拥有的特性
返回值:
传入函数的对象。即第一个参数obj

数据描述
当修改或定义对象的某个属性的时候,给这个属性添加一些特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var obj = {
test:"hello"
}
//对象已有的属性添加特性描述
Object.defineProperty(obj,"test",{
configurable:true | false,
enumerable:true | false,
value:任意类型的值,
writable:true | false
});
//对象新添加的属性的特性描述
Object.defineProperty(obj,"newKey",{
configurable:true | false,
enumerable:true | false,
value:任意类型的值,
writable:true | false
});

数据描述中的属性都是可选的,来看一下设置每一个属性的作用。

value
属性对应的值,可以使任意类型的值,默认为undefined

writable
属性的值是否可以被重写。设置为true可以被重写;设置为false。不能被重写,默认值为false。

enumerable
此属性的值是否可以被枚举(使用for…in或Object.keys())。设置为true可以被枚举;设置为false,不能被枚举。默认为false。

configurable
是否可以删除目标属性或者是否可以再次修改属性的特性(writable,configurable,enumerable)。设置为true可以被删除或可以重新设置特性;设置为false,属性想再改过来都不行。

回到开头,第一种给对象添加属性的方法,添加的属性是可删除、可枚举、可重写的。

第二种使用Object.definePropety给对象添加属性,如果不设置属性的特性,那么默认情况下,添加的属性是不可删除、不可枚举、不可重写的。
具体Object.defineProperty 方法参考

自测练习5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var a = {}
a.bar = 2
Object.defineProperty(a, "foo",
{ value: "hi"});
//为a添加名为foo的属性,值为"hi",没有设置属性的特性,所以这个属性是不可枚举、修改和重写的
console.log(delete a.foo)
//foo属性不可以被删除,故删除失败,输出false
console.log(delete a.bar)
//a属性是直接添加的,可以删除,输出true
a.foo = "world"
console.log(a.foo)
//foo属性是不可重写的,值不变,还是输出"hi"
for (var key in a){
console.log(key);
}//foo属性不可枚举,bar属性又被删除了,没有可以枚举的属性了,不输出
console.log("foo" in a);
//foo虽然不可枚举,但是还是存在对象a之中的,输出true
console.log("bar" in a);
//bar属性已经被删除,不在对象a之中,输出false
//请问console.log的输出?
undefined