工厂模式
工厂模式解决了多个对象相似的问题,没有解决对象识别的问题。
function createPerson(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function () {
alert(this.name);
}
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
构造函数模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Nicholas", 29, "Software Engineer");
通过new操作符调用构造函数,实际经历以下四个步骤
1. 创建一个新对象
2. 将构造函数作用域赋给新对象
3. 执行构造函数中的代码
4. 返回新对象
实例对象都有一个constructor属性,该属性指向person。通过构造函数标识实例对象的特定类型。
//true
alert(person1.constructor === Person);
//false
alert(person1.constructor === Object);
构造函数模式的问题
同样的方法在每个实例对象中都创建了一遍。
原型模式
function Person () {
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
alert(this.name);
};
var person1 = new Person();
person1.sayName();//Nicholas
var person2 = new Person();
person2.sayName();//Nicholas
每个函数都有一个prototype属性,这属性是一个指针,指向一个对象,这个对象是原型对象。原型对象内包含属性和方法,可以让实例对象共享这些属性和方法。
所有原型对象默认自动获得一个constructor属性指向prototype属性所在的函数。每一个用构造函数创建的实例,会包含一个指向构造函数的原型对象的指针。
Object.isPrototypeof()
Object.getPrototypeof()
当为实例对象添加属性时,这个属性会屏蔽原型对象中保存的同名属性。
使用delete
操作符可以完全删除实例属性(delete 操作符会从某个对象上移除指定属性。成功删除的时候回返回 true,否则返回 false。)
obj.hasOwnProperty(prop)
检测一个属性在实例中还是原型中
in
操作符
两种使用方式:1 单独使用 2 for-in循环中使用
单独使用
检测一个属性是否存在于对象或原型中,通过对象能访问的属性返回true。
alert(person1.hasOwnProperty("name"));//false
alert("name" in person1);//true
person1.name = "Greg";
alert(person1.name);//Greg
delete person1.name;
alert(person1.hasOwnProperty("name"));//true
alert("name" in person1);//true
alert(person1.name);//Nicholas
for-in
循环,返回所有能通过对象访问的,可枚举的属性。(属性特性[[Emunerable]为false的被实例覆盖的属性也是可枚举的])
Object.keys()
,接收一个对象,返回一个包含所有可枚举属性的数组。
person1.name = "Greg";
person1.xixi = "xixi";
52
var keys = Object.keys(Person.prototype);
console.log(keys);// Array(4) [ "name", "age", "job", "sayName" ]
//传入实例对象,只返回实例对象的属性的数组
var keys = Object.keys(person1);
console.log(keys);// Array [ "name", "xixi" ] [ "name" ]
更简单的原型语法
function Person() { }
Person.prototype = {
constructor: Person,
name: "xixi"
}
var keys = Object.keys(Person.prototype);
//原生constructor属性是不可枚举的,这里重设constructor导致[[Enumerable]]被设为true
console.log(keys);// Array [ "constructor", "name" ]
//通过Object.defineProperty把[[Enumerable]]属性设回为false
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});
var keys = Object.keys(Person.prototype);
console.log(keys);// Array [ "name" ]
原型的动态性
function Person() { }
var p1 = new Person();
//重写prototype,Person.prototype指向新的原型对象
Person.prototype = {
constructor: Person,
age: 18
};
//p1实例在重写原型对象前创建,p1的prototype属性指向原来的Person的原型对象,原来的原型对象中不包含age属性
console.log(p1.age);//undefined
var p2 = new Person();
console.log(p2.age);//18
原型对象的问题
function Person() { }
Person.prototype = {
constructor: Person,
age: 18,
friends: ["f1", "f2", "f3"]
}
var p1 = new Person();
var p2 = new Person();
p1.friends.push('f4');
//原型中的属性都是被共享的,friends属性为引用类型,对引用类型的操作会反映到所有实例,这不是所希望的
console.log(p1.friends);//Array(4) [ "f1", "f2", "f3", "f4" ]
//
console.log(p2.friends);//Array(4) [ "f1", "f2", "f3", "f4" ]
组合使用构造模式和原型模式
构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性
function Person(name, age, friends) {
this.name = name;
this.age = age;
this.friends = friends;
}
Person.prototype = {
constructor: Person,
sayName: function () {
console.log(this.name);
}
};
var p1 = new Person("Jack", 18, ["f1", "f2", "f3"]);
var p2 = new Person("Tom", 30, ["f1", "f2", "f3"]);
p1.friends.push("p4");
p1.sayName();//Jack
p2.sayName();//Tom
console.log(p1.friends);//Array(4) [ "f1", "f2", "f3", "p4" ]
console.log(p2.friends);//Array(3) [ "f1", "f2", "f3" ]
动态原型模式
把所有信息封装在构造函数中
function Person(name, age, job) {
//属性
this.name = name;
this.age = age;
this.job = job;
this.friends = ["f1", "f2"];
//方法
if (typeof this.sayName != "function") {//避免每次调用构造函数时给prototype重复添加属性,只在第一次调用时执行,本身prototype的属性共享于所有实例,不需要每次都添加
Person.prototype.sayName = function () {
alert(this.name);
}
Person.prototype.sayHello = function () {
alert("Hello");
}
}
}
var person = new Person("Jack", 29, "student");
person.sayName();