精品伊人久久大香线蕉,开心久久婷婷综合中文字幕,杏田冲梨,人妻无码aⅴ不卡中文字幕

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
JS面向?qū)ο蟮某绦蛟O(shè)計

         面向?qū)ο蟮恼Z言有一個標(biāo)志,即擁有類的概念,抽象實(shí)例對象的公共屬性與方法,基于類可以創(chuàng)建任意多個實(shí)例對象,一般具有封裝、繼承、多態(tài)的特性!但JS中對象與純面向?qū)ο笳Z言中的對象是不同的,ECMA標(biāo)準(zhǔn)定義JS中對象:無序?qū)傩缘募希鋵傩钥梢园局怠ο蠡蛘吆瘮?shù)。可以簡單理解為JS的對象是一組無序的值,其中的屬性或方法都有一個名字,根據(jù)這個名字可以訪問相映射的值(值可以是基本值/對象/方法)。

  一、理解對象:

         第一種:基于Object對象

var person = new Object();
person.name = 'My Name';
person.age = 18;
person.getName = function(){
    return this.name;
}

        第二種:對象字面量方式(比較清楚的查找對象包含的屬性及方法)

var person = {
    name : 'My name',
    age : 18,
    getName : function(){
        return this.name;
    }
}

      JS的對象可以使用‘.’操作符動態(tài)的擴(kuò)展其屬性,可以使用’delete’操作符或?qū)傩灾翟O(shè)置為’undefined’來刪除屬性。如下:

person.newAtt=’new Attr’;//添加屬性
alert(person.newAtt);//new Attr
delete person.age;
alert(person.age);//undefined(刪除屬性后值為undefined);

二、對象屬性類型

      ECMA-262第5版定義了JS對象屬性中特征(用于JS引擎,外部無法直接訪問)。ECMAScript中有兩種屬性:數(shù)據(jù)屬性和訪問器屬性

      1、數(shù)據(jù)屬性:

      數(shù)據(jù)屬性指包含一個數(shù)據(jù)值的位置,可在該位置讀取或?qū)懭胫担搶傩杂?個供述其行為的特性:

      [[configurable]]:表示能否使用delete操作符刪除從而重新定義,或能否修改為訪問器屬性。默認(rèn)為true;

      [[Enumberable]]:表示是否可通過for-in循環(huán)返回屬性。默認(rèn)true;

      [[Writable]]:表示是否可修改屬性的值。默認(rèn)true;

      [[Value]]:包含該屬性的數(shù)據(jù)值。讀取/寫入都是該值。默認(rèn)為undefined;如上面實(shí)例對象person中定義了name屬性,其值為’My name’,對該值的修改都反正在這個位置

      要修改對象屬性的默認(rèn)特征(默認(rèn)都為true),可調(diào)用Object.defineProperty()方法,它接收三個參數(shù):屬性所在對象,屬性名和一個描述符對象(必須是:configurable、enumberable、writable和value,可設(shè)置一個或多個值)。

      如下:(瀏覽器支持:IE9+、Firefox 4+、Chrome、Safari5+)

