掃描二維碼關注

首頁 APP開(kāi)發小(xiǎo)程序開(kāi)發 微信公衆号 網站建設 營銷推廣 經典案列 産品服務 關于我們

“學習(xí)不僅是掌握知識”

向書(shū)本學習(xí),還要向實踐學習(xí)、向生活學習(xí)。消化已有(yǒu)知識,
而且要力求有(yǒu)所發現、有(yǒu)所發明(míng)、有(yǒu)所創造

JavaScript中的(de)原型和(hé)對象機制(zhì)

2019/4/7 11:21:33

JavaScript中的(de)原型和(hé)對象機制(zhì)

1 對象相(xiàng)關的(de)一些語言特性

  1.1 一切皆爲對象

  JavaScript裏所有(yǒu)的(de)東西(xī)都(dōu)是對象. 對象是屬性的(de)集合. 數字, 字符串, 布爾值等原始值是"僞對象", 它們同樣擁有(yǒu)屬性, 但(dàn)是是在棧上(shàng)分(fēn)配并按值傳遞. 而其他(tā)的(de)對象是堆上(shàng)分(fēn)配并按引用(yòng)傳遞.

  一個(gè)很重要的(de)概念是, 函數也(yě)是對象, 能(néng)夠作爲變量的(de)值, 返回值, 參數或者屬性的(de)值. 函數對象特殊的(de)地(dì)方是能(néng)通過"xxx()"語法執行(xíng)包含在xxx函數對象内的(de)代碼. 因爲這一特殊性, typeof xxx 将會(huì)返回function, 但(dàn)這隻是一種便利設施.

  1.2 對象的(de)屬性可(kě)以動态添加和(hé)删除

var foo = new Object();
// 爲foo對象添加bar屬性
foo.bar = "foobar";
alert(foo.bar); //foobar
// 删除foo對象的(de)bar屬性
delete foo.bar;
alert(foo.bar); //undefined

  1.3 除了宿主對象, 其它對象皆由構造函數創建

  要有(yǒu)對象, 就先要有(yǒu)創建對象的(de)方法.

  在C++/Java等語言, 這個(gè)方法就是實例化XXX類的(de)一個(gè)實例xxx.

  而在JavaScript的(de)世界裏實際沒有(yǒu)類的(de)東西(xī), 當然仍然可(kě)以用(yòng)"類"和(hé)"實例"等慣用(yòng)語來描述JavaScript中類似的(de)行(xíng)爲, 但(dàn)其機制(zhì)是完全不同的(de). JavaScript的(de)對象是由構造函數創建的(de), 每個(gè)對象都(dōu)有(yǒu)constructor屬性表示創建該對象的(de)構造函數:

function Test() { this.a = "hello"; }
var test = new Test(); // 由Test構造函數創建
alert(test.constructor);
var o = { a: "hello" };
//實際相(xiàng)當于
var o_ = new Object();
o_.a = "hello"; // 由Object構造函數創建
alert(o.constructor);

  構造函數也(yě)是對象, 那構造函數是由什(shén)麽創建? 内建的(de)Function函數:

function Test(a, b)
{
    alert(a+b);
}
// 相(xiàng)當于:
Test = new Function(["a", "b"], "alert(a+b);");

  Function函數又(yòu)是由什(shén)麽創建? 實際上(shàng)Function是本機代碼實現的(de)固有(yǒu)對象. 不過爲了一緻性, Function也(yě)有(yǒu)constructor屬性, 該屬性指向它自己. 接上(shàng)面的(de)代碼:

/* 輸出 function Function(){
            [nATIve code]
        }
*/
alert(Test.constructor);
alert(Test.constructor.constructor === Test.constructor); // true
alert(Test.constructor === Object.constructor); // true

  2 原型prototype

  2.1 prototype的(de)概念

  prototype是構造函數的(de)一個(gè)屬性, 該屬性指向一個(gè)對象. 而這個(gè)對象将作爲該構造函數所創建的(de)所有(yǒu)實例的(de)基引用(yòng)(base reference), 可(kě)以把對象的(de)基引用(yòng)想像成一個(gè)自動創建的(de)隐藏屬性. 當訪問對象的(de)一個(gè)屬性時, 首先查找對象本身, 找到(dào)則返回; 若不, 則查找基引用(yòng)指向的(de)對象的(de)屬性(如(rú)果還找不到(dào)實際上(shàng)還會(huì)沿著(zhe)原型鏈向上(shàng)查找,  直至到(dào)根). 隻要沒有(yǒu)被覆蓋的(de)話(huà), 對象原型的(de)屬性就能(néng)在所有(yǒu)的(de)實例中找到(dào).

  原型默認爲Object的(de)新實例, 由于仍是對象, 故可(kě)以給該對象添加新的(de)屬性:

// prototype默認爲new Object(); 爲了方便, 記爲p_obj
function Person(name) {
    this.name = name;
}
// 爲 p_obj 增加 sayName 屬性
Person.prototype.sayName = function(){
    alert(this.name);
}
var john = new Person("John"); // john 的(de) base reference指向p_obj
var eric = new Person("Eric");  // eric 的(de) base reference也(yě)是指向p_obj
// 注意sayName代碼中的(de)this将指向實例化後的(de)對象(this綁定)
john.sayName(); // john對象本身沒有(yǒu)sayName屬性, 于是訪問原型對象p_obj的(de)sayName屬性
eric.sayName(); // 訪問同一個(gè)原型對象p_obj的(de)sayName屬性

var tmp = Person.prototype;
tmp.boss = "David";
// 于這個(gè)運行(xíng)點, p_obj已經被修改
// 根據上(shàng)述屬性訪問流程, 新的(de)修改(boss屬性)能(néng)反映到(dào)所有(yǒu)的(de)實例, 包括已經創建和(hé)即将創建的(de)
alert("John's boss is " + john.boss);
alert("Eric's boss is " + eric.boss);

// hisCar和(hé)sayCar屬性将增加到(dào)john對象而不是p_obj對象..
john.hisCar = "Audi";
john.sayCar = function(){
    alert(this.name + " has a car of " + this.hisCar);
}
john.sayCar();
// ..因此下一句将錯誤, 因爲eric對象本身和(hé)原型p_obj都(dōu)沒有(yǒu)sayName屬性
/* eric.sayCar(); */

2.2 原型鏈

  除了能(néng)修改prototype指向的(de)對象, 還能(néng)修改prototype指向哪一個(gè)對象, 即爲prototype賦予一個(gè)不同的(de)對象. 這可(kě)以實現一種簡單的(de)繼承:

function Superman() {}
Superman.prototype.sayHello = function(){
    alert("I'm a superman.");
}
function SupermanCan(skill){
    this.skill = skill;
}
// 爲prototype賦予Superman的(de)實例..
SupermanCan.prototype = new Superman();
// ..再動态添加新的(de)屬性
SupermanCan.prototype.sayMore = function(){
    this.sayHello(); // 調用(yòng)"父類"的(de)方法
    alert("I can " + this.skill);
}
var david = new SupermanCan("fly");
// output: I'm a superman. I can fly
david.sayMore();

  如(rú)果先實例化出一個(gè)對象, 再爲構造函數prototype賦予一個(gè)不同的(de)對象, 将會(huì): 已經創建的(de)對象的(de)基引用(yòng)不變, 将來創建的(de)對象的(de)基引用(yòng)爲新的(de)原型對象:

var f1 = {echo: function() { alert("sound"); } };
function Foo() {};
var foo = new Foo(); // foo的(de)基引用(yòng)指向Object實例
Foo.prototype = f1;
/* 未定義, 因爲這是"foo對象自己或者基引用(yòng)指向的(de)對象有(yǒu)echo屬性嗎?"
   而不是"foo對象自己或者Foo.prototype指向的(de)對象有(yǒu)echo屬性嗎?" */
