一、对象属性类型
1、数据属性:数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有 4 个描述其行为的特性。
[[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true。
[[Enumerable]]:表示能否通过 for-in 循环返回属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true。
[[Writable]]:表示能否修改属性的值。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true。
[[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为 undefined。
要修改属性默认的特性,必须使用 ECMAScript 5 的 Object.defineProperty()方法。这个方法 接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属 性必须是:configurable、enumerable、writable 和 value。设置其中的一或多个值,可以修改 对应的特性值。
一旦把属性定义为不可配置的(configurable:false), 就不能再把它变回可配置了。此时,再调用 Object.defineProperty()方法修改除 writable 之外 的特性,
var a = 0, window.a的configurable默认为false;
window.a = 0, window.a的configurable默认为true;
2. 访问器属性
[[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特 性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为 true。
[[Enumerable]]:表示能否通过 for-in 循环返回属性。对于直接在对象上定义的属性,这 5 个特性的默认值为 true。
[[Get]]:在读取属性时调用的函数。默认值为 undefined。
[[Set]]:在写入属性时调用的函数。默认值为 undefined。
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 = 2005; alert(book.edition); //2
支持 ECMAScript 5 的这个方法的浏览器有 IE9+(IE8 只是部分实现)、Firefox 4+、Safari 5+、Opera 12+和 Chrome。在这个方法之前,要创建访问器属性,一般都使用两个非标准的方法: __defineGetter__()和__defineSetter__()。这两个方法最初是由 Firefox 引入的,后来 Safari 3、 Chrome 1 和 Opera 9.5 也给出了相同的实现。使用这两个遗留的方法,可以像下面这样重写前面的例子。
var book = {
_year: 2004,
edition: 1
};
//定义访问器的旧有方法
book.__defineGetter__("year", function(){
return this._year;
});
book.__defineSetter__("year", function(newValue){
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
});
book.year = 2005; alert(book.edition); //2
在 不 支 持 Object.defineProperty() 方 法 的 浏 览 器 中 不 能 修 改 [[Configurable]] 和 [[Enumerable]]。
3、读取属性的特性
ECMAScript 5 的 Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述 符。这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果 是访问器属性,这个对象的属性有 configurable、enumerable、get 和 set;如果是数据属性,这 个对象的属性有 configurable、enumerable、writable 和 value。
二、创建对象
关于创建对象的几种模式我就不多赘述了,总体的思想是将公共方法和属性放在构造函数的原型中,在这我主要列出ES6及ES6之前的写法。
ES6之前:
function Person (name) {
this.name = name
}
Person.prototype.sayHi = function(){
return 'sayHi';
}
var person = new Person('Tom')
console.log(person.name) // Tom
console.log(person.sayHi()) // sayHi
ES6: class Person {
constructor(name){
this.name = name;
}
sayHi(){
return 'sayHi';
}
}
let person = new Person('Tom')
console.log(person.name) // Tom
console.log(person.sayHi()) // sayHi
三、继承
实现继承最好的就是寄生组合继承方法了,ES6的extends也是基于此来实现继承的;以下是2种写法:
ES6之前:
function SuperType (name) {
this.name = name
}
SuperType.prototype.sayHi = function(){
return this.name + 'sayHi';
}
function SubType (name,age) {
SuperType.call(this,name);
this.age = age
}
function inheritPrototype(subType, superType){
var prototype = Object.create(superType.prototype); // ES5 支持 Object.create()
prototype.constructor = subType;
subType.prototype = prototype;
}
inheritPrototype(SubType,SuperType);
var subType = new SubType('Tom')
console.log(subType.name) // Tom
console.log(subType.sayHi()) // TomsayHi
ES6: class SuperType {
constructor(name){
this.name = name;
}
sayHi(){
return `${this.name}sayHi`;
}
}
class SubType extends SuperType {
constructor(name,age){
super(name)
this.age = age;
}
}
let subType = new SubType('Tom')
console.log(subType.name) // Tom
console.log(subType.sayHi()) // TomsayHi
子类必须在constructor方法中调用super方法,否则新建实例时会报错。如果子类在constructor方法中使用了this初始化实例属性,调用super方法必须放到this初始化实例属性的前面。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。