创建普通对象
我们知道,创建对象可以这样
var person = new Object();
person.name = “Nicholas”;
person.age = 29;
person.job = “Software Engineer”;
person.sayName = function(){ alert(this.name);};
这个对象有name, age, job属性,还有一个sayName()的方法。这是大家常用的创建对象的方法,可以不断添加新属性,但是如果要创建多个类似的对象的话,就需要不断地重复这段代码,耗费很多内存。
工厂模式
这时候可以用工厂模式来解决这个问题
function createPerson(name, age, job){
var o = new Object(); //1.创建一个空对象并赋值给变量o
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o; //2.返回变量o
}
var person1 = createPerson(“Nicholas”, 29, “Software Engineer”);
var person2 = createPerson(“Greg”, 27, “Doctor”);
工厂模式还可以更加简练,比如可以创建一个语法糖,省略掉上一段代码的1和2 这时候便涉及到了Constructor模式了
Constructor模式
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(“Greg”, 27, “Doctor”);
注意constructor总是以大写开头,这是它的书写规范。 相比工厂模式,当我们用new这个关键字的时候,它帮我们写了一下步骤:
- 创建一个新对象
- 将这个对象的__proto__属性绑定到原型上面
- 将这个新对象赋给当前上下文(也就是this)
- 自动返回this 使用contructor模式还有一个好处,可以帮你判断类型
alert(person1 instanceof Person); //true
同样的,contructor模式也存在一个问题,在上一段代码中,我们添加了一个sayName()
的方法
this.sayName = function(){
alert(this.name);
};
如果需要创建100个不同的person, 也需要创建100个sayName()
函数.
alert(person1.sayName == person2.sayName); //false
但是这个函数代码都是一样的,实际上只需要创建一次即可。
function createPerson(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
};
var person1 = createPerson('Nicholas', 29, 'Software Engineer');
var person2 = createPerson('Greg', 27, 'Doctor');
如果要创建很多不同的对象,就必须创建很多不同的全局方法,很容易产生冲突,这时候另一种方式就大显身手了(原型)
prototype
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”
alert(person1.sayName == person2.sayName); //true
每个函数创建的时候,它的prototype属性也会被创建,指向一个普通的对象,这个对象会有一个constructor属性,指回这个函数,上面那段代码中
Person.prototype.constrctor
会重新指向Person。而每次创建一个新的实例,实例的__proto__属性会指向这个ProtoType
person1.sayName()
的时候,它本身是没有这个方法的,所以会自动到person1.__proto__
去找,如果还没有,就再上一层,去person1.__proto__.__proto__
找,直到找到或者返回null为止。
读取person1
的其他属性也是一样的,如果对象本身没有这个属性,会到它的___proto__去找,也就是Person.prototype
,如果有的话,就返回这个值,没有的话,会再到Person.prototype.__proto__
找,由于Person.prototype
是个普通的对象实例,所以Person.prototype.__proto__
指向的是Object.prototype
判断一个属性是否属于自身的对象还是对象的原型,可以用hasOwnProperty()
判断
原型同样可以用下列代码的方式创建,但是会覆盖掉原来默认的Prototype, 也就没有默认的constructor属性。
function Person(){ }
Person.prototype = {
name : “Nicholas”,
age : 29,
job : “Software Engineer”,
sayName : function () {
alert(this.name); }
};
下面这段代码也会运行错误,原因是当当实例被创建的时候,它的__proto__已经指向了默认的Person.prototype,这个时候你将Person.prototype指向另一个地址,实例的__proto__是没办法访问到的
function Person(){ }
var person = new Person();
Person.prototype = {
constructor: Person,
name : “Nicholas”,
age : 29,
job : “Software Engineer”,
sayName : function () {
alert(this.name); }
};
person.sayName(); //error
__proto__和prototype有什么区别呢,__proto__是在新的实例对象生成的时候,自动绑定到构造函数的prototype上面的,所有的对象都有__proto__属性。prototype则是在创建函数的时候,它的prototype属性也会被创建,指向一个普通的对象,这个对象会有一个constructor属性,指回这个函数。prototype只有函数才有的属性。
常见的创建对象的方式为下列代码,这样避免了共享对象的冲突
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = [“Shelby”, “Court”];
}
Person.prototype = {
constructor: Person,
sayName : function () {
alert(this.name);
}
};
var person1 = new Person(“Nicholas”, 29, “Software Engineer”);
var person2 = new Person(“Greg”, 27, “Doctor”);
person1.friends.push(“Van”);
alert(person1.friends); //”Shelby,Court,Van”
alert(person2.friends); //”Shelby,Court”
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true
原型继承
- 构造函数继承
缺点:每次创建一个子实例,父方法会重复构建多次,并且不会继承父类的prototype
function SuperType(){
this.property = true;
this.run = function() {}
}
function SubType(){
SuperType.call(this)
}
var instance = new SubType();
- 类式继承
缺点:一个子类的实例更改子类原型从父类构造函数中继承来的共有属性就会直接影响到其他子类
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
通过SubType.prototype = new SuperType();
完成了原型的继承。为什么要用SuperType
的实例来绑定SubType
的prototype
呢,因为这个实例的__proto__属性会指向SuperType.prototype
, 通过这个方式来继承SuperType
的属性和方法
toString()
和 valueOf()
等函数,也可以直接调用,因为这些函数已经在Object里面定义好了
如果要添加SubType的方法可以
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//new method
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
//override existing method
SubType.prototype.getSuperValue = function (){
return false;
};
// 下面的方式是错的,SubType.prototype被重新赋值,不能够再访问父函数的方法了
<!--SubType.prototype = {-->
<!-- getSubValue: function (){-->
<!-- return this.subproperty;-->
<!-- }-->
<!--}-->
var instance = new SubType();
alert(instance.getSuperValue()); //false
原型链存在的问题
function SuperType(){
this.colors = [“red”, “blue”, “green”];
}
function SubType(){ }
//inherit from SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType(); instance1.colors.push(“black”);
alert(instance1.colors); //”red,blue,green,black”
var instance2 = new SubType();
alert(instance2.colors); //”red,blue,green,black”
解决办法
function SuperType(){
this.colors = [“red”, “blue”, “green”];
}
function SubType(){ //inherit from SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push(“black”);
alert(instance1.colors); //”red,blue,green,black”
var instance2 = new SubType();
alert(instance2.colors); //”red,blue,green”
但是上述方法也存在子对象无法引用父对象prototype中的方法等问题
- 组合继承
function SuperType(name){
this.name = name;
this.colors = [“red”, “blue”, “green”];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
//inherit properties
SuperType.call(this, name);
this.age = age;
}
//inherit methods
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType(“Nicholas”, 29); instance1.colors.push(“black”);
alert(instance1.colors); //”red,blue,green,black”
instance1.sayName(); //”Nicholas”;
instance1.sayAge(); //29
var instance2 = new SubType(“Greg”, 27); alert(instance2.colors); //”red,blue,green”
instance2.sayName(); //”Greg”;
instance2.sayAge(); //27
上述方法会调用SuperType的constructor方法两次
- 优化
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name){
this.name = name;
this.colors = [“red”, “blue”, “green”];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
};
或者
// Shape - 父类(superclass)
function Shape() {
this.x = 0;
this.y = 0;
}
// 父类的方法
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// Rectangle - 子类(subclass)
function Rectangle() {
Shape.call(this); // call super constructor.
}
// 子类续承父类
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
console.log('Is rect an instance of Rectangle?',
rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?',
rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
使用Object.create()是将对象继承到__proto__属性上