理解对象
对象就是无序属性的集合, 其属性可以包含基本值, 对象或者函数。属性在创建时带有一些特征值(characteristic)。
属性类型
- 特性(attribute)描述属性(property)的各种特征,这些特性是为了实现JS引擎用的,不能直接访问,放在
[[ ]]
中。 - ECMAScript中有2种属性:数据属性和访问器属性。
数据属性
数据属性数据属性包含一个数据值的位置,这个位置可以读写。数据属性有4个特性(attribute)。
数据属性的特性(attribute) | 说明 |
---|---|
[[configurable]] | 表示是否可以重新配置该属性。包括删除属性,修改特性,改为访问器属性。 一旦定义为fasle,就不能再把它变回true了。默认为true。 |
[[enumerable]] | 表示该属性是否可枚举,默认为true。 如果原型对象和实例存在同名属性,则即使设置成false也会被for-in 枚举 |
[[writable]] | 表示是否修改这个属性的值(value),默认为true。 |
[[value]] | 存放这个属性的值,可读写,默认值为undefined。 |
Object.defineProperty()
方法用来修改属性的特性,也可以用这个方法创建属性并设置特性。
接收三个参数,即属性所在的对象,属性名 (字符串形式) ,描述符对象 (用{ }
) 。
如果调用这个方法时创建一个新属性时,不指定特性,则除[[value]]]
外的特性的默认值都为false。
var person={}
Object.defineProperty(person,"name",{
writable:false, //设置为不能修改值
value:"xiaoming",
})
person.name="dahong";
console.log(person.name) //"xiaoming"
Object.defineProperty(person,"age",{
value:20
}) //[[configurable]],[[enumerable]],[[writable]]的值都为false。
访问器属性
访问器属性不包含数据值,包含[[get]]
和[[set]]
函数,用来定义如何读取和写入数据,这2个函数是可选的。
访问器属性有4个特性,访问器属性的特性必须通过Object.defineProperty()
来定义。
访问器属性的特性(attribute) | 说明 |
---|---|
[[configurable]] | 表示是否可以重新配置该属性。包括删除属性,修改特性,改为访问器属性。 一旦定义为fasle,就不能再把它变回true了。默认为true。 |
[[enumerable]] | 表示该属性是否可枚举,默认为true。 如果原型对象和实例存在同名属性,则即使设置成false也会被for-in 枚举 |
[[get]] | get是一个函数,用来定义如何读取数据。用来如何读取并返回值。 |
[[set]] | set是一个函数,用来定义如何写入数据。用来如何处理传入的数据。 |
var book={
_year:2004, //这里的"_"只是一个记号,表示这个属性要通过对象方法访问,只是提醒作用。
//表示不要直接对"_year"操作,操作"_year"全部通过访问器属性"year"来进行。
edition:1
};
Object.defineProperty(book,"year",{ //定义访问器属性"year"
get:function(){ //定义"year"的get方式,让他用来如何读取并返回值。
return this._year; //每当访问"year"属性时,他都会执行该函数,然后返回处理后的值。
}, //*如果没有指定get,则该访问器属性不可读取!
set:function(newValue){ //定义"year"的set方式,让他用来如何处理传入的数据。
if(newValue>2004){
this._year=newValue; //每当对"year"赋值时,他会把该值传入函数,然后执行操作。
this.edition+=newValue-2004;
}
} //*如果没有指定set,则该访问器属性不可写入!
book.year=2018 //对"year"属性赋值,实际上是调用"year"的set函数,把值传递给set函数。
console.log(book.year) //访问"year"属性,实际上是调用"year"的get函数,返回函数处理后的值。
*严格模式下,对没有设置set的属性写入,或者对没有设置get的属性读取,都会抛出错误。
#####定义多个属性
Object.defineProperties()
方法用来对一个对象的多个属性同时进行定义,接收2个参数。
第1个参数是要添加和修改其属性的对象,第2个参数接收一个对象,即要修改的内容,其属性与第1个对象中的属性一一对应。
var book = {};
Object.defineProperties(book, { //对book对象的多个属性进行定义
_year: {
value: 2004
},
year: {
get: function() {
return this._year;
},
});
#####读取属性的特性
Object.getOwnPropertyDescriptor()
方法用来读取一个对象实例的某一个属性的特性,返回一个对象。
参数分别为:属性所在的对象,要读取的属性。
var book={
_year:2004,
};
var descriptor=Object.getOwnPropertyDescriptor(book,"_year");
console.log(descriptor); // {value: 2004, writable: true, enumerable: true, configurable: true}
##创建对象
- 使用Object构造函数或者字面量来创建多个对象的话,会产生大量重复的代码。
- 构造函数通常用大写字母开头,使用时,要使用
new
操作符。没有return。 - 调用构造函数时会经历4个步骤
- 创建一个新对象
- 让this指向这个新对象
- 执行构造函数里的代码
- 返回新对象
新实例里有一个constructor
属性指向构造函数,constructor
属性默认是不可枚举的。
当调用构造函数时不加new
,则是当做一般函数使用,this指向该函数本身所在的作用域(一般是window)。
原型模式
- 每创建函数,都会包含一个
prototype
属性,这是一个指针,指向原型对象。 - 使用
xxx.prototype.xxx
给原型对象添加属性,由这个原型对象的构造函数创建的实例都会共享原型函数的属性。 - 如果新实例里添加与原型对象同名的属性,则原型对象里的同名属性会被屏蔽。
- 所有的原型对象都会获得一个
constructor
,指向prototype
属性所在的构造函数。 - 原型对象最初只包含
constructor
,该属性也是共享的,可以通过对象实例访问。 - 由构造函数创建的实例包含一个指针
[[Prototype]]
(内部属性),指向原型对象,不能直接访问该属性。 - 当读取对象的某个属性时,会先在该实例搜索,找到则返回,找不到则继续在原型对象搜索。会执行2次搜索。
prototype.isPrototypeOf(object)
方法
- 用来确定某个对象的实例是否存在于另一个对象的原型链中,返回true或false。
Object.getPrototypeOf(object)
方法
- 返回对象实例的
[[Prototype]]
属性的值(返回原型对象)。
object.hasOwnProperty(“property”)
方法
- 检测一个属性是否只存在当前实例,只在给定属性存在于当前实例时才返回true
function Person(){ //空对象
};
var person1=new Person();
Person.prototype.name="Nicholas"; //为Person构造函数的原型对象添加属性
Person.prototype.constructor //指向Person
person1.hasOwnProperty("name") //false,"name"是来自原型函数。
Person.prototype.isPrototypeOf(person1) //确定person1是否在Person.prototype的原型链中。true
Object.getPrototypeOf(person1) //返回person1的原型对象
in
操作符
- 单独使用in操作符会在通过对象能够访问给定属性时返回true,无论该属性在实例中还是原型中。
- 使用in操作符
"property" in object
来判断属性。 - 使用
for-in
可以枚举属性,如果原型对象和实例存在同名属性,无论[[enumerable]]如何设置,都会被for-in 枚举。
Object.keys(object)
方法
- 接收一个对象作为参数,返回一个包含该对象所有可枚举属性的字符串数组。
- 如果传入的是对象的实例,则不会包含原型对象的属性。
function Person(){
};
Person.prototype.name="Nicholas";
var p1=new Person();
p1.age=20;
p1.gender="male";
console.log(Object.keys(p1)) //["age", "gender"]
使用更简单的语法
使用一个包含所有属性和方法的对象字面量来重写整个原型对象。
function Person(){
};
Person.prototype={
age:20,
gender:"men",
constructor:Person //重写后,constructor不再指向Person,需要重新定义。并且会变成可枚举的。
}
Object.defineProperty(Person.prototype,"constructor"{
enumerable:false, //重新将constructor设为不可枚举。
value:Person
}
)
原型对象存在的问题
原型共享可能会导致修改一个对象实例而导致另一个对象实例也被修改
function Person() {}
Person.prototype = {
constructor: Person,
friends: ["Shelby", "Court"],
};
var person1 = new Person();
var person2 = new Person();
//对person1.friends这个数组添加元素,导致person2.friends也被修改,因为这2个属性都指向同一个原型对象。
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van" //对原形修改
alert(person2.friends); //"Shelby,Court,Van"
alert(person1.friends === person2.friends); //true