javascript 對象的封裝(一)---對象的創(chuàng)建

一炕桨、對象的創(chuàng)建方式

  1. 生成實例對象的原始模式 (對象直接量)
    假定我們把貓看成一個對象西篓,它有"名字"和"顏色"兩個屬性巫玻。
//單個對象創(chuàng)建
 var Cat = {
    name : '',
    color : ''
  } 

也可能有讀者說榜配,你上面不是用字面量定義了一個對象嗎? 不過這真的是一種“快捷方式”,在編程語言中慈格,一般叫做“語法糖”怠晴。
javascript的數(shù)據(jù)有兩種,簡單數(shù)據(jù)和復(fù)雜數(shù)據(jù)浴捆。簡單數(shù)據(jù)有undefined蒜田,null,boolean汤功,number和string這五種。復(fù)雜數(shù)據(jù)只有一種溜哮,即對象(object)滔金。
那么色解,在javascript中,數(shù)組是對象餐茵,函數(shù)也是對象,基本類型(number, string, boolean)包裝類(Number,String,Boolean)也是對象科阎。
現(xiàn)在,我們需要根據(jù)這個原型對象的規(guī)格(schema)忿族,生成兩個實例對象锣笨。

//多個對象創(chuàng)建
var cat1 = {}; // 創(chuàng)建一個空對象
    cat1.name = "大毛"; // 按照原型對象的屬性賦值
    cat1.color = "黃色";
  var cat2 = {};
    cat2.name = "二毛";
    cat2.color = "黑色";

好了,這就是最簡單的封裝了道批,把兩個屬性封裝在一個對象里面错英。但是,這樣的寫法有兩個缺點隆豹,一是如果多生成幾個實例椭岩,寫起來就非常麻煩移迫;二是實例與原型之間宿接,沒有任何辦法看出有什么聯(lián)系。

  1. 通過普通函數(shù) / 構(gòu)造對象 返回一個對象
    2.1. 通過(普通)函數(shù)對象 返回一個對象
function Cat(name,color) {
    return {
      name:name,
      color:color
    }
  }
var cat1 = Cat("大毛","黃色");
var cat2 = Cat("二毛","黑色");

然后生成實例對象谤绳,就等于是在調(diào)用函數(shù)
這種方法的問題依然是碉考,cat1和cat2之間沒有內(nèi)在的聯(lián)系塌计,不能反映出它們是同一個原型對象的實例。

2.2. 通過(構(gòu)造)函數(shù)對象 (通過new創(chuàng)建對象)
為了解決從原型對象生成實例的問題侯谁,Javascript提供了一個構(gòu)造函數(shù)(Constructor)模式锌仅。
所謂"構(gòu)造函數(shù)",其實就是一個普通函數(shù)良蒸,但是內(nèi)部使用了this變量技扼。對構(gòu)造函數(shù)使用new運算符,就能生成實例嫩痰,并且this變量會綁定在實例對象上剿吻。

比如,貓的原型對象現(xiàn)在可以這樣寫串纺,

function Cat(name,color){
    this.name=name;
    this.color=color;
  }
//創(chuàng)建對象
var cat1 = new Cat("大毛","黃色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.name); // 大毛
alert(cat1.color); // 黃色
//這時cat1和cat2會自動含有一個constructor屬性丽旅,指向它們的構(gòu)造函數(shù)。
alert(cat1.constructor == Cat); //true
alert(cat2.constructor == Cat); //true
//Javascript還提供了一個instanceof運算符纺棺,驗證原型對象與實例對象之間的關(guān)系榄笙。
alert(cat1 instanceof Cat); //true
alert(cat2 instanceof Cat); //true

2.2.1 系統(tǒng)自帶的的,eg:new Object(), Array(), Number(),Boolean(), Date()...
單個對象創(chuàng)建

var obj = new Object();
     obj.name = 'lyl';
     console.log(obj.name); //lyl

多個對象創(chuàng)建
工廠模式:使用的系統(tǒng)內(nèi)部Object構(gòu)造函數(shù),創(chuàng)建的對象

function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){ alert(this.name); };
return o; 
}
var person1 = createPerson('Nike',29,'teacher');
var person2 = createPerson('Arvin',20,'student');

