啃一啃犀牛书--对象(2)

啃一啃犀牛书–对象(2)

属性的getter和setter

定义如下:

在ECMAscript5中,属性值可以用一个或两个方法替代,这两个方法就是getter和setter。
当程序查询存取器属性的值时,js调用getter方法(无参数)。这个方法的返回值就是属性存取表达式的值。
js调用setter方法,将赋值表达式右侧的值当做参数传入setter。

存取器定义为一个或两个和属性同名的函数,这个函数定义没有使用function关键字,而是使用get或set。

1
2
3
4
5
6
7
8
9
10
11
12
var p = {
x: 1.0,
y: 1.0,
get r() {return Math.sqrt(this.x*this.x + this.y*this.y)},
set r(newvalue) {
var oldvalue = Math.sqrt(this.x*this.x + this.y*this.y);
var radio = newvalue/oudvalue;
this.x *= radio;
this.y *= radio;
},
get theta() {return Math.atan2(this.y, this.x);}
}

属性的特性

属性的特性是它的值(value),可写性(writable),可枚举性(enumerable),和可配置性(configurable)。

存取器属性不具有值特性和可写性,它们的可写性是有setter方法存在与否决定。

我们通过Object.getOwnPropertyDescriptor()方法获取某个对象特定的属性描述符。

1
2
3
Object.getOwnPropertyDescriptor({x:1},'x');
//返回结果
//{value: 1, writable: true, enumerable: true, configurable: true}

对象的三个属性

原型属性

对象的原型属性是用来继承属性的,这个属性如此重要,以至于我们经常把”o的原型属性”直接叫做”o的原型”。

原型属性在实例对象创建之初就设置好的。通过对象直接量创建的对象使用Object.prototype作为它们的原型。通过new创建的对象
使用构造函数的prototype属性作为它们的原型。通过Object.create()方法创建的对象,第一个参数就是它的原型。

isPrototypeOf()方法来检测一个对象是否是另一个对象的原型。(与instanceof运算符类似。)

1
2
3
4
var p = {x:1};
var o = Object.create(p);
p.isPrototypeOf(o) //true
Object.prototype.isPrototypeOf(o) //true
类属性

对象的类属性是一个字符串,用以表示对象的类型信息。在ECMAscript3和ECMAscript5都未提供设置这个属性的方法,并只有一种间接的方法可以查询它。

我们通过对象本身的toString()方法。返回如下格式的字符串;

1
[object class]

因此要想获得对象的类,可以调用对象的toString()方法,然后提取已返回的字符串的第八个到倒数第二个位置之间的字符。
但是好多对象继承的toString()方法被重写了,为了能调用正确的toString()版本,必须间接的调用Function.call()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function classof(o){
if(o===null) return "Null";
if(o===undefined) return "Undefined";
return Object.prototype.toString.call(o).slice(8,-1);
}
classof(null); // => "Null"
classof(1); // => "Number"
classof(""); // => "String"
classof(false); // => "Boolean"
classof({}); // => "Object"
classof([]); // => "Array"
classof(/./); // => "Regexp"
classof(new Date()); // => "Date"
classof(window); // => "window"
function f(){}; //
classof(new f()) // => "Object"

可扩展性

对象的可扩展性用以表示是否可以给对象添加新属性。所有的内置对象和自定义对象都是显式可扩展的,宿主对象的
可扩展性是有js引擎定义的。

通过将对象传入Object.esExtensible(),来判断对象是否是可扩展的。如果想将对象转换为不可扩展的,需要调用
Object.preventExtensions(),将待转换的对象作为参数传入。但是,一旦将对象转换为不可扩展的,就无法再将其转换回可扩展的了。
这里其实可以参考typescript,在定义对象的时候,可以指定属性,如下

1
2
3
4
5
6
7
8
9
interface Person{
name: string;
age: number;
}
let yxy: Person = {
name: 'yxy',
age: 27
}
//如果中间少属性或者多属性都是会编译报错的,这里可以说是接口的定义。

序列化对象

对象序列化是指将对象的状态转换为字符串,也可以将字符串还原为对象。

JSON.stringify() ——- 序列化

JSON.parse() ——还原js对象

对象的方法

toString()

toString()方法没有参数,它将返回一个表示这个方法的对象的字符串。
由于默认的toString()并不会输出很多有用的信息,因此很多类都带有自定义的toString()方法。
比如Array.toString(),Date.toStrnig()。

toLocaleString()

除了基本的toString()方法,对象都包含toLocaleString()方法,这个方法返回一个表示这个对象的本地化字符串。
Object中默认的toLocaleString()方法并不做任何本地化自身的操作,它仅调用toString()方法并返回对应值。

Date和Number类对toLocaleString()方法做了定制,可以用它对数字,日期和实践做本地化的转换。
下面以示例展示:

1
2
3
4
var a = new Date();             //   Fri Jul 28 2017 16:06:23 GMT+0800 (CST)
var b = a.toLocaleString(); // 2017/7/28 下午4:06:23
var c = 123456789;
var d = c.toLocaleString('zh-Hans-CN-u-nu-hanidec'); //"一二三,四五六,七八九"

toJSON()

Object.prototype实际上没有定义toJSON()方法,但是对于需要执行序列化的对象来说,JSON.stringify()方法会调用toJSON()方法。如果
在待序列化的对象中存在这个方法,则调用它,返回值即序列化的结果,而不是原始的对象。

1
2
var a = new Date();
console.log(a.toJSON()); //2017-07-28T08:16:31.527Z

valueOf()

valueOf方法和toString()方法非常类似,但往往当js需要将对象转换为某种原始值而非字符串的时候才会调用它,尤其是转换为数字的时候。如果在需要使用原始值的上下文中使用了对象,js就会自动
调用这个方法。默认的valueOf()方法不足为奇,但有些内置类自定义了valueOf()方法。

1
2
3
var a =  new Date();
console.log(a.valueOf()); // 1501230002800
//在这里,Date.valueOf方法输入的结果与Date.parse()方法输出结果一直。