JavaScript構(gòu)造函數(shù)與原型之間的聯(lián)系
一、構(gòu)造函數(shù)和原型
1、構(gòu)造函數(shù)
構(gòu)造函數(shù)是一種特殊的函數(shù),主要用來初始化對象,即為對象成員變量賦初始值,它總與 new
一起使用。我們可以把對象中一些公共的屬性和方法抽取出來,然后封裝到這個函數(shù)里面。
在 JS 中,
使用構(gòu)造函數(shù)時要注意以下兩點:
- 構(gòu)造函數(shù)用于創(chuàng)建某一類對象,其首字母要大寫。
- 構(gòu)造函數(shù)要和
new
一起使用才有意義。
new 在執(zhí)行時會做四件事情:
- 在內(nèi)存中創(chuàng)建一個新的空對象。
- 讓 this 指向這個新的對象。
- 執(zhí)行構(gòu)造函數(shù)里面的代碼,給這個新對象添加屬性和方法。
- 返回這個新對象(所以構(gòu)造函數(shù)里面不需要
return
)。
JavaScript
的構(gòu)造函數(shù)中可以添加一些成員,可以在構(gòu)造函數(shù)本身上添加,也可以在構(gòu)造函數(shù)內(nèi)部的 this 上添加。通過這兩種方式添加的成員,就分別稱為靜態(tài)成員和實例成員。
實例成員:在構(gòu)造函數(shù)內(nèi)部創(chuàng)建的對象成員稱為實例成員,只能由實例化的對象來訪問。
靜態(tài)成員:在構(gòu)造函數(shù)本上添加的成員稱為靜態(tài)成員,只能由構(gòu)造函數(shù)本身來訪問 。
例如:
function A(uname,age){ this.uname = uname; this.age = age; this.say = function() { console.log(this.uname+'你好'); } } var wh = new A('王歡',18); var xl = new A('小熊',18);
在上述代碼中,構(gòu)造函數(shù)中通過this添加的name
,age
,say
方法都是實例成員。只能由實例化的對象來訪問。在構(gòu)造函數(shù)本身上添加的成員叫靜態(tài)成員,
如:創(chuàng)建一個靜態(tài)成員。
A.sex='女';
2、構(gòu)造函數(shù)的問題
構(gòu)造函數(shù)方法很好用,但是存在浪費內(nèi)存的問題
如下所示:
function Student(age,name){ this.age = age; this.name = name; this.score = function(){ console.log('孩子們成績都很好!'); } } console.dir(Student); var xl = new Student(18,'小熊'); var wh = new Student(17,'王歡'); xl.score(); wh.score();
通過下述代碼判斷兩次調(diào)用的方法是否地址相同。
console.log(xl.score === wh.score);
打印結(jié)果為:
可知兩次調(diào)用A內(nèi)部的say函數(shù),地址并不相同,是因為開辟兩個內(nèi)存空間,導(dǎo)致浪費內(nèi)存。
3、構(gòu)造函數(shù)原型 prototype
構(gòu)造函數(shù)通過原型分配的函數(shù)是所有對象所共享的。JavaScript 規(guī)定,每一個構(gòu)造函數(shù)都有一個 prototype
屬性,指向另一個對象。注意這個 prototype
就是一個對象,這個對象的所有屬性和方法,都會被構(gòu)造函數(shù)所擁有。
如下所示,創(chuàng)建一個構(gòu)造函數(shù):
function Student(age,name){ this.age = age; this.name = name; this.score = function(){ console.log('孩子們成績都很好!'); } } console.dir(Student);
打印該構(gòu)造函數(shù)里面所有的方法,可知:
可以找到 prototype
對象。
可以把那些不變的方法,直接定義在prototype
對象上,這樣所有對象的實例就可以共享這些方法。
function Student(age,name){ this.age = age; this.name = name; } Student.prototype.score = function(){ console.log('孩子們成績都很好!'); } console.dir(Student); var xl = new Student(18,'小熊'); var wh = new Student(17,'王歡'); xl.score(); wh.score(); console.log(xl.score === wh.score);
打印結(jié)果為:
并且兩次調(diào)用函數(shù)只開辟了一個內(nèi)存空間,也減少了內(nèi)存的浪費。
注意:一般情況下,公共屬性定義到構(gòu)造函數(shù)里面,公共方法定義到原型對象身上。
4、對象原型 __proto__
對象都會有一個屬性__proto__
指向構(gòu)造函數(shù)的 prototype
原型對象,之所以我們對象可以使用構(gòu)造函數(shù)prototype原型對象的屬性和方法,就是因為對象有 __proto__
原型的存在。
如下所示:
function Student(age,name){ this.age = age; this.name = name; } Student.prototype.score = function(){ console.log('孩子們成績都很好!'); } // console.dir(Student); var xl = new Student(18,'小熊'); var wh = new Student(17,'王歡'); console.log(xl);
通過以下代碼名看其是否具有__proto__
對象原型
console.log(xl);//對象身上系統(tǒng)自己添加一個__proto__屬性指向構(gòu)造函數(shù)的原型對象
輸出結(jié)果為:
可知存在。
在上述例子中輸入下述代碼判斷__proto__
對象原型和原型對象prototype
是否等價。
console.log(xl.__proto__ === Student.prototype);
打印結(jié)果為:true
故: __proto__
對象原型和原型對象prototype
是等價的
通過實例對象調(diào)用score函數(shù),如下所示:
xl.score();
輸出結(jié)果為:
可以調(diào)用,其方法查找規(guī)則是:首先看看xl對象身上是否有score
方法,如果有,則執(zhí)行這個對象上的score
,如果沒有該方法,因為有__prooto__
屬性的存在,就去構(gòu)造函數(shù)原型對象 prototype
身上去查找。
可用下圖描述:
__proto__
對象原型的意義就在于為對象的查找機制提供一個方向,或者說一條路線,但是它是一個非標(biāo)準(zhǔn)屬性,因此實際開發(fā)中,不可以使用這個屬性,它只是內(nèi)部指向原型對象 prototype
5、constructor 構(gòu)造函數(shù)
根據(jù)前面的例子,分別打印實例對象(Student
)的對象原型( __proto__)和構(gòu)造函數(shù)(xl)(prototype
)原型對象
console.log(Student.prototype); console.log(xl.__proto__);
打印結(jié)果為:
可知:對象原型( __proto__)和構(gòu)造函數(shù)(prototype)原型對象里面都有一個屬性 constructor 屬性 ,constructor
我們稱為構(gòu)造函數(shù),因為它指回構(gòu)造函數(shù)本身。
再分別打印對象原型和構(gòu)造函數(shù)原型的constroctor
屬性。觀察其返回值。
console.log(Student.prototype.constructor); console.log(xl.__proto__.constructor);
打印結(jié)果為:
可知他們均指向Student
構(gòu)造函數(shù)。
即constructor
主要用于記錄該對象引用于哪個構(gòu)造函數(shù),它可以讓原型對象重新指向原來的構(gòu)造函數(shù)。
一般情況下,對象的方法都在構(gòu)造函數(shù)的原型對象中設(shè)置。當(dāng)給構(gòu)造函數(shù)添加多個方法時,可以采用對象的方式,
如下所示:
Student.prototype = { score: function(){ console.log('孩子們成績都很好!')}, study: function(){ console.log('好好學(xué)習(xí)!'); }
當(dāng)打印修改后的原型對象的consructor屬性時:
發(fā)現(xiàn)原型對象的指向發(fā)生變化,這是因為給原型對象采取對象形式賦值,這樣就會覆蓋構(gòu)造函數(shù)原型對象原來的內(nèi)容,這樣修改后的原型對象constructor
就不再指向當(dāng)前構(gòu)造函數(shù)了。
此時,我們可以在修改后的原型對象中,添加一個 constructor
指向原來的構(gòu)造函數(shù)。
如下:
Student.prototype = { constructor:Student, score: function(){ console.log('孩子們成績都很好!')}, study: function(){ console.log('好好學(xué)習(xí)!'); }
最后,打印的結(jié)果為:
成功指回原構(gòu)造函數(shù)。
6、構(gòu)造函數(shù)、實例、原型對象三者之間的關(guān)系
根據(jù)上例:構(gòu)造函數(shù)、實例、原型對象三者之間的關(guān)系可用下圖描述:
7、JavaScript 的成員查找機制(規(guī)則)
- 當(dāng)訪問一個對象的屬性(包括方法)時,首先查找這個對象自身有沒有該屬性。
- 如果沒有就查找它的原型(也就是 __proto__指向的
prototype
原型對象)。 - 如果還沒有就查找原型對象的原型(Object的原型對象)。
依此類推一直找到 Object
為止(null)。
__proto__對象原型的意義就在于為對象成員查找機制提供一個方向,或者說一條路線。
8、 擴展內(nèi)置對象
可以通過原型對象,對原來的內(nèi)置對象進行擴展自定義的方法。
首先打印數(shù)組的原型對象,查看有哪些內(nèi)置對象。
console.log(Array.prototype);
打印結(jié)果為:
例如現(xiàn)在給數(shù)組增加自定義求偶數(shù)和的功能。
Array.prototype.sum = function(){ var sum = 0; for(var i=0;i<this.length;i++){ sum += this[i]; } return sum; }
檢查內(nèi)置對象是否擴展成功,再次輸入:
console.log(Array.prototype);
構(gòu)建成功,在給一個具體的實例對象,判斷是否可以正常使用:
var arr = [1,2,3]; console.log(arr.sum());
打印結(jié)果為:
二、類的本質(zhì)
class
本質(zhì)還是function
.- 類的所有方法都定義在類的
prototype
屬性上 - 類創(chuàng)建的實例,里面也有__proto__ 指向類的prototype原型對象
- ES6的類它的絕大部分功能,ES5都可以做到,新的
class
寫法只是讓對象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法而已。
到此這篇關(guān)于JavaScript構(gòu)造函數(shù)與原型的文章就介紹到這了,更多相關(guān)JavaScript構(gòu)造函數(shù)、原型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS跨域(Access-Control-Allow-Origin)前后端解決方案詳解
這篇文章主要介紹了瀏覽器跨域(Access-Control-Allow-Origin)解決方案詳解包括了前端跨域,后端跨域,js原生實現(xiàn)jsonp,jQuery實現(xiàn)jsonp,vue.js實現(xiàn)jsonp,需要的朋友可以參考下2022-01-01Uncaught EvalError:Refused to evaluate a
這篇文章主要為大家介紹了Uncaught EvalError:Refused to evaluate a string as JavaScript解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-09-09