日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

JavaScript中的原型和对象机制

發布時間:2023/11/29 javascript 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JavaScript中的原型和对象机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1 對象相關的一些語言特性

1.1 一切皆為對象
JavaScript里所有的東西都是對象. 對象是屬性的集合. 數字, 字符串, 布爾值等原始值是"偽對象", 它們同樣擁有屬性, 但是是在棧上分配并按值傳遞. 而其他的對象是堆上分配并按引用傳遞.
一個很重要的概念是,?函數也是對象, 能夠作為變量的值, 返回值, 參數或者屬性的值. 函數對象特殊的地方是能通過"xxx()"語法執行包含在xxx函數對象內的代碼. 因為這一特殊性, typeof xxx 將會返回function, 但這只是一種便利設施.

1.2 對象的屬性可以動態添加和刪除

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


1.3 除了宿主對象, 其它對象皆由構造函數創建
要有對象, 就先要有創建對象的方法.?
在C++/Java等語言, 這個方法就是實例化XXX類的一個實例xxx.
而在JavaScript的世界里實際沒有類的東西, 當然仍然可以用"類"和"實例"等慣用語來描述JavaScript中類似的行為, 但其機制是完全不同的. JavaScript的對象是由構造函數創建的, 每個對象都有constructor屬性表示創建該對象的構造函數:

function?Test()?{?this.a?=?"hello";?}
var?test?=?new?Test();?//?由Test構造函數創建
alert(test.constructor);

var?o?=?{?a:?"hello"?};
//實際相當于
var?o_?=?new?Object();
o_.a?=?"hello";?//?由Object構造函數創建
alert(o.constructor);

構造函數也是對象, 那構造函數是由什么創建? 內建的Function函數:

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

Function函數又是由什么創建? 實際上Function是本機代碼實現的固有對象. 不過為了一致性, Function也有constructor屬性, 該屬性指向它自己. 接上面的代碼:

/*?輸出?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的概念
prototype是構造函數的一個屬性, 該屬性指向一個對象. 而這個對象將作為該構造函數所創建的所有實例的基引用(base reference),?可以把對象的基引用想像成一個自動創建的隱藏屬性. 當訪問對象的一個屬性時, 首先查找對象本身, 找到則返回; 若不, 則查找基引用指向的對象的屬性(如果還找不到實際上還會沿著原型鏈向上查找, ?直至到根).?只要沒有被覆蓋的話, 對象原型的屬性就能在所有的實例中找到.
原型默認為Object的新實例, 由于仍是對象, 故可以給該對象添加新的屬性:

//?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?的?base?reference指向p_obj
var?eric?=?new?Person("Eric");??//?eric?的?base?reference也是指向p_obj

//?注意sayName代碼中的this將指向實例化后的對象(this綁定)
john.sayName();?//?john對象本身沒有sayName屬性,?于是訪問原型對象p_obj的sayName屬性
eric.sayName();?//?訪問同一個原型對象p_obj的sayName屬性


var?tmp?=?Person.prototype;
tmp.boss?=?"David";
//?于這個運行點,?p_obj已經被修改
//?根據上述屬性訪問流程,?新的修改(boss屬性)能反映到所有的實例,?包括已經創建和即將創建的
alert("John's?boss?is?"?+?john.boss);
alert("Eric's?boss?is?"?+?eric.boss);


//?hisCar和sayCar屬性將增加到john對象而不是p_obj對象..
john.hisCar?=?"Audi";
john.sayCar?=?function(){
????alert(this.name?+?"?has?a?car?of?"?+?this.hisCar);
}
john.sayCar();
//?..因此下一句將錯誤,?因為eric對象本身和原型p_obj都沒有sayName屬性
/*?eric.sayCar();?*/


2.2 原型鏈
除了能修改prototype指向的對象, 還能修改prototype指向哪一個對象, 即為prototype賦予一個不同的對象. 這可以實現一種簡單的繼承:

function?Superman()?{}
Superman.prototype.sayHello?=?function(){
????alert("I'm?a?superman.");
}

function?SupermanCan(skill){
????this.skill?=?skill;
}
//?為prototype賦予Superman的實例..
SupermanCan.prototype?=?new?Superman();
//?..再動態添加新的屬性
SupermanCan.prototype.sayMore?=?function(){
????this.sayHello();?//?調用"父類"的方法
????alert("I?can?"?+?this.skill);
}

var?david?=?new?SupermanCan("fly");
//?output:?I'm?a?superman.?I?can?fly
david.sayMore();

如果先實例化出一個對象, 再為構造函數prototype賦予一個不同的對象, 將會: 已經創建的對象的基引用不變, 將來創建的對象的基引用為新的原型對象:

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

var?foo2?=?new?Foo();?//?foo2的基引用指f1對象
foo2.echo();?//?output:?sound

所有的構造函數的prototype都不能為空, 就是說Superman.prototype = null 會被解釋引擎無視;? 另一方面, Object構造函數也有prototype屬性(該屬性是只讀的, 可以為原型增加屬性,但不能賦予不同的對象), 故因此可以有多層的原型鏈, 但原型鏈的根必定會是Object.prototype . 這意味著給Object.prototype增加屬性影響到所有對象:

Object.prototype.echo?=?function()?{
????alert("hello");
}

//?echo屬性將增加到所有對象固有對象和自定義對象

var?arr?=?new?Array();
arr.echo();
Array.echo();

function?ObjCons()????{
????this.dummy?=?"d";
}
var?obj?=?new?ObjCons();
obj.echo();
ObjCons.echo();


3. 構造函數和new的實質
構造函數是一個地地道道的函數, 一個函數之所以能成為構造函數, 是因為new運算符:

this.msg?=?"window";

function?Test()
{
????alert(this.msg);
}

Test();?//?window
var?test?=?new?Test();?//?undefined.?因為test對象沒有定義msg屬性

二者區別在于如何切入對象: Test() 在某個對象(例子中為window)的上下文上執行代碼, 即this指向這個對象; new Test()創建一個新對象, 并以這個新的對象為上下文(this指向新對象)執行代碼, 然后返回這個新對象.?
假如有個函數:

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

結合以上的所有論述, 可以推測new Test()行為的偽代碼表示為:
??????創建一個新對象temp;
??????temp.constructor = Test;
??????temp.(base reference) = Test.prototype; // 這一句先于代碼體執行, 意味著構造函數里的this.xxx能訪問原型對象的屬性xxx
??????bind: this?=?temp; //?將this綁定到temp對象
??????// 開始執行函數代碼
??????var dummy = "have money";
??????this.wish = dummy; // 為temp對象添加wish屬性
??????doSomeThing();
??????....
??????// 結束執行函數代碼
??????return temp;
這個未必會符合內部的二進制實現, 但卻能很好地解釋了JavaScript的特性.

總結

以上是生活随笔為你收集整理的JavaScript中的原型和对象机制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。