function Person(name, age, sex){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sex = sex;
obj.sayHi = function(){
console.log('hi, I am ' + this.name);
}
return obj;
}
var p1 = Person('zcl', 24, 'male');
返回一个对象,但是无法识别具体的对象
function Person(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
this.sayHi = function(){
console.log('hi, I am ' + this.name);
}
return obj;
}
var p2 = new Person('zcl', 24, 'male');
与工厂模式的不同之处在于
关于constructor(构造函数)属性,它是用来识别对象类型的
p1.constructor == Object; //true p2.constructor == Person; //true p2.constructor == Object; //false
但是推荐使用instanceof操作符
console.log(p1 instanceof Object);// true console.log(p2 instanceof Person);// true console.log(p3 instanceof Object);// true
构造函数模式的缺点
构造函数的问题是每个方法都要在每个实例上重新创建一遍,这极大的浪费了内存资源,也就是说每创建一个Person
对象都要在内存中新开辟一定的内存空间。
可以使用以下方式来解决
function Person(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
this.sayHi = sayHi;
return obj;
}
// 在全局作用域中创建一个函数
function sayHi(){
console.log('hi, I am ' + this.name);
}
var p2 = new Person('zcl', 24, 'male');
但是上述的解决方式会污染全局作用域,所以有了第三种方式
function Person(){
}
Person.prototype.name = 'zcl';
Person.prototype.age = '24';
Person.prototype.sayHi = function(){
console.log('hi, I am ' + this.name);
}
var p3 = new Person('zcl', 24, 'male');
var p4 = new Person('zcl', 24, 'male');
console.log(p3.sayHi == p4.sayHi);// true
[[Prototype]]的
isPrototypeOf
方法和Object.getPrototypeOf
方法console.log(Person.prototype.isPrototypeOf(p3));// true console.log(Object.getPrototypeOf(p3) == Person.prototype); //true
当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性
p3.name = 'zyt';
console.log(p3.name); // zyt
hasOwnProperty()
方法function Person(){ } Person.prototype.name = 'zcl'; Person.prototype.age = '24'; Person.prototype.sayHi = function(){ console.log('hi, I am ' + this.name); } var p3 = new Person(); var p4 = new Person(); console.log(p3.hasOwnProperty('name'));//false p3.name = 'zyt'; console.log(p3.hasOwnProperty('name'));//true
function Person(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.sayHi = function(){
console.log('hi, I am ' + this.name);
}
var p5 = new Person('zcl', 24, 'male');
function Person(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
if(typeof this.sayHi != 'function'){
Person.prototype.sayHi = function(){
console.log('hi, I am ' + this.name);
}
}
}
var p6 = new Person('zcl', 24, 'male')
有点类似工厂模式+构造函数模式
function Perosn(name, age, sex){
var o = new Object();
o.name = name;
o.age = age;
o.sex = sex;
o.sayHi = function(){
console.log('hi, I am ' + this.name);
}
return o;
}
var p7 = new Person('zcl', 24, 'male');
function Perosn(name, age, sex){
var o = new Object();
o.sayHi = function(){
console.log('hi, I am ' + name);
}
return o;
}
var p7 = new Person('zcl', 24, 'male');
除了调用sayHi
方法之外没有别的方法能访问p7
的数据成员
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property
}
function SubType(){
this.subproperty = false;
}
SubType.prototype = new SuperType(); // 核心
SubType.prototype.getSubValue = function(){
return this.subproperty;
}
var instance = new SubTpe();
console.log(instance.getSuperValue()); // true
原型链继承的缺点*
function Father(){
this.colors = ['red', 'blue'];
}
function Son(){
}
Son.prototype = new Father();
var son1 = new Son();
var son2 = new Son();
son1.colors.push('green');
console.log(son1.colors);//"red,blue,green"
console.log(son2.colors);//"red,blue,green"
原型链中包含引用类型的属性时,某一个子类实例对该属性的修改所有子类实例共享
在创建子类实例时,无法向父类的构造函数传参,或者说换一种方法,即使能够传参,也会影响所有的子类实例
别名:伪造对象或经典继承
function Father(){
this.colors = ['red', 'blue'];
}
function Son(){
Father.call(this);
}
var son1 = new Son();
var son2 = new Son();
son1.colors.push('green');
console.log(son1.colors);//"red,blue,green"
console.log(son2.colors);//"red,blue"
需要注意的是,使用这种模式创建的子类不是父类的实例
console.log(son1 instanceof Father); // false console.log(son1 instanceof Son); // true
借用构造函数模式的缺点
别名:伪经典继承
原型链继承+借用构造函数继承
function Father(name){
this.name = name;
this.colors = ['red', 'blue'];
}
Father.prototype.sayHi = function(){
console.log('hi, I am ' + this.name);
}
function Son(name, age){
Father.call(this, name);
this.age = age;
}
Son.prototype = new Father();
Son.prototype.constructor = Son;
var son1 = new Son('zcl', 24);
son1.colors.push('green');
console.log(son1.colors); // "red, blue, green"
var son2 = new Son('wly', 26);
console.log(son2.colors); // "red, blue"
console.log(son1 instanceof Son); // true
console.log(son1 instanceof Father); // true
组合继承的缺点
function object(o){
function F(){}
F.prototype = o;
return new F();
}
本质上讲,产生的新对象是传入的对象o
的一个实例副本,即做了一次浅复制
var person = {
name:'zcl',
friends:['wly', 'txf', 'zyt']
}
var p1 = object(person);
p1.name = 'zcl1';
p1.friends.push('lt');
console.log(p1.name); // zcl1
var p2 = object(person);
p2.name = 'zcl2';
console.log(p1.name); // zcl1
console.log(person.name); // zcl
console.log(person.friends); // "wly, txf, zyt, lt"
注意
p1中原本没有name属性,在定义p1时,name属性只存在p1的原型对象中,但是一旦给p1.name属性赋值,就会覆盖原型属性,而不会直接修改原型属性对象,也就不会修改person对象中name属性的值,但是friends属性不一样,会直接修改原型对象中的friends属性,也就是person的friends属性
借用了原型式继承,创建一个仅由于封装继承过程的函数
思路与寄生式构造模式与工厂模式类似
function createAnother(o){
var clone = object(o);
clone.sayHi = function(){
console.log('hi, I am ' + this.name);
}
return clone;
}
寄生式继承缺点
function inheritPrototype(son, father){
var prototype = object(father.prototype);
prototype.constructor = son;
son.prototype = prototype;
}
function Father(name){
this.name = name;
this.colors = ['red', 'blue'];
}
Father.prototype.sayHi = function(){
console.log('hi, I am ' + this.name);
}
function Son(name, age){
Father.call(this, name);
this.age = age;
}
inheritPrototype(Son, Father);
Son.prototype.sayAge = function(){
console.log(this.age);
}
[参考资料] 《JS高级程序设计》