學習目標
- 原型
- 原型鏈
- 原型指向改變後是如何新增方法和屬性
- 原型指向改變後的原型鏈
- 例項物件的屬性和原型物件的屬性重名
- 通過原型繼承
- 組合繼承
- 拷貝繼承
一,原型
問題:
請看以下程式碼,如果我們要建立100個物件,應該怎麼建立?
function Person(name, sex) {
this.name = name;
this.sex = sex;
this.drink () {
console.log('我想喝手磨咖啡!!')
}
}
for (let i = 0; i < 100; i++) {
var per = new Person('蘇大強', '男');
per.drink();
}
複製程式碼
從上面的程式碼可以看出,如果我們要建立100個Person物件,這樣要開一百個記憶體空間,每次都要呼叫drink()函式,由於drink()函式都是一樣的,每個記憶體空間裡都有它太過於浪費空間,那我們怎樣才能避免這種情況,減少記憶體呢?我們接下來引入原型prototype
function Person(name, sex) {
this.name = name;
this.sex = sex;
}
//為原型新增方法
Person.prototype.drink = function () {
console.log('我想喝手磨咖啡!!')
}
//例項化物件
let per = new Person('蘇大強', '男');
per.drink();
複製程式碼
我們運用了原型prototype,可以共享資料,減少記憶體空間。
二,原型鏈
我們既然清楚了原型,那我們再來看看原型鏈。首先我們列印一下建構函式Person和例項物件per。
console.dir(Person);//建構函式
console.dir(per);//例項物件
複製程式碼
從圖上看,建構函式中的prototype中的屬行和例項物件per中的__proto__中的屬性一模一樣,那我們想想它們相等嗎?我們可以驗證一下。
console.log(per.__proto__ === Person.prototype);
複製程式碼
由此我們可以判斷出,建構函式Person中的prototype原型和例項物件per中的__proto__原型指向是相同的,我們一般是先有建構函式再有例項物件,例項物件由建構函式建立,所以說例項物件中的__proto__原型指向的是建構函式中的原型prototype
例項物件中__proto__是原型,瀏覽器使用的。建構函式中的prototype是原型,程式設計師使用的
那接下來我們看一幅圖來看看原型鏈到底是什麼?
我們來分析分析整張圖
- 首先,建構函式中的prototype屬性指向自己的原型物件
- 然後,原型物件中的構造器指向的是,原型物件所在的建構函式
- 再然後,例項物件中的__proto__指向的是,它所在建構函式中prototype屬性所指向的原型物件
所以從上圖我們可以得到以下幾點:
- 例項物件的原型指向了建構函式中prototype屬性所指向的原型物件,所以例項物件和原型物件之間有關係,它和建構函式是一個間接的關係。
- 我們從程式碼中也可以得出,例項物件可以直接訪問原型物件中的屬性或方法。
- 例項物件和原型物件之間有關係,它們的關係是通過原型__proto__來連線的。
最終我們可以得出,原型鏈:它是一種關係,例項物件和原型物件之間的關係,關係是通過原型__proto__來聯絡的
三,原型指向改變後是如何新增方法和屬性
原型改變新增方法也無非就是兩種:1.在原型改變前新增加方法。2.在原型改變以後新增方法。
首先,我們來看第一種:
function Person(name, sex) {
this.name = name;
this.sex = sex;
}
Person.prototype.drink = function () {
console.log('我想喝水!!')
}
function Student(name, sex) {
this.name = name;
this.sex = sex;
}
Student.prototype.eat = function () {
console.log('我想吃東西!!')
}
//改變原型指向
Student.prototype = new Person('人', '男');
let stu = new Student('學生', '女');
stu.drink();
stu.eat();
複製程式碼
我們來執行以下:
我們可以看到圖中的資訊,stu.eat()不是一個函式,剛才我們明明將eat()新增到了Student的原型上,怎麼現在報錯了?
原因是:由於Student的原型指向改變了,它指向了new Person('人', '男'),並且Person的原型上並沒有eat(),所以報錯,那麼第一種情況在原型改變之前新增是錯誤的!
我們再來看第二種情況:在原型改變之後新增方法。
function Person(name, sex) {
this.name = name;
this.sex = sex;
}
Person.prototype.drink = function () {
console.log('我想喝水!!')
}
function Student(name, sex) {
this.name = name;
this.sex = sex;
}
//改變原型指向
Student.prototype = new Person('人', '男');
//為原型新增方法
Student.prototype.eat = function () {
console.log('我想吃東西!!')
}
let stu = new Student('學生', '女');
stu.drink();
stu.eat();
複製程式碼
我們來執行以下:
四,原型指向改變後的原型鏈
那麼,當原型指向改變之後,原型鏈會發生怎樣的改變呢?
那我們來們分析以下:
- 原型指向改變之前
- 原型指向改變之後
我們先來分析原型指向改變之前:
//人的建構函式
function Person(name) {
this.name = name;
}
//為原型新增方法
Person.prototype.drink = function () {
console.log('我想喝水!!')
}
//學生的建構函式
function Student(name) {
this.name = name;
}
//為原型新增方法
Student.prototype.eat = function () {
console.log('我想吃東西!!')
}
//例項物件
let per = new Person('老師');
let stu = new Student('學生');
console.dir(Person);//建構函式
console.dir(per);//例項物件
console.dir(Student);//建構函式
console.dir(stu);//例項物件
複製程式碼
我們執行以下這段程式碼:
請看每個prototype和__proto__,我們可以得到它們的原型鏈圖:
由此圖,我們可以看出,原型沒有改變之前,例項物件的__proto__都指向自己建構函式中prototype屬性所指向的原型物件。
我們再來看看原型指向改變之後:
//人的建構函式
function Person(name) {
this.name = name;
}
//為原型新增方法
Person.prototype.drink = function () {
console.log('我想喝水!!')
}
//學生的建構函式
function Student(name) {
this.name = name;
}
//為原型新增方法
Student.prototype.eat = function () {
console.log('我想吃東西!!')
}
//改變學生的原型指向
Student.prototype = new Person('老師');
//例項物件
let stu = new Student('學生');
console.dir(Person);//建構函式
console.dir(new Person('老師'))//例項物件
console.dir(Student.prototype)//Student的原型物件
console.dir(Student);//建構函式
console.dir(stu);//例項物件
複製程式碼
我們來看看執行結果:
我們來分析分析:
我們程式碼和圖結合來看,當Student.prototype = new Person('老師');之後,①學生建構函式的prototype屬性會斷開指向原型物件,②原型物件中的構造器也會斷開指向建構函式,③例項物件的__proto__會斷開指向原型物件
這裡的序號沒有任何意義,相當於起的名字!!!
還沒有完,我們再來看圖:
當原型指向改變之後,學生的建構函式中的prototype屬性指向了new Person('老師');,隨後,學生的例項化物件中的__proto__屬性指向了學生建構函式中prototype屬性所指向的new Person('老師');
原型鏈改變完畢!
五,例項物件的屬性和原型物件的屬性重名
當例項物件中的屬性和原型物件中的屬性重名時應該先訪問那個?
我們來看一看程式碼:
//人的建構函式
function Person(age, sex) {
this.age = age;
this.sex = sex;
}
//為原型新增屬性
Person.prototype.sex = "女";
//例項化物件
var per = new Person(10,"男");
console.log(per.sex);
複製程式碼
看圖:
如果在例項物件中找不到呢?我們來看程式碼:
function Person(age) {
this.age = age;
}
//為原型新增屬性
Person.prototype.sex = "女";
//例項化物件
var per = new Person(10);
console.log(per.sex);
複製程式碼
我們來看執行結果:
六,通過原型繼承
//js中通過原型來實現繼承
//人的建構函式
function Person(name, age, sex) {
this.name = name;
this.sex = sex;
this.age = age;
}
//為原型新增方法
Person.prototype.eat = function () {
console.log("人吃東西");
};
Person.prototype.sleep = function () {
console.log("人在睡覺");
};
Person.prototype.play = function () {
console.log("生活就是編程式碼!");
};
//學生的建構函式
function Student(score) {
this.score = score;
}
//改變學生的原型的指向即可==========>學生和人已經發生關係
Student.prototype = new Person("小明", 10, "男");
//為原型新增方法
Student.prototype.study = function () {
console.log("學習很累很累的哦.");
};
var stu = new Student(100);
console.log(stu.name);
console.log(stu.age);
console.log(stu.sex);
stu.eat();
stu.play();
stu.sleep();
console.log("下面的是學生物件中自己有的");
console.log(stu.score);
stu.study();
複製程式碼
看執行結果:
七,組合繼承
//組合繼承:原型繼承+借用建構函式繼承
//人的建構函式
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.sayHi=function () {
console.log("你好嗎?");
};
function Student(name, age, sex, score) {
//借用建構函式:屬性值重複的問題
Person.call(this, name, age, sex);
this.score = score;
}
//改變原型指向----繼承
Student.prototype = new Person();//不傳值
Student.prototype.eat = function () {
console.log("吃東西");
};
//例項物件
var stu = new Student("金仔", 20, "男", "100分");
console.log(stu.name, stu.age, stu.sex, stu.score);
stu.sayHi();
stu.eat();
var stu2=new Student("含仔", 20, "女", "100分");
console.log(stu2.name, stu2.age, stu2.sex, stu2.score);
stu2.sayHi();
stu2.eat();
複製程式碼
看執行結果:
八,拷貝繼承
function Person() {};
Person.prototype.age = 10;
Person.prototype.sex = "男";
Person.prototype.height = 100;
Person.prototype.play = function () {
console.log("玩耍!");
};
var Student = {};
//Person的構造中有原型prototype,prototype就是一個物件,那麼裡面,age,sex,height,play都是該物件中的屬性或者方法
for (let key in Person.prototype) {
Student[key] = Person.prototype[key];
}
console.dir(Student);
Student.play();
複製程式碼
請看執行結果:
至此,本片文章的全部內容完畢!本人是一個前端新人,本片文章若哪裡有不正確的地方,請各位前端大佬不吝斧正!我們們攜手共同進步!再次感謝!
另外,本人將要參加實習找工作,若是那位大佬覺得小學弟可以的話,請給小學弟一個機會。郵箱:1441398660@qq.com