在使用工廠模式創(chuàng)建對象的時候祷蝌,我們都可以注意到茅撞,在createPerson函數(shù)中,返回的是一個對象。那么我們就無法判斷返回的對象究竟是一個什么樣的類型米丘。于是就出現(xiàn)了使用自定義構(gòu)造函數(shù)創(chuàng)建對象剑令。

2.2.2 自定義的:為了和普通函數(shù)區(qū)分,首字母大寫拄查,采用大駝峰式寫法(普通函數(shù)采用小駝峰式寫法)

//創(chuàng)建單個對象
function Obj (name) {
         this.name = name;
         this.age = 18;
     }
     var obj = new Obj('lyl');
     console.log(obj.name); //lyl
     console.log(obj.age); //18
//創(chuàng)建多個對象
function Obj (name) {
         this.name = name;
         this.age = 18;
     }
     var obj = new Obj('lyl');
     var obj = new Obj('dfv');
//

自定義構(gòu)造函數(shù)的基本構(gòu)造原理:
首先吁津,文字理論解釋一番,其實一切的關(guān)鍵全在與new這個操作符上堕扶,用new和不用new返回的結(jié)果大相徑庭碍脏。不用new,則Obj('lyl')根本就是一個函數(shù)的正常執(zhí)行稍算,沒有返回值典尾,則默認返回undefined,而是用new操作符后js引擎就會將該函數(shù)看作構(gòu)造函數(shù)看待邪蛔,經(jīng)過內(nèi)部的一系列隱士操作急黎,返回值就是一個對象了。
看下如下demo:
demo1:用new和不用new的區(qū)別演示:

function Obj () {
         this.age = 18;
     }
   //   不用new
     console.log(Obj()); // undefined
   //  用new
     console.log(new Obj()); //Obj {age: 18}

demo2 用new返回值為對象的基本原理:
不用new侧到,函數(shù)內(nèi)的this指向的是window勃教,所以this.xxx定義的變量都是window上的屬性,但為什么使用new后其中的this就不是window對象了呢匠抗?那是因為:
用new后故源,js引擎會在函數(shù)被進行兩步隱士操作(假設(shè)構(gòu)造函數(shù)名為Person):
第一步, var this = Object.create(Peson.prototype); (也是創(chuàng)建對象的一種方法汞贸,下邊會講到) 隱士的改變函數(shù)內(nèi)this的含義绳军,現(xiàn)在函數(shù)內(nèi)的this是一個原型為Person.prototype, 構(gòu)造函數(shù)為Person的對象(其實此過程就將想要的對象基本創(chuàng)造成功了矢腻,只是差些屬性而已门驾,從此可是看出構(gòu)造函數(shù)創(chuàng)建對象的最根本原理是借用Object.create()方法來實現(xiàn)的,只不過被封裝功能化了)多柑;
第二步, 在創(chuàng)建的對象設(shè)置完所需要的屬性后奶是,隱士的將創(chuàng)建的對象this通過return返回 return this;
通過代碼的展現(xiàn):

//  構(gòu)造函數(shù)的原型
    Person.prototype = {
      say: function () {
        console.log('I am saying');
      }
    }

   //  構(gòu)造函數(shù)
    function Person () {
     //  隱士操作
     // var this = Object.create(Person.prototype);
     
     //返回對象屬性的設(shè)置
       this.name = 'lyl';
       this.age = 18;

     //  隱士操作
       // return this;
    }

    var person1 = new Person();
    console.log(person1.name); // lyl
    person1.say(); //I am saying

上述兩步理論的驗證:
第一步:現(xiàn)在函數(shù)內(nèi)的this是一個原型為Person.prototype, 構(gòu)造函數(shù)為Person的對象

//  構(gòu)造函數(shù)的原型
    Person.prototype = {
      say: function () {
        console.log('I am saying');
      }
    }

   //  構(gòu)造函數(shù)
    function Person () {

       this.name = 'lyl';
       this.age = 18;
       // 打印this對象的原型
       console.log(this.__proto__); // 
       // 驗證this是否是Person構(gòu)造函數(shù)的實例
       console.log(this instanceof Person); //true
    }
    new Person();//打印結(jié)果如下
   //  Object say: ()__proto__: Object
   // true

    Person();//打印結(jié)果如下
   //  Window
   // false