var person = {};
Object.defineProperty(person, 'name', {
    configurable: false,
    writable: false,
    value: 'Jack'
});
alert(person.name);//Jack
delete person.name;
person.name = 'lily';
alert(person.name);//Jack

      可以看出,delete及重置person.name的值都沒有生效,這就是因?yàn)檎{(diào)用defineProperty函數(shù)修改了對象屬性的特征;值得注意的是一旦將configurable設(shè)置為false,則無法再使用defineProperty將其修改為true(執(zhí)行會報錯:can't redefine non-configurable property);

      2、訪問器屬性:

      它主要包括一對getter和setter函數(shù),在讀取訪問器屬性時,會調(diào)用getter返回有效值;寫入訪問器屬性時,調(diào)用setter,寫入新值;該屬性有以下4個特征:

      [[Configurable]]:是否可通過delete操作符刪除重新定義屬性;

      [[Numberable]]:是否可通過for-in循環(huán)查找該屬性;

      [[Get]]:讀取屬性時調(diào)用,默認(rèn):undefined;

      [[Set]]:寫入屬性時調(diào)用,默認(rèn):undefined;

      訪問器屬性不能直接定義,必須使用defineProperty()來定義,如下:

var person = {
    _age: 18
};
Object.defineProperty(person, 'isAdult', {
    get: function () {
        if (this._age >= 18) {
            return true;
        } else {
            return false;
        }
    }
});
alert(person.isAdult?'成年':'未成年');//成年

      從上面可知,定義訪問器屬性時getter與setter函數(shù)不是必須的,并且,在定義getter與setter時不能指定屬性的configurable及writable特性;

      此外,ECMA-262(5)還提供了一個Object.defineProperties()方法,可以用來一次性定義多個屬性的特性:

var person = {};
Object.defineProperties(person,{
    _age:{
        value:19
    },
    isAdult:{
        get: function () {
            if (this._age >= 18) {
                return true;
            } else {
                return false;
            }
        }
    }
});
alert(person.isAdult?'成年':'未成年');//成年

 

      上述代碼使用Object.defineProperties()方法同時定義了_age及isAudlt兩個屬性的特性

      此外,使用Object.getOwnPropertyDescriptor()方法可以取得給定屬性的特性:

var descriptor = Object.getOwnPropertyDescriptor(person,'_age');
alert(descriptor.value);//19

   對于數(shù)據(jù)屬性,可以取得:configurable,enumberable,writable和value;

   對于訪問器屬性,可以取得:configurable,enumberable,get和set

三、創(chuàng)建對象

使用Object構(gòu)造函數(shù)或?qū)ο笞置媪慷伎梢詣?chuàng)建對象,但缺點(diǎn)是創(chuàng)建多個對象時,會產(chǎn)生大量的重復(fù)代碼,因此下面介紹可解決這個問題的創(chuàng)建對象的方法

     1、工廠模式

function createPerson(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.getName = function () {
        return this.name;
    }
    return o;//使用return返回生成的對象實(shí)例
}
var person = createPerson('Jack', 19, 'SoftWare Engineer');

     創(chuàng)建對象交給一個工廠方法來實(shí)現(xiàn),可以傳遞參數(shù),但主要缺點(diǎn)是無法識別對象類型,因?yàn)閯?chuàng)建對象都是使用Object的原生構(gòu)造函數(shù)來完成的。

     2、構(gòu)造函數(shù)模式

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.getName = function () {
        return this.name;
    }
}

var person1 = new Person('Jack', 19, 'SoftWare Engineer');

var person2 = new Person('Liye', 23, 'Mechanical Engineer');

 

     使用自定義的構(gòu)造函數(shù)(與普通函數(shù)一樣,只是用它來創(chuàng)建對象),定義對象類型(如:Person)的屬性和方法。它與工廠方法區(qū)別在于:

  • 沒有顯式地創(chuàng)建對象
  • 直接將屬性和方法賦值給this對象;
  • 沒有return語句;

      此外,要創(chuàng)建Person的實(shí)例,必須使用new關(guān)鍵字,以Person函數(shù)為構(gòu)造函數(shù),傳遞參數(shù)完成對象創(chuàng)建;實(shí)際創(chuàng)建經(jīng)過以下4個過程:

  1. 創(chuàng)建一個對象
  2. 將函數(shù)的作用域賦給新對象(因此this指向這個新對象,如:person1)
  3. 執(zhí)行構(gòu)造函數(shù)的代碼
  4. 返回該對象

     上述由Person構(gòu)造函數(shù)生成的兩個對象person1與person2都是Person的實(shí)例,因此可以使用instanceof判斷,并且因?yàn)樗袑ο蠖祭^承Object,因此person1 instanceof Object也返回真:

alert(person1 instanceof Person);//true;
alert(person2 instanceof Person);//true;
alert(person1 instanceof Object);//true;
alert(person1.constructor === person2.constructor);//ture;

     雖然構(gòu)造函數(shù)方式比較不錯,但也存在缺點(diǎn),那就是在創(chuàng)建對象時,特別針對對象的屬性指向函數(shù)時,會重復(fù)的創(chuàng)建函數(shù)實(shí)例,以上述代碼為基礎(chǔ),可以改寫為:

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.getName = new Function () {//改寫后效果與原代碼相同,不過是為了方便理解
        return this.name;
    }
}
   上述代碼,創(chuàng)建多個實(shí)例時,會重復(fù)調(diào)用new Function();創(chuàng)建多個函數(shù)實(shí)例,這些函數(shù)實(shí)例還不是一個作用域中,當(dāng)然這一般不會有錯,但這會造成內(nèi)存浪費(fèi)。當(dāng)然,可以在函數(shù)中定義一個getName = getName的引用,而getName函數(shù)在Person外定義,這樣可以解決重復(fù)創(chuàng)建函數(shù)實(shí)例問題,但在效果上并沒有起到封裝的效果,如下所示:
function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.getName = getName;
}
function getName() {//到處是代碼,看著亂!!
        return this.name;
}

     3、原型模式

     JS每個函數(shù)都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,它是所有通過new操作符使用函數(shù)創(chuàng)建的實(shí)例的原型對象。原型對象最大特點(diǎn)是,所有對象實(shí)例共享它所包含的屬性和方法,也就是說,所有在原型對象中創(chuàng)建的屬性或方法都直接被所有對象實(shí)例共享。

function Person(){
}
Person.prototype.name = 'Jack';//使用原型來添加屬性
Person.prototype.age = 29;
Person.prototype.getName = function(){
    return this.name;
}
var person1 = new Person();
alert(person1.getName());//Jack
var person2 = new Person();
alert(person1.getName === person2.getName);//true;共享一個原型對象的方法

      原型是指向原型對象的,這個原型對象與構(gòu)造函數(shù)沒有太大關(guān)系,唯一的關(guān)系是函數(shù)的prototype是指向這個原型對象!而基于構(gòu)造函數(shù)創(chuàng)建的對象實(shí)例也包含一個內(nèi)部指針為:[[prototype]]指向原型對象。

       實(shí)例屬性或方法的訪問過程是一次搜索過程:

  • 首先從對象實(shí)例本身開始,如果找到屬性就直接返回該屬性值;
  • 如果實(shí)例本身不存在要查找屬性,就繼續(xù)搜索指針指向的原型對象,在其中查找給定名字的屬性,如果有就返回;

     基于以上分析,原型模式創(chuàng)建的對象實(shí)例,其屬性是共享原型對象的;但也可以自己實(shí)例中再進(jìn)行定義,在查找時,就不從原型對象獲取,而是根據(jù)搜索原則,得到本實(shí)例的返回;簡單來說,就是實(shí)例中屬性會屏蔽原型對象中的屬性;

     原型與in操作符

     一句話:無論原型中屬性,還是對象實(shí)例的屬性,都可以使用in操作符訪問到;要想判斷是否是實(shí)例本身的屬性可以使用object.hasOwnProperty(‘a(chǎn)ttr’)來判斷;

     原生對象中原型

     原生對象中原型與普通對象的原型一樣,可以添加/修改屬性或方法,如以下代碼為所有字符串對象添加去左右空白原型方法:

String.prototype.trim = function(){
    return this.replace(/^\s+/,'').replace(/\s+$/,'');
}
var str = '   word space   ';
alert('!'+str.trim()+'!');//!word space!

     原型模式的缺點(diǎn),它省略了為構(gòu)造函數(shù)傳遞初始化參數(shù),這在一定程序帶來不便;另外,最主要是當(dāng)對象的屬性是引用類型時,它的值是不變的,總是引用同一個外部對象,所有實(shí)例對該對象的操作都會其它實(shí)例:

function Person() {
}
Person.prototype.name = 'Jack';
Person.prototype.lessons = ['Math','Physics'];
var person1 = new Person();
person1.lessons.push('Biology');
var person2 = new Person();
alert(person2.lessons);//Math,Physics,Biology,person1修改影響了person2

     4、組合構(gòu)造函數(shù)及原型模式

      目前最為常用的定義類型方式,是組合構(gòu)造函數(shù)模式與原型模式。構(gòu)造函數(shù)模式用于定義實(shí)例的屬性,而原型模式用于定義方法和共享的屬性。結(jié)果,每個實(shí)例都會有自己的一份實(shí)例屬性的副本,但同時又共享著對方方法的引用,最大限度的節(jié)約內(nèi)存。此外,組合模式還支持向構(gòu)造函數(shù)傳遞參數(shù),可謂是集兩家之所長。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.lessons = ['Math', 'Physics'];
}
Person.prototype = {
    constructor: Person,//原型字面量方式會將對象的constructor變?yōu)镺bject,此外強(qiáng)制指回Person
    getName: function () {
        return this.name;
    }
}
var person1 = new Person('Jack', 19, 'SoftWare Engneer');
person1.lessons.push('Biology');
var person2 = new Person('Lily', 39, 'Mechanical Engneer');
alert(person1.lessons);//Math,Physics,Biology
alert(person2.lessons);//Math,Physics
alert(person1.getName === person2.getName);//true,//共享原型中定義方法

 

   在所接觸的JS庫中,jQuery類型的封裝就是使用組合模式來實(shí)例的!!!

 
    5、動態(tài)原型模式

     組合模式中實(shí)例屬性與共享方法(由原型定義)是分離的,這與純面向?qū)ο笳Z言不太一致;動態(tài)原型模式將所有構(gòu)造信息都封裝在構(gòu)造函數(shù)中,又保持了組合的優(yōu)點(diǎn)。其原理就是通過判斷構(gòu)造函數(shù)的原型中是否已經(jīng)定義了共享的方法或?qū)傩裕绻麤]有則定義,否則不再執(zhí)行定義過程。該方式只原型上方法或?qū)傩灾欢x一次,且將所有構(gòu)造過程都封裝在構(gòu)造函數(shù)中,對原型所做的修改能立即體現(xiàn)所有實(shí)例中:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.lessons = ['Math', 'Physics'];
}
if (typeof this.getName != 'function') {//通過判斷實(shí)例封裝
  Person.prototype = {
    constructor: Person,//原型字面量方式會將對象的constructor變?yōu)镺bject,此外強(qiáng)制指回Person
    getName: function () {
      return this.name;
    }
  }
}
var person1 = new Person('Jack', 19, 'SoftWare Engneer');
person1.lessons.push('Biology');
var person2 = new Person('Lily', 39, 'Mechanical Engneer');
alert(person1.lessons);//Math,Physics,Biology
alert(person2.lessons);//Math,Physics
alert(person1.getName === person2.getName);//true,//共享原型中定義方法

     注:以上內(nèi)容參考《JavaScript 高級程序設(shè)計》第3版

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

主站蜘蛛池模板: 社会| 辽源市| 四平市| 林口县| 临朐县| 云梦县| 仲巴县| 定州市| 内乡县| 克东县| 鱼台县| 芷江| 伊春市| 宁城县| 潞西市| 濉溪县| 潞城市| 宣汉县| 三都| 武安市| 遂平县| 安塞县| 绥滨县| 奉贤区| 灵川县| 门源| 保定市| 延寿县| 当阳市| 南宁市| 澎湖县| 天台县| 威海市| 浦江县| 二连浩特市| 兰溪市| 广昌县| 明水县| 两当县| 仪陇县| 军事|