JavaScript面向对象的程序设计——“对象的基本概念”的注意要点 - 前端学习笔记
对象的基本概念
面向对象(Object-Oriented,OO),的语言最大的特征就是它们都有类的概念,通过类可以创建任意多个具有相同属性和方法的对象。
创建自定义对象最简单的方式就是创建一个Object 的实例,然后再给他添加属性和方法。如:
var math = new Object();math.π = function pi(){ return Math.PI;};document.write(math.π());
这里借用了Math.PI,最后的结果是无论调用的是math.π
还是Math.PI
都会返回圆周率的值。其中,前者就是我们自己自定义的对象。又如:
var person = new Object();person.name = "Oliver";person.age = 18;person.job = "Software Engineer";person.sayName = function(){ alert(this.name);};
上面这个对象可以用下面的结构化图像解释清楚:
Object => person |-name|-age |-job |-sayName()|-this.name
这里面person 是对象,又是Object 的实例,其中他有三个属性和一个方法,这个方法用于显示this.name,会被解析为person.name。
上面的例子又可以用字面量的语法写成这样:
var person = { name: "Oliver", age: 18, job: "Software Engineer", sayName: function(){ alert(this.name); }};
属性类型
根据ECMA-262 第五版,ECMAScript 中有两种属性(内部值):数据属性和访问器属性。为了表示特性的内部值,该规范把它们放在了两对方括号中,如[[Enumerable]]。
数据属性
数据属性有4 个描述其行为的特性:
[[Configurable]]:表示能否通过delete 删除属性;
[[Enumerable]]:表示能否通过for-in 循环返回属性;
[[Writable]]:表示能否修改属性的值;
[[Valu]]:包含这个属性的数据值;
其中,前三个默认都是true,最后一个默认为undefined。
如何修改数据属性默认的特性呢?这就要用到ECMAScript 5 的Object.defineProperty()
方法。这个方法接收三个参数:属性所在的对象,属性的名字和一个描述符对象。描述符对象就是上面提到的4 个描述其行为的特性:configurable、enumerable、writable 和value。
修改方式如下:
var person = {};Object.defineProperty(person, "name", { writable: false, value: "Oliver"});console.log(person.name); //Oliverperson.name = "Niko";console.log(person.name); //Niko
又如:
var person = { name: "Troy", age: 18};Object.defineProperty(person, "name", { writable: false, value: "Oliver", enumerable: false});for (x in person){ console.log(x); //age}
如:
var person = { name: "Troy", age: 18};Object.defineProperty(person, "name", { value: "Oliver", configurable: false});delete person.name;console.log(person.name) //Oliver
要注意的是,一旦把属性定义为不可配置的,就不能再把它变回可配置的了。
不要在IE8 中使用Object.defineProperty()
方法。
访问器属性
该属性不包含数据值,只有一对getter 和setter 函数(两者为可选);4个 特性如下:
[[Configurable]]
[[Enumerable]]
[[Get]]:在读取属性时调用的函数;默认undefined;
[[Set]]:在写入属性时调用的函数;默认undefined;
同样,必须使用Object.defindProperty()
来定义。如:
var book = { _year: 2004, edition: 1};Object.defineProperty(book, "year", { get: function(){ return this._year; }, set: function(newValue){ if(newValue > 2004){this._year = newValue;this.edition += newValue - 2004; } }});book.year = 2006;console.log(book.edition);
两个默认的属性:_year 和edition。year 前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。
对于上面的下划线不太明白的,可以参考下面一种写法:
var book = { year: 2004, edition: 1};Object.defineProperty(book, "setYear", { get: function(){ return this.year; }, set: function(newValue){ if(newValue > 2004){this.year = newValue;this.edition += newValue - 2004; } }});book.setYear = 2007;console.log(book.edition);console.log(book.year)
两者功能相同,实际上该方法是给book 对象又设置了另一个属性即第一段代码中的_year 和第二段代码中的setYear。
另外,一般在这个方法之前,要创建访问器属性,一般都是使用两个非标准的方法:__defineGetter__()
和__defineSetter__()
。如:
var book = { name: "hello"};book.__defineGetter__("name",function(){ return "world";});document.write(book.name);
定义多个属性
ECMAScript 5 又定义了一个Object.defineProperties()
方法。利用这个方法可以通过描述符一次定义多个属性。如:
var book = {};Object.defineProperties(book, { _year:{ value: 2004, writable: true }, edition:{ value: 1, writable: true }, year:{ get: function(){return this._year; }, set: function(newValue){if(newValue > 2004){ this._year = newValue; this.edition += newValue - 2004;} } }});book.year = 2006;console.log(book.year + " " + book.edition);
还真是奇葩,不知是否浏览器的问题,数据属性还是要加上为true 的writable 访问器属性才能起作用。不是默认为true 么。真是奇怪
读取属性的特征
使用ECMAScript 5 的Object.getOwnPropertyDescriptor()
方法,可以取得给定属性的描述符。接收两个参数:所在的对象和要读取其描述符的属性名称。如:
var cars = {};Object.defineProperties(cars, { name: { value: "Benz", writable: false }, _color: { value: "White", writable: true }, price: { value: 300000, writable: true }, color: { get: function(){return this._color; }, set: function(newValue){if(newValue == "Black"){ this._color = "Black"; this.price = 280000}else if(newValue == "White"){ this._color = "White"; this.price = 300000} } }});function selectColors(colorValue){ cars.color = colorValue; document.write(cars.name + ": " + cars.color + "款 " + "(" + cars.price + "元人民币" + ")");}selectColors("Black"); //Benz: Black款 (280000元人民币)selectColors("White"); //Benz: White款 (300000元人民币)var descriptor = Object.getOwnPropertyDescriptor(cars, "name");document.write(descriptor.value + descriptor.writable + descriptor.configurable + descriptor.enumerable); //Benzfalsefalsefalsevar descriptor2 = Object.getOwnPropertyDescriptor(cars, "color");document.write(descriptor2.get + descriptor2.set + descriptor2.configurable + descriptor2.enumerable + descriptor2.value); //function () { return this._color; }function (newValue) { if(newValue == "Black"){ this._color = "Black"; this.price = 280000 }else if(newValue == "White"){ this._color = "White"; this.price = 300000 } }falsefalseundefined