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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

对js面向对象的理解

發(fā)布時間:2025/3/21 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 对js面向对象的理解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)自:http://www.cnblogs.com/jingwhale/p/4678656.html

js面向?qū)ο罄斫?br /> ECMAScript 有兩種開發(fā)模式:1.函數(shù)式(過程化),2.面向?qū)ο?OOP)。
面向?qū)ο蟮恼Z言有一個標(biāo)志,那就是類的概念,而通過類可以創(chuàng)建任意多個具有相同屬性和方法的對象。但是,ECMAScript 沒有類的概念,因此它的對象也與基于類的語言中的對象有所不同。
面向?qū)ο笥?大特征:封裝,繼承,多態(tài)

封裝

封裝其實(shí)就是隱藏內(nèi)部信息,只對外提供一個接口

js(如果沒有作特殊說明,本文中的js僅包含ES5以內(nèi)的內(nèi)容)本身是沒有class類型的,但是每個函數(shù)都有一個prototype屬性。prototype指向一個對象,當(dāng)函數(shù)作為構(gòu)造函數(shù)時,prototype則起到類似class的作用。

創(chuàng)建對象3種方式

var box = new Object(); //創(chuàng)建一個Object 對象 box.name = 'Lee'; //創(chuàng)建一個name 屬性并賦值 box.age = 100; //創(chuàng)建一個age 屬性并賦值 box.run = function () { //創(chuàng)建一個run()方法并返回值 return this.name + this.age + '運(yùn)行中...'; }; alert(box.run()); //輸出屬性和方法的值

上面創(chuàng)建了一個對象,并且創(chuàng)建屬性和方法,在run()方法里的this,就是代表box 對象本身。這種是JavaScript 創(chuàng)建對象最基本的方法,但有個缺點(diǎn),想創(chuàng)建多個類似的對象,就會產(chǎn)生大量的代碼。
為了解決多個類似對象聲明的問題,我們可以使用一種叫做工廠模式的方法,這種方法就是為了解決實(shí)例化對象產(chǎn)生大量重復(fù)的問題。

1.工廠模式

使用簡單的函數(shù)創(chuàng)建對象,為對象添加屬性和方法,然后返回對象

function createObject(name, age) { //集中實(shí)例化的函數(shù) var obj = new Object(); obj.name = name; obj.age = age; obj.run = function () { return this.name + this.age + '運(yùn)行中...'; }; return obj; } var box1 = createObject('Lee', 100); //第一個實(shí)例 var box2 = createObject('Jack', 200); //第二個實(shí)例 alert(box1.run()); alert(box2.run()); //保持獨(dú)立

工廠模式解決了重復(fù)實(shí)例化的問題,但是它有許多問題,創(chuàng)建不同對象其中屬性和方法都會重復(fù)建立,消耗內(nèi)存;還有函數(shù)識別問題等等。

2.構(gòu)造函數(shù)

構(gòu)造函數(shù)的方法有一些規(guī)范:
1)函數(shù)名和實(shí)例化構(gòu)造名相同且大寫,(PS:非強(qiáng)制,但這么寫有助于區(qū)分構(gòu)造函數(shù)和
普通函數(shù));
2)通過構(gòu)造函數(shù)創(chuàng)建對象,必須使用new 運(yùn)算符。

function Box(name, age) { //構(gòu)造函數(shù)模式 this.name = name; this.age = age; this.run = function () { return this.name + this.age + '運(yùn)行中...'; }; } var box1 = new Box('Lee', 100); //new Box()即可 var box2 = new Box('Jack', 200); alert(box1.run()); alert(box1 instanceof Box); //很清晰的識別他從屬于Box

構(gòu)造函數(shù)可以創(chuàng)建對象執(zhí)行的過程:

1)當(dāng)使用了構(gòu)造函數(shù),并且new 構(gòu)造函數(shù)(),那么就后臺執(zhí)行了new Object();
2)將構(gòu)造函數(shù)的作用域給新對象,(即new Object()創(chuàng)建出的對象),而函數(shù)體內(nèi)的this 就
代表new Object()出來的對象。
3)執(zhí)行構(gòu)造函數(shù)內(nèi)的代碼;
4)返回新對象(后臺直接返回)。

注:

1)構(gòu)造函數(shù)和普通函數(shù)的唯一區(qū)別,就是他們調(diào)用的方式不同。只不過,構(gòu)造函數(shù)也是函數(shù),必須用new 運(yùn)算符來調(diào)用,否則就是普通函數(shù)。

2)this就是代表當(dāng)前作用域?qū)ο蟮囊谩H绻谌址秶鷗his 就代表window 對象,如果在構(gòu)造函數(shù)體內(nèi),就代表當(dāng)前的構(gòu)造函數(shù)所聲明的對象。

這種方法解決了函數(shù)識別問題,但消耗內(nèi)存問題沒有解決。同時又帶來了一個新的問題,全局中的this 在對象調(diào)用的時候是Box 本身,而當(dāng)作普通函數(shù)調(diào)用的時候,this 又代表window。即this作用域的問題。

3.原型

我們創(chuàng)建的每個函數(shù)都有一個prototype(原型)屬性,這個屬性是一個對象,它的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法。邏輯上可以這么理解:prototype 通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個對象的原型對象。使用原型的好處可以讓所有對象實(shí)例共享它所包含的屬性和方法。也就是說,不必在構(gòu)造函數(shù)中定義對象信息,而是可以直接將這些信息添加到原型中。

function Box() {} //聲明一個構(gòu)造函數(shù) Box.prototype.name = 'Lee'; //在原型里添加屬性 Box.prototype.age = 100; Box.prototype.run = function () { //在原型里添加方法 return this.name + this.age + '運(yùn)行中...'; };

構(gòu)造函數(shù)的聲明方式和原型模式的聲明方式存儲情況如下:

image

所以,它解決了消耗內(nèi)存問題。當(dāng)然它也可以解決this作用域等問題。

我們經(jīng)常把屬性(一些在實(shí)例化對象時屬性值改變的),定義在構(gòu)造函數(shù)內(nèi);把公用的方法添加在原型上面,也就是混合方式構(gòu)造對象(構(gòu)造方法+原型方式):

var person = function(name){this.name = name};person.prototype.getName = function(){return this.name; }var zjh = new person(‘zhangjiahao’);zjh.getName(); //zhangjiahao

下面詳細(xì)介紹原型:

1.原型對象
  每個javascript對象都有一個原型對象,這個對象在不同的解釋器下的實(shí)現(xiàn)不同。比如在firefox下,每個對象都有一個隱藏的__proto__屬性,這個屬性就是“原型對象”的引用。

2.原型鏈
  由于原型對象本身也是對象,根據(jù)上邊的定義,它也有自己的原型,而它自己的原型對象又可以有自己的原型,這樣就組成了一條鏈,這個就是原型鏈,JavaScritp引擎在訪問對象的屬性時,如果在對象本身中沒有找到,則會去原型鏈中查找,如果找到,直接返回值,如果整個鏈都遍歷且沒有找到屬性,則返回undefined.原型鏈一般實(shí)現(xiàn)為一個鏈表,這樣就可以按照一定的順序來查找。

1)__proto__和prototype
JS在創(chuàng)建對象(不論是普通對象還是函數(shù)對象)的時候,都有一個叫做__proto__的內(nèi)置屬性,用于指向創(chuàng)建它的函數(shù)對象的原型對象prototype。以上面的例子為例:

console.log(zjh.proto === person.prototype) //true
同樣,person.prototype對象也有__proto__屬性,它指向創(chuàng)建它的函數(shù)對象(Object)的prototype

console.log(person.prototype.proto === Object.prototype) //true
繼續(xù),Object.prototype對象也有__proto__屬性,但它比較特殊,為null

console.log(Object.prototype.proto) //null
我們把這個有__proto__串起來的直到Object.prototype.__proto__為null的鏈叫做原型鏈。如下圖:

2dbd5870d84a471896d69f7d1980ae63

2)constructor
原型對象prototype中都有個預(yù)定義的constructor屬性,用來引用它的函數(shù)對象。這是一種循環(huán)引用

person.prototype.constructor === person //true
Function.prototype.constructor === Function //true
Object.prototype.constructor === Object //true
3)為加深對理解,我們再舉一個例子:

function Task(id){ this.id = id; } Task.prototype.status = "STOPPED"; Task.prototype.execute = function(args){ return "execute task_"+this.id+"["+this.status+"]:"+args; } var task1 = new Task(1); var task2 = new Task(2); task1.status = "ACTIVE"; task2.status = "STARTING"; print(task1.execute("task1")); print(task2.execute("task2"));

