構(gòu)造函數(shù)
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都是構(gòu)造函數(shù)Person的實(shí)例。
構(gòu)造函數(shù)其實(shí)與一般函數(shù)并沒有什么不同,不過一般默認(rèn)構(gòu)造函數(shù)的首字母大寫。它與一般函數(shù)的唯一區(qū)別就是通過new操作符來調(diào)用,其實(shí)任何函數(shù)只要通過new操作符來調(diào)用,那么他就可以作為構(gòu)造函數(shù)。上面的Person可以通過下面任何一種方式調(diào)用
1.當(dāng)做構(gòu)造函數(shù)調(diào)用
var person1 = new Person('Joke',29,"Teacher");
2.作為普通函數(shù)調(diào)用
Person('Joke','30','Doctor');
window.sayName();
3.在另一個(gè)對(duì)象的作用域中調(diào)用
var o = new Object();
Persion.call(0,'Joke',29,"Teacher");
o.sayName();
構(gòu)造函數(shù)的問題
構(gòu)造函數(shù)模式雖然好用,但是也有問題。使用構(gòu)造函數(shù)的主要問題就是每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍。在上面的例子中person1,person2都有sayName方法,但它們兩個(gè)的方法并不是同一個(gè)方法。這明顯是沒有必要的。
function Persion(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
console.log(this.name);
}
這樣可以解決每個(gè)實(shí)例都有一個(gè)做相同事情的方法,但是又有了新的問題,sayName是在全局定義的,所以它可以在任何地方調(diào)用,但我們的本意是要只是對(duì)象調(diào)用。
原型模式
我們創(chuàng)建的每一個(gè)函數(shù)都有一個(gè)prototype屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象,而這個(gè)對(duì)象的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法。如果按照字面意思來理解,那么prototype就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的對(duì)象實(shí)例的原型對(duì)象。原型的好處是可以讓所有實(shí)例共享它所包含的屬性和方法。
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
無論什么時(shí)候,只要?jiǎng)?chuàng)建了一個(gè)新函數(shù),就會(huì)根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個(gè)prototype 屬性,這個(gè)屬性指向函數(shù)的原型對(duì)象。在默認(rèn)情況下,所有原型對(duì)象都會(huì)自動(dòng)獲得一個(gè)constructor屬性,這個(gè)屬性包含一個(gè)指向prototype屬性所在函數(shù)的指針。就拿前面的例子來說。
console.log(Person.prototype.constructor) //? Person(){}
Person.prototype.sayJob = function() {
console.log(this.job);
}
var p2 = new Person();
p2.job = '老師';
p2.sayJob();
原型的問題
原型模式也不是沒有缺點(diǎn),首先,它身略了為構(gòu)造函數(shù)傳遞初始化參數(shù)的環(huán)節(jié),結(jié)果所有的實(shí)例默認(rèn)情況下都有相同的屬性值。但這不是最大的問題,最大的問題還是共享的本質(zhì)導(dǎo)致的。原型中的所有屬性都是共享的,這種共享對(duì)函數(shù)時(shí)非常適合的,對(duì)于屬性也可以通過在實(shí)例中添加同名屬性覆蓋原型屬性。但是對(duì)于包含引用類型值的屬性來說,問題就比較突出了。
function Person() {}
Person.prototype = {
name: 'Nice',
age: 20,
job: 'Doctor',
friends:['張勝男','李時(shí)'],
sayName: function() {
console.log(this.name)
}
}
var p1 = new Person();
var p2 = new Person();
p1.friends.push('p1Friend');
// 由于js 引用類型的特質(zhì)
console.log(p2.friends)//["張勝男", "李時(shí)", "p1Friend"]
組合使用構(gòu)造函數(shù)和原型模式
//組合使用構(gòu)造函數(shù)和原型模式
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)// ["李三", "王二"]