alert(foo.echo);
var foo2 = new Foo(); // foo2的(de)基引用(yòng)指f1對象
foo2.echo(); // output: sound

  所有(yǒu)的(de)構造函數的(de)prototype都(dōu)不能(néng)爲空, 就是說Superman.prototype = null 會(huì)被解釋引擎無視;  另一方面, Object構造函數也(yě)有(yǒu)prototype屬性(該屬性是隻讀(dú)的(de), 可(kě)以爲原型增加屬性,但(dàn)不能(néng)賦予不同的(de)對象), 故因此可(kě)以有(yǒu)多層的(de)原型鏈, 但(dàn)原型鏈的(de)根必定會(huì)是Object.prototype . 這意味著(zhe)給Object.prototype增加屬性影響到(dào)所有(yǒu)對象:

Object.prototype.echo = function() {
    alert("hello");
}
// echo屬性将增加到(dào)所有(yǒu)對象固有(yǒu)對象和(hé)自定義對象
var arr = new Array();
arr.echo();
Array.echo();
function ObjCons()    {
    this.dummy = "d";
}
var obj = new ObjCons();
obj.echo();
ObjCons.echo();

  3. 構造函數和(hé)new的(de)實質

  構造函數是一個(gè)地(dì)地(dì)道道的(de)函數, 一個(gè)函數之所以能(néng)成爲構造函數, 是因爲new運算符:

this.msg = "window";
function Test()
{
    alert(this.msg);
}
Test(); // window
var test = new Test(); // undefined. 因爲test對象沒有(yǒu)定義msg屬性

  二者區别在于如(rú)何切入對象: Test() 在某個(gè)對象(例子中爲window)的(de)上(shàng)下文(wén)上(shàng)執行(xíng)代碼, 即this指向這個(gè)對象; new Test()創建一個(gè)新對象, 并以這個(gè)新的(de)對象爲上(shàng)下文(wén)(this指向新對象)執行(xíng)代碼, 然後返回這個(gè)新對象.

  假如(rú)有(yǒu)個(gè)函數:

function Test() {
    var dummy = "have money";
    this.wish = dummy;
    doSomeThing();
    
}

  結合以上(shàng)的(de)所有(yǒu)論述, 可(kě)以推測new Test()行(xíng)爲的(de)僞代碼表示爲:

      創建一個(gè)新對象temp;
      temp.constructor = Test;
      temp.(base reference) = Test.prototype; // 這一句先于代碼體執行(xíng), 意味著(zhe)構造函數裏的(de)this.xxx能(néng)訪問原型對象的(de)屬性xxx
      bind: this = temp; // 将this綁定到(dào)temp對象
      // 開(kāi)始執行(xíng)函數代碼
      var dummy = "have money";
      this.wish = dummy; // 爲temp對象添加wish屬性
      doSomeThing();
      ....
      // 結束執行(xíng)函數代碼
      return temp;


  這個(gè)未必會(huì)符合内部的(de)二進制(zhì)實現, 但(dàn)卻能(néng)很好地(dì)解釋了JavaScript的(de)特性.

 


深圳市南山區南山街(jiē)道南海(hǎi)大(dà)道西(xī)桂廟路(lù)北陽光(guāng)華藝大(dà)廈1棟4F、4G-04

咨詢電話(huà):136 8237 6272
大(dà)客戶咨詢:139 0290 5075
業(yè)務QQ:195006118
技術(shù)QQ:179981967

更多可(kě)以了解的(de)信息

客戶案列
新聞資訊
資質榮譽
團隊風采
項目進度查詢

售前QQ咨詢
QQ溝通 項目QQ溝通

精銳軟件(jiàn)

Copyright© 2018-2023 深圳市無窮大軟件技術有限公司 All Rights Reserved. 京ICP證000000号 公安備案号:粵公網安備44030502009460号