第二步:隱士的將創(chuàng)建的對象this通過return返回:
由以上一些代碼竣灌,我們已經(jīng)可以看出返回的是滿足條件的對象聂沙,現(xiàn)在我們創(chuàng)建對象時不用new,并顯示的模擬這兩步隱士操作來驗證(我們不用new則兩步隱士操作就不會產(chǎn)生)

//  構(gòu)造函數(shù)的原型
    Person.prototype = {
      say: function () {
        console.log('I am saying');
      }
    }

   //  構(gòu)造函數(shù)
    function Person () {
      var that = Object.create(Person.prototype);

       that.name = 'lyl';
       that.age = 18;
       
       return that;
    //提前返回that導(dǎo)致return this無法執(zhí)行而失效
    }

    var person = new Person();
   //此處不用new也是可以成功返回一個滿足條件的對象初嘹,因為顯示的返回了that
    console.log(person.name); //lyl
    person.say(); //I am saying

p.s. 關(guān)于顯示返回that的問題及汉,當(dāng)我們用new生成對象,若我們顯示return的是一個對象 / 引用值屯烦,則會導(dǎo)致return this失效坷随,若返回的是原始值房铭,則return this不會失效

  1. 原型創(chuàng)建對象模式(通過原型創(chuàng)建對象)
    構(gòu)造函數(shù)方法很好用,但是存在一個浪費內(nèi)存的問題温眉。
function Cat(name,color){
    this.name = name;
    this.color = color;
    this.type = "貓科動物";
    this.eat = function(){alert("吃老鼠");};
  }
//生成實例
var cat1 = new Cat("大毛","黃色");
var cat2 = new Cat ("二毛","黑色");
alert(cat1.type); // 貓科動物
cat1.eat(); // 吃老鼠
//表面上好像沒什么問題育叁,但是實際上這樣做,有一個很大的弊端芍殖。那就是對于每一個實例對象,type屬性和eat()方法都是一模一樣的內(nèi)容谴蔑,每一次生成一個實例豌骏,都必須為重復(fù)的內(nèi)容,多占用一些內(nèi)存隐锭。這樣既不環(huán)保窃躲,也缺乏效率。
alert(cat1.eat == cat2.eat); //false

能不能讓type屬性和eat()方法在內(nèi)存中只生成一次钦睡,然后所有實例都指向那個內(nèi)存地址呢蒂窒?回答是可以的。
Javascript規(guī)定荞怒,每一個構(gòu)造函數(shù)都有一個prototype屬性洒琢,指向另一個對象。這個對象的所有屬性和方法褐桌,都會被構(gòu)造函數(shù)的實例繼承衰抑。
這意味著,我們可以把那些不變的屬性和方法荧嵌,直接定義在prototype對象上呛踊。

function Cat(name,color){
    this.name = name;
    this.color = color;
  }
Cat.prototype.type = "貓科動物";
Cat.prototype.eat = function(){alert("吃老鼠")};
var cat1 = new Cat("大毛","黃色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.type); // 貓科動物
cat1.eat(); // 吃老鼠
//這時所有實例的type屬性和eat()方法,其實都是同一個內(nèi)存地址啦撮,指向prototype對象谭网,因此就提高了運行效率。
alert(cat1.eat == cat2.eat); //true

3.2 Prototype模式的驗證方法
為了配合prototype屬性赃春,Javascript定義了一些輔助方法愉择,幫助我們使用它。
1) isPrototypeOf()
這個方法用來判斷聘鳞,某個proptotype對象和某個實例之間的關(guān)系薄辅。

function Cat(name,color){
    this.name = name;
    this.color = color;
  }
Cat.prototype.type = "貓科動物";
Cat.prototype.eat = function(){alert("吃老鼠")};
var cat1 = new Cat("大毛","黃色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.type); // 貓科動物
cat1.eat(); // 吃老鼠
alert(Cat.prototype.isPrototypeOf(cat1)); //true
alert(Cat.prototype.isPrototypeOf(cat2)); //true

2)hasOwnProperty()
每個實例對象都有一個hasOwnProperty()方法,用來判斷某一個屬性到底是本地屬性抠璃,還是繼承自prototype對象的屬性站楚。

