1.工厂方法:没有解决对象识别问题,即怎样知道一个对象的类型
function createPerson(name, age, job) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.job = job;
obj.sayName = function() {
alert("my name:"+this.name);
};
return obj;
}
var person = createPerson("kitty", 18, "font-to-end developer");
2.构造方法
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
alert(this.name);
};
}
var person1 = new Person("liLei", 20, "doctor");
var person2 = new Person("hanmeimei", 18, "teacher");
弊端:sayName方法在要在每个实例上重新创建一遍,person1,person2这两个对象的sayName方法不是同一个实例,因为函数即是对象,故每定义一个函数,就是实例化了一个对象
改进策略:
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName() {
alert(this.name);
}
解决上面问题:包含的是指向全局函数sayName的指针,因此person1和person2对象共享了在全局作用域中定义的同一个sayName函数;
但引来其他问题:在全局作用域上定义的函数实际上只能被某个对象调用,有点名不副实。并且,如果对象需要定义很多个方法,那么,就要定义很多个全局函数,于是我们自定义的引用类型就丝毫没有封装性可言了。此策略依然不行。
3.原型模式
每创建一个函数,就会为该函数创建一个prototype属性,该属性指向函数的原型对象。所有原型对象,对会自动获得一个constructor属性,此属性指向prototype属性所在函数的指针(即开始的函数)。我们还可以继续为原型对象添加属性和方法。
例如:
function Person() {
}
Person.prototype.name = "kitty";
Person.prototype.age = 20;
Person.prototype.sex = "female";
Person.sayName = function() {
alert(this.name);
}
图解:
介绍几个和原型有关的方法:
方法一:isPrototypeOf 检测某个实例是否是某个原型的原型
var person1 = new Person();
var person2 = new Person();
alert(person1.sayName === person2.sayName); // true
Person.prototype.isPrototypeOf(person1); // true
var person3 = new Object();
Person.prototype.isPrototypeOf(person3); //false
方法二:Object.getPrototypeOf 获取某个实例的原型
Object.getPrototypeOf(person1) === Person.prototype // true
方法三: hasOwnProperty 检测某个对象的属性是否来自实例对象
person1.hasOwnProperty("name"); // false
person1.name = "marry";
person1.hasOwnProperty("name"); // true
方法四: propertyName in object :检测属性名称是否存在某个对象中,只要存在该实例对象或者原型对象中,均返回true
"name" in person1 // true
方法五:Object.keys 要取得对象上所有可枚举的实例属性,返回字符串数组
Object.keys(Person.prototype); // ["name", "age", "job", "sayName"]
var person1 = new Person();
person1.name = "kk";
person1.tt = "tt";
Object.keys(person1); // ["name", "tt"]
方法六:Object.getOwnPropertyNames 取得对象上的所有实例属性,无论它是否可枚举
Object.getOwnPropertyNames(Person.prototype) // ["constructor", "name", "age", "job", "sayName"]
说明:方法五、方法六引出的原因:
原型模式存在的缺点:
最大问题在于其共享本性所导致的:原型中所有属性是被很多实例共享的,这种共享对于函数是非常合适的,包含基本值的类型也说得过去,但是,对于包含引用类型值得属性来说,问题就比较突出。例如:
function Person() {
}
Person.prototype = {
constructor: Person,
name: "kitty",
fields: ["zhangsan", "lisi", "hanmeimei"], // 引用类型
sayName: function() {
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.fields.push("lucy");
alert(person1.fields===person2.fields); // true
故很少有人看到单独使用原型模式
4.构造函数模式 + 原型模式
思想:构造函数(可传参)模式用于定义实例属性,原型模式用于定义方法和共享的属性
function Person(name, age) {
this.name = name;
this.age = age;
this.fields = ["lily", "lucy"];
}
Person.prototype = {
constructor: Person,
sayName: function() {
alert(this.name);
}
};
var person1 = new Person("zhangsan", 10);
var person2 = new Person("lisi", 10);
person1.fields.push("hehe");
alert(person2.fields); // ["lily", "lucy"]
结果:每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用
此种模式是广受欢迎的
5.动态原型模式:
用于解决构造函数和原型分家问题
function Person(name, age) {
this.name = name;
this.age = age;
if (typeof this.sayName !== "function") {
Person.prototype.sayName = function() {
alert(this.name);
};
}
}
特点:把所有信息都封装在构造函数中,而通过在构造函数中初始化原型,又同时保证了使用构造函数和原型的优点。通过判断原型的条件,可以使sayName方法中只初始化一次。注意:此处不能给原型使用字面量的方式创建(Person.prototype = {sayName: function() {…}}),否则会切断现有实例与新原型的关系。
5.寄生构造函数模式:
function Person(name, age) {
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function() {
alert(this.name);
};
return o;
}
由于创建的对象与构造函数没有任何关系,故不能用instanceof 检测对象
此种模式能不用,则不用
6.稳妥构造函数模式:没有公共属性,而且其方法也不引用this对象
function Person(name, age) {
var o = new Object();
o.sayName = function() {
alert(name);
};
return o;
}
这样,就只有一种方式能够访问name属性,就是通过sayName方法
由于,只有创建的对象与构造函数没有任何关系,故也不能使用instanceof检测对象