理解对象
简单来说,对象是键值对的集合。
属性类型:数据属性,访问器属性。
-
数据属性:Configurable,Enumerable,Writable,Value
configurable:是否能通过delete删除该属性,对象上定义的属性默认为true.
enumerable:是否可通过for-in枚举,对象上定义的属性默认为true.
writable:是否可修改,对象上定义的属性默认为true。
value:这个属性的值,默认undefined。
修改属性默认的特性:Object.defineProperty(对象,属性,描述符对象)Object.defineProperty(person, 'name', { writable: false, value: 'Li' })
注意:将configurable配置为false之后,不能delete它,也不能修改 它,并且不能将它在改为true了。在调用object.defineProperty()方法,如果不指定configurable、writable、enumerable的值,默认为false。
-
访问器属性:getter,setter(非必须)
configurable: 同数据属性。
enumerable: 同数据属性。
get:在读取访问器属性的时候会调用这个函数。
set:在写入访问器属性的时候会调用这个函数。var book = { _year:2004, edition: 1 } Object.defineProperty(book, "year", { get: function() { return this._year }, set: function(val) { if (val > 2004) { this._year = val; this.edition += val - 2004 } } })
注:_year前面的下划线是一种常用的记号,表示只能通过对象方法访问的属性
Object.defineProperties(book, 对应属性的描述符对象)
Object.getOwnPropertyDescriptor(book, "_year"),该方法只能用于实例属性
创建对象
创建单个对象: 对象字面量/构造函数
创建多个对象
-
工厂模式
function createPerson(name, age) { var o = new Object(); o.name = name; o.age = age; o.sayName = function() { alert(this.name) } return o } var person1 = createPerson('Li', 23); var person2 = createPerson('Di', 13);
优点:可以快速的创建多个相似的对象;
缺点:无法知道一个对象的类型(对象识别)。 -
自定义构造函数
function Person(name, age) { this.name = name; this.age = age; this.sayName = function() { alert(this.name) } } var person1 = new Person('Li', 23); var person2 = new Person('Di', 13);
new操作符
a. 创建一个新对象;
b. 设置原型链(将新对象的__proto__
指向函数的prototype) ;
c. 将构造函数的作用域赋给新对象(绑定this);
d. 执行构造函数的代码(为新对象添加属性);
e. 返回新对象。可以用instanceof检测person1和person2的类型
person1 instanceof Person => true
person1 instanceof Object => true优点:对象类型识别
缺点:每个对象都有单独的sayName函数实例,这个不必要。 -
原型模式
function Person() { } Person.prototype.name = 'Li'; Person.prototype.age = 31; Person.prototype.sayName = function() { alert(this.name) } var person1 = new Person(); var person2 = new Person();
person1和person2共享所有的属性和方法。
关于prototype和
__proto
Person.prototype.constructor 指向 Person
person1.__proto__ 指向 Person.prototype
Person.prototype.isPrototypeOf(person1) 指向 true
Object.getPrototypeOf(person1) == Person.prototype
Object.getPrototypeOf(person1).name 值为:"Li"
读取对象的某个属性的时候,先在对象实例本身读取,如果没有找到,则去原型对象上去找,如果还没有找到,则沿着原型链继续往上找。
可以通过person1.hasOwnProperty("age")来检测该属性是否是实例上的属性。
操作符in: `alert("name" in person1) 不管是实例属性还是原型上的属性都返回true.
in配合hasOwnProperty()可以检测属性是否是原型属性:function hasProtoProperty(object, name) { return !object.hasOwnProperty(name) && (name in object) }
for-in:返回所有可枚举的属性(enumerable为true),包括实例上的以及原型对象上的。属性没有顺序。如果属性值为undefined或是null会抛出错误或不执行,所以建议先判断。
Object.keys(person1)获取对象上所有可枚举的实例属性。返回数组。
Object.getOwnPropertyNames(person1)获取对象上的所有实例属性,不论是否可以枚举。返回数组。调用构造函数会为实例添加一个指向最初原型(prototype)的指针(
__proto__
),如果在调用构造函数之后重新定义prototype,则会切断这个指针,即person.proto不再指向Person.prototype了。可以在原生对象(String/Number/Array/……)的原型上添加方法,但是不推荐。
缺点:所有实例共享属性和方法,属性值不够灵活(当属性值为引用值的时候)
-
组合使用自定义构造函数和原型模式
通过自定义构造函数定义实例属性,原型模式定义共享的属性和方法function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayName = function() { alert(this.name) } var person1 = new Person("Li", 12); var person2 = new Person("Di", 24);
-
动态原型模式
function Person(name, age) { this.name = name; this.age = age; // 一下这段代码只在初次调用构造函数的时候执行 if(typeof this.sayName != "function") { Person.prototype.sayName = function() { alert(this.name) } } } var person1 = new Person("Li", 12); var person2 = new Person("Di", 24);
-
寄生构造函数模式
类似工厂模式,建议在特殊情况下使用。
function Person(name, age) {
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function() {
alert(this.name)
}
return o
}
var person1 = new Person("Li", 12);
var person2 = new Person("Di", 24);
-
稳妥构造函数模式
稳妥对象:没有公共属性,方法也不引用this对象。在安全的环境中使用。function Person(name, age){ var o = new Object(); o.sayName = function() { alert(name) } return o }
网友评论