构造函数
function Persion(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
}
}
var person1 = new Person('Joke',29,"Teacher");
var person2 = new Person('Greg',39,"Doctor");
上例中 person1,person2都是构造函数Person的实例。
构造函数其实与一般函数并没有什么不同,不过一般默认构造函数的首字母大写。它与一般函数的唯一区别就是通过new操作符来调用,其实任何函数只要通过new操作符来调用,那么他就可以作为构造函数。上面的Person可以通过下面任何一种方式调用
1.当做构造函数调用
var person1 = new Person('Joke',29,"Teacher");
2.作为普通函数调用
Person('Joke','30','Doctor');
window.sayName();
3.在另一个对象的作用域中调用
var o = new Object();
Persion.call(0,'Joke',29,"Teacher");
o.sayName();
构造函数的问题
构造函数模式虽然好用,但是也有问题。使用构造函数的主要问题就是每个方法都要在每个实例上重新创建一遍。在上面的例子中person1,person2都有sayName方法,但它们两个的方法并不是同一个方法。这明显是没有必要的。
function Persion(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
console.log(this.name);
}
这样可以解决每个实例都有一个做相同事情的方法,但是又有了新的问题,sayName是在全局定义的,所以它可以在任何地方调用,但我们的本意是要只是对象调用。
原型模式
我们创建的每一个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面意思来理解,那么prototype就是通过调用构造函数而创建的对象实例的原型对象。原型的好处是可以让所有实例共享它所包含的属性和方法。
function Person() {}
Person.prototype.name = 'Nice';
Person.prototype.age = 20;
Person.prototype.job = 'Doctor';
Person.prototype.sayName = function() {
console.log(this.name)
}
// 也可以这样写
// Person.prototype = {
// name:'Nice',
// age:20,
// job:'Doctor',
// sayName:function(){
// console.log(this.name)
// }
// }
var p1 = new Person();
p1.sayName();
//p1.sayJob();//p1.sayJob is not a function
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype 属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性包含一个指向prototype属性所在函数的指针。就拿前面的例子来说。
console.log(Person.prototype.constructor) //ƒ Person(){}
Person.prototype.sayJob = function() {
console.log(this.job);
}
var p2 = new Person();
p2.job = '老师';
p2.sayJob();
原型的问题
原型模式也不是没有缺点,首先,它身略了为构造函数传递初始化参数的环节,结果所有的实例默认情况下都有相同的属性值。但这不是最大的问题,最大的问题还是共享的本质导致的。原型中的所有属性都是共享的,这种共享对函数时非常适合的,对于属性也可以通过在实例中添加同名属性覆盖原型属性。但是对于包含引用类型值的属性来说,问题就比较突出了。
function Person() {}
Person.prototype = {
name: 'Nice',
age: 20,
job: 'Doctor',
friends:['张胜男','李时'],
sayName: function() {
console.log(this.name)
}
}
var p1 = new Person();
var p2 = new Person();
p1.friends.push('p1Friend');
// 由于js 引用类型的特质
console.log(p2.friends)//["张胜男", "李时", "p1Friend"]
组合使用构造函数和原型模式
//组合使用构造函数和原型模式
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ['李三','王二'];
}
Person.prototype = {
constructor:Person,
sayName:function(){
console.log(this.name)
}
}
var p1 = new Person('流氓',20,'盲流');
var p2 = new Person('方子',23,'盲流');
p1.friends.push('p1的新朋友');
console.log(p1.friends)//["李三", "王二", "p1的新朋友"]
console.log(p2.friends)// ["李三", "王二"]