参考资料:
- 深入理解JavaScript系列(5):强大的原型和原型链
- JavaScript new关键字
- javascript技术难点(三)之this、new、apply和call详解
- [面向对象的程序设计]javascript高级程序设计(第三版)
- JavaScript秘密花园
疑问
{object Object}
创建对象
-
Object
构造函数 - 对象字面量
创建属性
定义单一属性
- 数据属性(默认可以直接创建或者使用
Object.defineProperty()
- 访问器属性(必须使用
Object.defineProperty()
)
数据属性
- 数据属性包含一个数据值的位置,在这个位置进行读取和写入值
- 数据属性包含多个描述其行为的特性(
Configurable
,Enumerable
,Writable
,Vlaue
)
修改属性的默认特性
Object.defineProperty()
(ECMAScript5)- 参数:接受三个参数,属性所在的对象,属性的名字,一个描述符对象,如不指定描述符对象,则除
value
外都为false
var person = {};
Object.defineProperty(person,"name",{
writable: false,
value: "haoxiang"
});
alert(person.name); // haoxiang
person.name = "haoqi";
alert(person.name); // haoxiang
访问器属性
- 访问器属性不包含数据值,这类属性包含一对函数(
getter
,setter
) - 在读取访问器属性时,会调用
getter
,写入时,会调用setter
([[Configurable]]
,[[Enumerable]]
,[[get]]
,[[set]]
) - 访问器的属性不可以直接进行定义,必须使用
Object.defineProperty()
定义
var book = {
_year : 2015,
edition: 1
}; // 定义了两个数据属性,其中的`_`用来标记只能通过对象方法访问到的属性
Object.defineProperty(book,"mouseyear",{
get: function(){
return "read";
},
set: function(){
alert("write");
}
});
alert(book.mouseyear);//read,读取属性会调用`get`函数
book.mouseyear = 200;//write,写入属性会调用`set`函数
来个复杂些的,书上说这个是使用访问器属性的常用方式,主要的特点是设置一个属性的值,会影响其他属性的变化,先提前留个印象
var book = {
_year : 2004,
edition: 1
}; //定义`book`对象,其中包含`_year`,`edition`数据属性
Object.defineProperty(book, "year",{
get :function () {
return this._year;
},
set :function (newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
});
//接着为`book`对象创建`year`访问器属性,访问器属性是没有`[[Value]]`特性的,它的值就是依靠get,set函数读取和写入
console.log(book.year);
//2004,这里我们使用`book.year`读取这个访问器属性,这个访问器属性会使用它的`get`函数,`return this._year`
book.year = 2005;
//为对象的访问器属性写入新值,这个值看起来作为了`set`函数的参数,这个传参过程让人有些看不懂,后续我会跟进
console.log(book._year);
//2005,由于之前使用了访问器属性的`set`函数,这个函数内部会把`this._year = newValue`,所以这里输出正常,是2005
alert(book.edition);
//2,由于之前使用了访问器属性的`set`函数,这个函数内部会把`this.edition += newValue - 2004;,所以这里输出正常,`edition`是2
定义多个属性
Object.defineProperties()
参数:接受两个对象参数,第一个对象为要添加或者修改属性的对象,第二个对象为需要在第一个对象中添加的属性
读取属性的特性
Object.getOwnPropertyDescriptor()
参数:接受两个参数,第一参数为属性所在的对象,第二个参数为要读取特性的属性
返回值:有返回值,返回值为一个对象,包含当前属性的所有特性
看代码
var num = {num1 : 1};// 定义一个对象,包含数据属性`num1`
var res = Object.getOwnPropertyDescriptor(num,"num1");// 使用这个方法,结果是包含属性所有特性的对象,放在res中
for( d in res){
console.log(d);
}
//value
//writable
//enumerable
//configurable
//枚举属性的所有特性,也间接说明了如果字面量创造对象,包含的属性为数据属性,访问器属性必须通过方法来创建
{% endhighlight %}
### 构造函数模式
> - 创建已知函数,利用this和函数本身传参的特性
> - 之后使用new操作符
> - 通过构造函数模式创造的对象,会有一个`constructor属性`指向构造函数
#### 过程
```javascript
function Person (name,age) {
this.name = name;
this.age = age;
}
var person1 = new Person("hao","22");
console.log(person1);
//Person {name: "hao", age: "22"}
new操作符的大致过程
- 创建一个新对象
- 将构造函数的作用域给新对象,(因此this就指向新对象)
- 执行构造函数内的代码(为新对象添加属性)
- 返回新对象
我大致模拟了这个过程,不一定可以保证必然是正确的,如果大家发现我的想法是错的,可以直接留言
function Person (name,age,job) {
this.name = name;
this.age = age;
this.job = job;
}
var person1 = {}; //创建了一个新对象
person1.Person = Person;
console.log(person1.Person);
//将新对象的一个属性作为变量,将函数的指针(也就是函数名,函数的地址)赋给属性person,因此person1.person指向堆内存中的函数对象
person1.Person("hao",22,"student");//执行这个函数进行赋值
console.log(person1);//Object {name: "hao", age: 22, job: "student"}
下面这种的可能性貌似大些:
function Person (name,age,job) {
this.name = name;
this.age = age;
this.job = job;
}
var o = {
};
Person.call(o,"hao",22,"student");
console.log(o);
//Object {name: "keneth", age: 22, job: "studenet"}
原型模式
- 函数的prototype属性和原型对象的constructor属性: 函数内部会有prototype属性,这个属性指向保存着所有实例共享的属性和方法的对象,叫做函数的原型对象,在默认情况下,所有的
原型对象都会获得constructor属性,这个属性包含一个指向prototype属性所在函数的指针。