結(jié)果:
execute task_1[ACTIVE]:task1
execute task_2[STARTING]:task2
構(gòu)造器會自動為task1,task2兩個對象設(shè)置原型對象Task.prototype,這個對象被Task(在此最為構(gòu)造器)的prototype屬性引用,參看下圖中的箭頭指向。

image

由于Task本身仍舊是函數(shù),因此其”proto”屬性為Function.prototype, 而內(nèi)建的函數(shù)原型對象的”proto”屬性則為Object.prototype對象。最后Obejct.prototype的”proto”值為null。

總結(jié):

實(shí)例對象的__proto__指向,其構(gòu)造函數(shù)的原型;構(gòu)造函數(shù)原型的constructor指向?qū)?yīng)的構(gòu)造函數(shù)。構(gòu)造函數(shù)的prototype獲得構(gòu)造函數(shù)的原型。

有時某種原因constructor指向有問題,可以通過

constructor:構(gòu)造函數(shù)名;//constructor : Task
重新指向。

繼承

繼承是面向?qū)ο笾幸粋€比較核心的概念。其他正統(tǒng)面向?qū)ο笳Z言都會用兩種方式實(shí)現(xiàn)繼承:一個是接口實(shí)現(xiàn),一個是繼承。而ECMAScript 只支持繼承,不支持接口實(shí)現(xiàn),而實(shí)現(xiàn)繼承的方式依靠原型鏈完成。

在JavaScript 里,被繼承的函數(shù)稱為超類型(父類,基類也行,其他語言叫法),繼承的函數(shù)稱為子類型(子類,派生類)

1.call+遍歷
屬性使用對象冒充(call)(實(shí)質(zhì)上是改變了this指針的指向)繼承基類,方法用遍歷基類原型。

function A() {this.abc=12; }A.prototype.show=function () {alert(this.abc); };//繼承A function B() {//繼承屬性;this->new B()A.call(this); //有參數(shù)可以傳參數(shù)A.call(this,name,age) }//繼承方法;B.prototype=A.prototype; for(var i in A.prototype) {B.prototype[i]=A.prototype[i]; } //添加自己的方法 B.prototype.fn=function () {alert('abc'); };var objB=new B(); var objA=new A();objB.show();

可以實(shí)現(xiàn)多繼承。

2.寄生組合繼承
主要是Desk.prototype = new Box(); Desk 繼承了Box,通過原型,形成鏈條。主要通過臨時中轉(zhuǎn)函數(shù)和寄生函數(shù)實(shí)現(xiàn)。

臨時中轉(zhuǎn)函數(shù):基于已有的對象創(chuàng)建新對象,同時還不必因此創(chuàng)建自定義類型
寄生函數(shù):目的是為了封裝創(chuàng)建對象的過程

//臨時中轉(zhuǎn)函數(shù) function obj(o) { //o表示將要傳遞進(jìn)入的一個對象 function F() {} //F構(gòu)造是一個臨時新建的對象,用來存儲傳遞過來的對象 F.prototype = o; //將o對象實(shí)例賦值給F構(gòu)造的原型對象 return new F(); //最后返回這個得到傳遞過來對象的對象實(shí)例 } //寄生函數(shù) function create(box, desk) { var f = obj(box.prototype); f.constructor = desk; //調(diào)整原型構(gòu)造指針 desk.prototype = f; } function Box(name) { this.name = name; this.arr = ['apple','pear','orange']; } Box.prototype.run = function () { return this.name; }; function Desk(name, age) { Box.call(this, name); this.age = age; } //通過寄生組合繼承實(shí)現(xiàn)繼承 create(Box, Desk); //這句話用來替代Desk.prototype = new Box(); var desk = new Desk('Lee',100); desk.arr.push('peach'); alert(desk.arr); alert(desk.run());

臨時中轉(zhuǎn)函數(shù)和寄生函數(shù)主要做的工作流程:

臨時中轉(zhuǎn)函數(shù):返回的是基類的實(shí)例對象函數(shù)
寄生函數(shù):將返回的基類的實(shí)例對象函數(shù)的constructor指向派生類,派生類的prototype指向基類的實(shí)例對象函數(shù)(是一個函數(shù)原型),從而實(shí)現(xiàn)繼承。

總結(jié)

以上是生活随笔為你收集整理的对js面向对象的理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。