function Cat(name,color){
    this.name = name;
    this.color = color;
  }
Cat.prototype.type = "貓科動物";
Cat.prototype.eat = function(){alert("吃老鼠")};
var cat1 = new Cat("大毛","黃色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.type); // 貓科動物
cat1.eat(); // 吃老鼠
alert(Cat.prototype.isPrototypeOf(cat1)); //true
alert(Cat.prototype.isPrototypeOf(cat2)); //true
alert(cat1.hasOwnProperty("name")); // true
alert(cat1.hasOwnProperty("type")); // false

3) in運算符
in運算符可以用來判斷,某個實例是否含有某個屬性搏嗡,不管是不是本地屬性窿春。

function Cat(name,color){
    this.name = name;
    this.color = color;
  }
Cat.prototype.type = "貓科動物";
Cat.prototype.eat = function(){alert("吃老鼠")};
var cat1 = new Cat("大毛","黃色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.type); // 貓科動物
cat1.eat(); // 吃老鼠
alert(Cat.prototype.isPrototypeOf(cat1)); //true
alert(Cat.prototype.isPrototypeOf(cat2)); //true
alert("name" in cat1); // true
alert("type" in cat1); // true
//in運算符還可以用來遍歷某個對象的所有屬性拉一。
for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }

3.3. 組合使用構(gòu)造函數(shù)模式和原型模式
根據(jù)原型與構(gòu)造函數(shù)的特點,可以組合使用構(gòu)造函數(shù)模式和原型模式

function Person(name,age,job){
this.name =name;
this.age = age;
this.job = job;
}
Person.prototype = {
constructor:Person,
sayName: function(){
alert(this.name);
};
}
var person1 = new Person('Nike',20,'teacher');
  1. Object.create(原型)旧乞; 創(chuàng)建一個繼承該原型的實例對象(Object.creat())
var f4 = {x: 1};
var t4 = Object.create(f4);
//如果f4的屬性改變蔚润,t4相應(yīng)的屬性也會改變

關(guān)于此方法的一些注意事項:

(1)、若傳參為Object.prototype尺栖,則創(chuàng)建的原型為Object.prototype嫡纠,和 new Object()創(chuàng)建的對象是一樣的

Object.create(Object.prototype) <==>new Object(); 

(2)、若傳參為空 或者 null延赌,則創(chuàng)建的對象是沒有原型的除盏, 導(dǎo)致該對象是無法用document.write()打印會報錯,因為document.write()打印的原理是調(diào)用Object.prototype.toString()方法挫以,該對象沒有原型者蠕,也就沒有該方法,所以document.write()無法打印
由此延伸的知識點: 引用值都也是算作是對象掐松,所以都可以用document.write()打吁饴隆;原始值numebr, boolean, string都有自己對象的包裝類大磺,借助此機制也是可以用document.write()打印出的抡句;但undefined 和 null既不是引用值,也沒有對應(yīng)的包裝類杠愧,所以應(yīng)該無法打印的玉转,但大家會發(fā)現(xiàn)這兩個值也是可是用document.write()打印的,因為這兩個值被設(shè)定為特殊值殴蹄,document.write()打印其是不用調(diào)用任何方法的究抓,而是之直接打印其值

二、創(chuàng)建的對象的本質(zhì)

以上四種方法看似不同袭灯,但他們的本質(zhì)是一樣的刺下,都是通過最基本的new Object()與原型來實現(xiàn)對象的創(chuàng)建的。
使用構(gòu)造器創(chuàng)建對象稽荧,其本質(zhì)如下:

image

對象的__proto__指向了構(gòu)造器的prototype橘茉,所以修改構(gòu)造器的屬性不會影響對象,但是修改構(gòu)造器原型會姨丈。
使用Object.create()

image

對象的__proto__ 指向了構(gòu)造器畅卓,所以可以通過修改父對象影響對象的屬性

三、__proto__prototype

