問題: 請看如下代碼,若是咱們要建立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。
bash
console.dir(Person);//構造函數
console.dir(per);//實例對象
複製代碼
從圖上看,構造函數中的prototype中的屬行和實例對象per中的__proto__中的屬性如出一轍,那咱們想一想它們相等嗎?咱們能夠驗證一下。
console.log(per.__proto__ === Person.prototype);
複製代碼
由此咱們能夠判斷出,構造函數Person中的prototype原型和實例對象per中的__proto__原型指向是相同的,咱們通常是先有構造函數再有實例對象,實例對象由構造函數建立,因此說實例對象中的__proto__原型指向的是構造函數中的原型prototype
實例對象中__proto__是原型,瀏覽器使用的。構造函數中的prototype是原型,程序員使用的函數
那接下來咱們看一幅圖來看看原型鏈究竟是什麼?
學習
咱們來分析分析整張圖
因此從上圖咱們能夠獲得如下幾點:
ui
最終咱們能夠得出,原型鏈:它是一種關係,實例對象和原型對象之間的關係,關係是經過原型__proto__來聯繫的this
原型改變添加方法也無非就是兩種:1.在原型改變前添加加方法。2.在原型改變之後添加方法。
spa
首先,咱們來看第一種:
prototype
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