- 对象属性:对象属性分为数据属性和访问器属性,其中数据属性包括Configurable、Enumerable、Writable、Value(前三者默认为true,Value默认为undefined),访问器属性Configurable(定义是否可以通过delete删除对象属性或者修改属性值,默认为true)、Enumerable(表示能否使用for-in循环返回属性,默认为true)、Get(undefined)和Set(undefined)。使用ES5的Object.defineProperty()方法时如果没有显示赋值configurable、enumerable及writable,则这些属性值默认均为false。
//对象属性定义
var person = {};
Object.defineProperty(person, 'job', {
configurable: true,
enumerable: true,
writable: true,
value: 'Student'
});
//判断对象属性为实例属性还是原型属性
function judgeProperty (object, property) {
if (property in object) {
if (object.hasOwnProperty(property)) {
console.log(object +' has own property -- ' + object[property]);
} else {
console.log(object + ' has prototype property -- ' + object[property]);
}
} else {
console.log(object + ' has no property -- ' + object[property]);
}
}
/* ES5的Object.keys()方法用于获取实例的所有可枚举属性 */
//原型模式
function Person () {
Person.prototype.name = 'fn';
Person.prototype.age = 24;
Person.prototype.greet = function () {
console.log('hello ' + this.name);
}
}
var person1 = new Person();
Object.defineProperties(person1, {
name: {
configurable: true,
enumerable: true,
writable: true,
value: 'hh'
},
gender: {
configurable: true,
enumerable: true,
writable: true,
value: 'male'
},
job: {
configurable: true,
enumerable: false,
writable: true,
value: 'stu'
},
interests: {
configurable: true,
enumerable: true,
writable: true,
value: 'basketball'
}
});
console.log(Object.keys(person1)); // [ 'name', 'gender', 'interests' ]
Object.keys(person1) instanceof Array // true
console.log(Object.keys(Person.prototype)); //[ 'name', 'age', 'greet' ]
/* Object.getOwnPropertyNames()获取所有的实例属性,包括不可枚举属性 */
console.log(Object.getOwnPropertyNames(person1)); //[ 'name', 'gender', 'job', 'interests' ]
ES5的Object.getOwnPropertyDescriptor()方法获取实例给定属性描述符;如果需要获取原型对象的属性描述,需要传入原型对象;
创建对象
- 工厂模式:通过函数封装及特定接口实现具体对象的抽象;
//工厂模式
function createStudent (id, name, age, grade) {
var stu = new Object();
stu.id = id;
stu.name = name;
stu.age = age;
stu.grade = grade;
stu.greet = function () {
console.log(this.name + ', your grade is ' + this.grade);
}
return stu;
}
var stu1 = createStudent(01, 'A', 18, 90);
var stu2 = createStudent(02, 'B', 19, 89);
工厂模式的问题是创建对象时均使用Object(),无法确定对象的类型;
- 构造函数模式
构造函数模式不需要返回值,不足是每个对象都具有不相等的同名方法实例;
// 构造函数模式
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
console.log(this.name);
}
}
// person1与person2中的sayName不相等
var person1 = new Person('A', 20, 'Engineer');
var person2 = new Person('B', 20, 'Engineer');
- 原型模式
原型模式的优点是原型对象的属性和方法可以被所有对象实例所共享;
原型的动态性:动态添加属性到原型中可以被在此修改前后的实例对象访问到;使用构造函数创建实例对象时会为实例同时添加一个指向最初原型的[[Prototype]]指针,而重写整个原型对象不仅会切断构造函数与最初原型的联系,也会切断现有原型与之前已存在的实例对象的联系;
//原型模式
function Person () {
Person.prototype.name = 'fn';
Person.prototype.age = 24;
Person.prototype.greet = function () {
console.log('hello ' + this.name);
}
}
// 原型模式简化
function QuickPerson () {
QuickPerson.prototype = {
constructor: QuickPerson,
name: 'Sim',
age: 24,
greet: function () {
console.log('hello' + this.name);
}
}
}
var person2 = new QuickPerson();
console.log(Object.getOwnPropertyDescriptor(QuickPerson.prototype, 'constructor'));
console.log(person1 instanceof Person); // true
console.log(person1 instanceof Object); // true
console.log(person1.constructor == Person); // true
console.log(person1.constructor == Object); //false
console.log("================================")
console.log(person2 instanceof QuickPerson); // false
console.log(person2 instanceof Object); // true
console.log(person2.constructor == QuickPerson); //true
console.log(person2.constructor == Object); //false
console.log("================================")
console.log(person1.__proto__); //Person { name: 'fn', age: 24, greet: [Function] }
console.log(person1.__proto__ == Person.prototype); //true
console.log(person2.__proto__); // QuickPerson{}
console.log(person2.__proto__ == QuickPerson.prototype); //false
console.log(QuickPerson.prototype.constructor); // [Function: QuickPerson]或[Function: Object](在重新QuickPerson.prototype时未显式定义constructor属性时)
console.log("================================")
console.log(Object.getOwnPropertyDescriptor(Person.prototype, 'constructor')); // enumerable属性默认为false
console.log("================================")
console.log(Object.getOwnPropertyDescriptor(QuickPerson.prototype, 'constructor')); // 使用字面量形式重写constructor后会将其enumerable属性修改为true;
console.log(QuickPerson.prototype);
console.log(Person.prototype);
/**
得到上述结果的原因是我们使用了字面量形式重新定义了QuickPerson.prototype;构造函数创建的同时会创建其prototype对象属性,而构造函数的prototype默认会自动创建constructor属性并指向构造函数本身;instanceof用于检测右边对象的prototype是否在左边实例的原型链上,而person2.constructor用于判断实例的构造函数;使用字面量重写QuickPerson.prototype时如果不定义constructor属性,则QuickPerson.prototype.constructor的值为[Function Object]
重新QuickPerson.prototype属性后,person2.__proto__仍然指向最初原型,而QuickPerson的prototype属性现在是new出来的新对象QuickPerson.prototype,因此person1 instanceof QuickPerson返回false,并且person2.__proto__ == QuickPerson.prototype返回false, 所以Person.__proto__与QuickPerson.__proto__返回值不同;
如果重写prototype时未定义constructor,那么QuickPerson.prototype将返回一个普通对象;{ name: 'Sim', age: 24, greet: [Function: greet] };定义了constructor时返回的是原型对象
QuickPerson {
constructor: [Function: QuickPerson],
name: 'Sim',
age: 24,
greet: [Function: greet]
}
Person { name: 'fn', age: 24, greet: [Function] }
**/
var person1 = new Person()
var person2 = new Person()
person1.greet === person2.greet; // true
// isPrototypeOf()
Person.prototype.isPrototypeOf(person1); // true
// ES5新增方法Object.getPrototypeOf()获取对象原型
Object.getPrototypeof(person1) === Person.prototype // true
- 组合构造函数模式和原型模式
这是创建自定义类型的最常见方式,使用构造函数模式定义实例属性,使用原型模式定义方法和共享属性,这样将两种模式优势互补,同时最大程度节约内存;
// 组合构造函数模式和原型模式
function Food () {
this.name = 'banana';
this.color = 'yellow';
}
Food.prototype = {
constructor: Food,
eat: function () {
console.log(this.name);
}
}
- 动态原型模式
将所有的属性与方法都封装在构造函数中,通过判断某些方法是否存在,只需初始化一次;
// 动态原型模式
function Food () {
this.name = 'banana';
this.color = 'yellow';
if (typeof this.eat != 'function') {
Food.prototype.eat = function () {
console.log(this.name);
}
}
}
- 寄生构造函数模式
该方式与工厂模式比较相似,区别是创建实例时使用new关键字并将其包装函数称作构造函数;
// 寄生构造函数模式
function Student (id, name, age, grade) {
var stu = new Object();
stu.id = id;
stu.name = name;
stu.age = age;
stu.grade = grade;
stu.greet = function () {
console.log(this.name + ', your grade is ' + this.grade);
}
return stu;
}
var stu1 = new Student(01, 'A', 18, 90);
var stu2 = new Student(02, 'B', 19, 89);
- 稳妥构造函数模式
稳妥对象指的是没有公有属性并且其方法不引用this的对象;稳妥构造函数模式中新创建对象的实例方法不引用this,不适用new关键字调用构造函数;这种模式适合在某些安全环境中使用(不允许使用this和new的环境)