__proto__prototype是比較容易混淆的兩個屬性蟋恬。__proto__是對象的屬性翁潘,用來在原型鏈上查找你需要的方法的實際對象。prototype是函數(shù)獨有的屬性當(dāng)我們使用關(guān)鍵詞new并且將函數(shù)作為構(gòu)造函數(shù)來構(gòu)造對象的時候, 它被用來構(gòu)建對象的__proto__屬性.
對于非函數(shù)的對象來說比較簡單只有__proto__歼争,但是函數(shù)也是對象拜马,所以在函數(shù)中__proto__prototype同時存在渗勘,一個函數(shù)作為構(gòu)造器時,參與其中的是prototype屬性俩莽,但是被創(chuàng)建時旺坠,__proto__指向了Function.prototype

參考:

JS對象創(chuàng)建的幾種方式整理javascript技巧腳本之家 http://www.jb51.net/article/107012.htm

js之對象(經(jīng)典) - 最騷的就是你 - 博客園 https://www.cnblogs.com/libin-1/p/5911190.html

Javascript 面向?qū)ο缶幊蹋ㄒ唬悍庋b - 阮一峰的網(wǎng)絡(luò)日志 http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html

js對象的創(chuàng)建 - 簡書 http://www.reibang.com/p/6377784b62b2?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

簡單理解javascript中apply()和call() - CSDN博客 https://blog.csdn.net/iamchuancey/article/details/78177028

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扮超,一起剝皮案震驚了整個濱河市取刃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌出刷,老刑警劉巖蝉衣,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異巷蚪,居然都是意外死亡,警方通過查閱死者的電腦和手機濒翻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門屁柏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人有送,你說我怎么就攤上這事淌喻。” “怎么了雀摘?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵裸删,是天一觀的道長。 經(jīng)常有香客問我阵赠,道長涯塔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任清蚀,我火速辦了婚禮匕荸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘枷邪。我一直安慰自己榛搔,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布东揣。 她就那樣靜靜地躺著践惑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嘶卧。 梳的紋絲不亂的頭發(fā)上尔觉,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音芥吟,去河邊找鬼穷娱。 笑死绑蔫,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泵额。 我是一名探鬼主播配深,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嫁盲!你這毒婦竟也來了篓叶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤羞秤,失蹤者是張志新(化名)和其女友劉穎缸托,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘾蛋,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡俐镐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了哺哼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佩抹。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖取董,靈堂內(nèi)的尸體忽然破棺而出棍苹,到底是詐尸還是另有隱情,我是刑警寧澤茵汰,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布枢里,位于F島的核電站,受9級特大地震影響蹂午,放射性物質(zhì)發(fā)生泄漏栏豺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一豆胸、第九天 我趴在偏房一處隱蔽的房頂上張望冰悠。 院中可真熱鬧,春花似錦配乱、人聲如沸溉卓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桑寨。三九已至,卻和暖如春忿檩,著一層夾襖步出監(jiān)牢的瞬間尉尾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工燥透, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沙咏,地道東北人辨图。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像肢藐,于是被迫代替她去往敵國和親故河。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • ??面向?qū)ο螅∣bject-Oriented,OO)的語言有一個標志痘煤,那就是它們都有類的概念凑阶,而通過類可以創(chuàng)建任意...
    霜天曉閱讀 2,109評論 0 6
  • 面向?qū)ο螅∣bject-Oriented,OO)的語言有一個標志,那就是它們都有類的慨念衷快,而通過類可以創(chuàng)建任意多個...
    threetowns閱讀 878評論 0 4
  • 博客內(nèi)容:什么是面向?qū)ο鬄槭裁匆嫦驅(qū)ο竺嫦驅(qū)ο缶幊痰奶匦院驮瓌t理解對象屬性創(chuàng)建對象繼承 什么是面向?qū)ο?面向?qū)ο?..
    _Dot912閱讀 1,424評論 3 12
  • 世間最幸福的事莫過于睡自家的床,吃父母做的飯都伪,被暗戀的人表白,陪自己的小孩玩游戲积担。 —題記 (一) 回到家第一...
    安南子閱讀 984評論 2 12
  • 又逢難得的春節(jié)假期陨晶,今年的感覺卻與以往大不相同;回顧過往十幾年每每遇上這難得的假期我?guī)缀醪粫疹櫲魏稳说母惺艿坭担^對...
    蓉魅閱讀 297評論 0 0