<xmp id="siekq">
<xmp id="siekq"><nav id="siekq"><strong id="siekq"></strong></nav>
<menu id="siekq"><menu id="siekq"></menu></menu> <dd id="siekq"></dd>
  • <xmp id="siekq"><nav id="siekq"></nav>
  • <menu id="siekq"><strong id="siekq"></strong></menu>
    <nav id="siekq"><code id="siekq"></code></nav>

    Javascript 面向對象編程(一):封裝

    作者: 阮一峰

    日期: 2010年5月17日

    騰訊課堂 NEXT 學院

    學習Javascript,最難的地方是什么?

    我覺得,Object(對象)最難。因為Javascript的Object模型很獨特,和其他語言都不一樣,初學者不容易掌握。

    下面就是我的學習筆記,希望對大家學習這個部分有所幫助。我主要參考了以下兩本書籍:

    《面向對象的Javascript》(Object-Oriented JavaScript)

    《Javascript高級程序設計(第二版)》(Professional JavaScript for Web Developers, 2nd Edition)

    它們都是非常優秀的Javascript讀物,推薦閱讀。

    筆記分成三部分。今天的第一部分是討論"封裝"(Encapsulation),后面的第二部分第三部分討論"繼承"(Inheritance)。

    ============================

    Javascript 面向對象編程(一):封裝

    作者:阮一峰

    Javascript是一種基于對象(object-based)的語言,你遇到的所有東西幾乎都是對象。但是,它又不是一種真正的面向對象編程(OOP)語言,因為它的語法中沒有class(類)。

    那么,如果我們要把"屬性"(property)和"方法"(method),封裝成一個對象,甚至要從原型對象生成一個實例對象,我們應該怎么做呢?

    一、 生成實例對象的原始模式

    假定我們把貓看成一個對象,它有"名字"和"顏色"兩個屬性。

      var Cat = {

        name : '',

        color : ''

      }

    現在,我們需要根據這個原型對象的規格(schema),生成兩個實例對象。

      var cat1 = {}; // 創建一個空對象

        cat1.name = "大毛"; // 按照原型對象的屬性賦值

        cat1.color = "黃色";

      var cat2 = {};

        cat2.name = "二毛";

        cat2.color = "黑色";

    好了,這就是最簡單的封裝了,把兩個屬性封裝在一個對象里面。但是,這樣的寫法有兩個缺點,一是如果多生成幾個實例,寫起來就非常麻煩;二是實例與原型之間,沒有任何辦法,可以看出有什么聯系。

    二、 原始模式的改進

    我們可以寫一個函數,解決代碼重復的問題。

      function Cat(name,color) {

        return {

          name:name,

          color:color

        }

      }

    然后生成實例對象,就等于是在調用函數:

      var cat1 = Cat("大毛","黃色");

      var cat2 = Cat("二毛","黑色");

    這種方法的問題依然是,cat1cat2之間沒有內在的聯系,不能反映出它們是同一個原型對象的實例。

    三、 構造函數模式

    為了解決從原型對象生成實例的問題,Javascript提供了一個構造函數(Constructor)模式。

    所謂"構造函數",其實就是一個普通函數,但是內部使用了this變量。對構造函數使用new運算符,就能生成實例,并且this變量會綁定在實例對象上。

    比如,貓的原型對象現在可以這樣寫,

      function Cat(name,color){

        this.name=name;

        this.color=color;

      }

    我們現在就可以生成實例對象了。

      var cat1 = new Cat("大毛","黃色");

      var cat2 = new Cat("二毛","黑色");

      alert(cat1.name); // 大毛

      alert(cat1.color); // 黃色

    這時cat1cat2會自動含有一個constructor屬性,指向它們的構造函數。

      alert(cat1.constructor == Cat); //true

      alert(cat2.constructor == Cat); //true

    Javascript還提供了一個instanceof運算符,驗證原型對象與實例對象之間的關系。

      alert(cat1 instanceof Cat); //true

      alert(cat2 instanceof Cat); //true

    四、構造函數模式的問題

    構造函數方法很好用,但是存在一個浪費內存的問題。

    請看,我們現在為Cat對象添加一個不變的屬性type(種類),再添加一個方法eat(吃)。那么,原型對象Cat就變成了下面這樣:

      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()方法都是一模一樣的內容,每一次生成一個實例,都必須為重復的內容,多占用一些內存。這樣既不環保,也缺乏效率。

      alert(cat1.eat == cat2.eat); //false

    能不能讓type屬性和eat()方法在內存中只生成一次,然后所有實例都指向那個內存地址呢?回答是可以的。

    五、 Prototype模式

    Javascript規定,每一個構造函數都有一個prototype屬性,指向另一個對象。這個對象的所有屬性和方法,都會被構造函數的實例繼承。

    這意味著,我們可以把那些不變的屬性和方法,直接定義在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()方法,其實都是同一個內存地址,指向prototype對象,因此就提高了運行效率。

      alert(cat1.eat == cat2.eat); //true

    六、 Prototype模式的驗證方法

    為了配合prototype屬性,Javascript定義了一些輔助方法,幫助我們使用它。,

    6.1 isPrototypeOf()

    這個方法用來判斷,某個proptotype對象和某個實例之間的關系。

      alert(Cat.prototype.isPrototypeOf(cat1)); //true

      alert(Cat.prototype.isPrototypeOf(cat2)); //true

    6.2 hasOwnProperty()

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

      alert(cat1.hasOwnProperty("name")); // true

      alert(cat1.hasOwnProperty("type")); // false

    6.3 in運算符

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

      alert("name" in cat1); // true

      alert("type" in cat1); // true

    in運算符還可以用來遍歷某個對象的所有屬性。

      for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }

    未完,請繼續閱讀這個系列的第二部分《構造函數的繼承》和第三部分《非構造函數的繼承》

    (完)

    留言(155條)

    直白易懂!逐步深入~這篇對于我這種初學者很有用啊!

    崇拜中。。阮兄知識面讓我情何以堪啊。。。

    不錯的note,最近我在看pro Javascript Design Patterns這本書,三、四章的封裝、繼承這塊講的非常好

    《javascript: the good part》中Dauglas不推薦用new的這種方法構造對象,因為如果忘記加上new,“即沒有編譯時警告,也沒有運行時警告”。他推薦的是函數化的方法,不使用prototype。

    好多年不寫程序了,不過還是覺得javascript是挺復雜的。

    喜歡這篇。
    越來越能看出,今后,這種娓娓道來的知識描述形式,將把至今為止的,邏輯嚴謹機械正確但卻難懂的知識描述方式打入歷史的垃圾箱里。

    人,是有靈性的,是非線性的,是量子性的。
    至今為止所謂的“線性嚴謹二元邏輯性的學術描述方式”,只不過是一種違反人類本質天性的東西,必將在完成其歷史使命之后,退出歷史舞臺。

    引用fan的發言:

    Dauglas不推薦用new的這種方法構造對象,因為如果忘記加上new,“即沒有編譯時警告,也沒有運行時警告”。他推薦的是函數化的方法,不使用prototype。

    雖然這個意見是正確的。但是,Douglas提出的方法,需要自己寫一個函數,在函數里再使用prototype,我覺得很不符合直覺。

    很簡潔實用的文章,里面有不少重來沒有看過的用法,如:alert("name" in cat1);
    沒想到還能用 in 這樣做判斷

    非常實用易懂的文章!
    最近看《Object Oriented JavaScript》,看完第六章講12種對象封裝構造的方法,徹底暈菜。。看了您的文章決定繼續去讀完這本書,的確是本很好的JS參考書啊

    這么一段代碼,ruan兄猜猜是啥結果?我覺得這是封裝中最容易犯的錯誤
    var a=function(){
    //empty
    }

    a.prototype.var1=[1,2,3];

    var b=new a();
    b.var1.push(4);

    var c=new a();
    alert(c.var1.join(","))

    引用axu的發言:

    這么一段代碼,ruan兄猜猜是啥結果?我覺得這是封裝中最容易犯的錯誤

    所以只能把不變的屬性和方法,綁在prototype對象上,而不能把可變屬性綁上去。

    我始終覺得javascript是一門函數式編程語言,這種OO的封裝應該只是一種臨時的workaround

    這時所有實例的type屬性和eat()方法,其實都是一個內存地址,指向prototype對象,因此就提高了運行效率。

    也增大了風險,因為一個地方改變了,其他地方都變了。和他帶來的好處相比,風險更大,不應該推薦。

    ruan兄應該是有點“完美主義”的偏執吧

    會不會連載,如果連載的話,就跟著你學了!

    引用fan的發言:

    《javascript: the good part》中Dauglas不推薦用new的這種方法構造對象,因為如果忘記加上new,“即沒有編譯時警告,也沒有運行時警告”。他推薦的是函數化的方法,不使用prototype。

    這個“如果“有點蒼白

    js的水很深很深 特別是用js嘗試模仿oo的風格

    還是喜歡使用簡單的 名稱空間+函數

    引用ethantsien的發言:

    這個“如果“有點蒼白

    蒼白與否暫且擱置,為了避免忘記加 new,最好的辦法是類名首字母大寫。

    不錯!是一篇好文章!

    這篇文章絕對要頂,學js多年,像中國唯一一個能把問題講的這么直白透徹的。

    這篇文章分析得十分仔細清楚,絕對經典啊!!!!

    引用axu的發言:

    這么一段代碼,ruan兄猜猜是啥結果?我覺得這是封裝中最容易犯的錯誤
    var a=function(){
    //empty
    }

    a.prototype.var1=[1,2,3];

    var b=new a();
    b.var1.push(4);

    var c=new a();
    alert(c.var1.join(","))



    這就涉及到對象的繼承機制了. 更直白一點, 涉及到 JS 語言的變量賦值與引用的區分.

    var parent = function(name){
    var that = {};
    that.name = name;
    return that;
    };

    var child = function(name,age){
    var that = parent(name);
    that.age = age;
    return that;
    };

    函數化不難

    Ruan兄, 文章易懂,不錯,Mark~~~!

    第五點應該是構造函數+原型 的混合模式吧。

    簡單易懂,對像我一樣的初學者來說很好

    這么入門級的文章,被這么多人“捧”,真夠汗的~~


    好文章,深得深入淺出的精髓

    我個人認為:javascript 只是腳本語言,它和c++或java等語言所肩負的任務不同, 因此關于“繼承”等功能不是重點,無需太復雜。
    我不贊同依靠javascript大量創建UI界面,構建UI的基礎應該是html+css。
    當然javascript也可以寫得很復雜和高明,那是高手們喜歡做的事。但不代表非要這樣不可。
    現在的電腦設備速度越來越快,內存越來越大,無需太計較,簡單易懂、安全(prototype的做法易出錯,風險大,使用時要很小心。)才是重點。

    Javascript是一種基于對象(object-based)的語言,你遇到的所有東西幾乎都是對象。但是,它又不是一種真正的面向對象編程(OOP)語言,因為它的語法中沒有class(類)。

    不明白為什么這么說,沒有class就不是真正的面向對象?面向對象是一種編程思想,有沒有class只是一種對這種思想的具體實現與體現,但沒有class也未必不是面向對象。


    ecmascript規范里如是說:
    ECMAScript is an object-oriented programming language for performing computations and manipulating computational objects within a host environment.

    ----Ecmascript262 v5 page 1

    初學者看了您的文章受益匪淺,已經購買推薦的書籍正在學習中.感謝...

    第5部分里的 alert(cat1.eat == cat2.eat); 是否改為===更恰當些?

    看了幾個星期的書,還不如看著幾個例子,淺顯易懂!

    OOP的人看JS真的是很變戀,你說Cat.prototype.type = "貓科動物";這個東西看上去像模仿類的靜態方法吧,但訪問時又不是直接通過類而是通過類的實例cat1.type;。這讓初學編程的人是學面向過程還是面向對向啊?

    引用Yoya的發言:
    OOP的人看JS真的是很變戀,你說Cat.prototype.type = "貓科動物";這個東西看上去像模仿類的靜態方法吧,但訪問時又不是直接通過類而是通過類的實例cat1.type;。這讓初學編程的人是學面向過程還是面向對向啊?

    訪問時通過類或是通過類的實例不能作為判斷是否面向對象的依據,其實都是訪問一塊公共內存而已

    我的問題跟Yoya的一樣 ,OOP的人看JS真的是很變戀,你說Cat.prototype.type = "貓科動物";這個東西看上去像模仿類的靜態方法吧,但訪問時又不是直接通過類而是通過類的實例cat1.type;搞不清楚到底是不是共享的了

    循序漸進 適合學習

    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); // 貓科動物

    這里兩個對象的type顯然不是一個內存地址,可以寫一個代碼跑一下啊,我試了,不再同一內存地址,如果在同一內存地址不就成了靜態變量。

    引用Dada的發言:

    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); // 貓科動物

    這里兩個對象的type顯然不是一個內存地址,可以寫一個代碼跑一下啊,我試了,不再同一內存地址,如果在同一內存地址不就成了靜態變量。

    剛才我說的也不準確,下面是摘自一個博客,我感覺挺準確的,你文章里寫的有點歧義,容易讓人以為prototype中所定的屬性是靜態方法。

    話說每一個方法對象被創建時,都會自動的擁有一個叫 prototype 的屬性。這個屬性并無什么特別之處,它和其他的屬性一樣可以訪問,可以賦值。不過當我們用 new 關鍵字來創建一個對象的時候,prototype 就起作用了:它的值(也是一個對象)所包含的所有屬性,都會被復制到新創建的那個對象上去。

    我昨天的留言,感覺太草率了,今天又讀了一些,發現博主說的還是比較準確的,雖然給我造成了一定的誤解,不過博主說還是對的,我上面說的那個根本不正確。。。。

    阮大哥,你這篇剛剛寫出來的時候我就看過了,當時看了半天沒看懂,直到今天才看懂,我是個超級初級入門者,入門了兩年啊,哈哈哈,我實在是太笨了。

    謝謝你,終于看明白了,而且原來是那么的淺顯易懂啊

    阮老大,javascript現在面相對象編程是一個大趨勢啊。我搜遍了國內的網上書店都沒有買到將關于javascript面相對象編程的書籍。國內的專家寫的什么垃圾書,在就過時了。javascript面相對象的時代到來了。封住繼承是必須的了。求javascript面相對象的書啊。。網上就您的這幾篇稿子經典,跪添了。

    引用Azrael的發言:

    我的問題跟Yoya的一樣 ,OOP的人看JS真的是很變戀,你說Cat.prototype.type = "貓科動物";這個東西看上去像模仿類的靜態方法吧,但訪問時又不是直接通過類而是通過類的實例cat1.type;搞不清楚到底是不是共享的了

    所以,我是不喜歡這個風格。
    特別是,這種風格帶來的如果只是內存節約的話

    文章非常好,加深理解!!!感謝作者!

    引用Ruan YiFeng的發言:

    所以只能把不變的屬性和方法,綁在prototype對象上,而不能把可變屬性綁上去。



    從原型拿來的東西,只是能查詢,檢索到,更新不會影響到原型的值。這樣理解好些?

    很好懂,謝謝啦!

    引用ethantsien的發言:

    這個“如果“有點蒼白

    蒼白+1

    Cat.prototype.isPrototypeOf(cat1)//true——原來構造函數的原型才是與實例的原型一致,也就是說原型一致
    Cat.constructor == cat1.constructor //false——不知道為什么構造函數與構造函數為什么不一致?
    Cat == cat1.constructor //true

    Cat和cat1都有兩個子對象:constructor和prototype
    cat1.constructor就是Cat
    cat1.prototype就是Cat.prototype
    問題是,Cat的另一個子對象Cat.constructor是什么呢?

    [QUOTE]
    我們現在就可以生成實例對象了。
      var cat1 = new Cat("大毛","黃色");
      var cat2 = new Cat("二毛","黑色");
      alert(cat1.name); // 大毛
      alert(cat1.color); // 黃色
    這時cat1和cat2會自動含有一個constructor屬性,指向它們的構造函數。
      alert(cat1.constructor == Cat); //true
      alert(cat2.constructor == Cat); //true
    [/QUOTE]
    ----------------------------------
    cat1和cat2自身是沒有constructor屬性的,而是通過原型鏈找到Cat.prototype.constructor屬性

     alert(cat1.eat == cat2.eat); //false

    為什么這一句我測試的結果是true?

    阮哥 前面都看的懂了 但是最后一句不太明白
    for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }
    希望你能解釋一下

    果然是你寫的.....

    我擦,評論好多見解是錯的。。。把原型鏈先搞清楚就不會錯了

    Prototype模式 還有一點沒有說到,
    className.Prototype.method(){
    //這里不能使用構造里的私有屬性
    }

    Prototype模式雖然能減少內存使用,提高應用性能,但是會使代碼變得冗長,增加維護難度。更好的做法應該是使用“繼承”的方法,將相同的屬性和方法封裝在另外的對象中。

    console.log(cat1.type == cat2.type); 和 console.log(cat1.type === cat2.type);
    返回的都是 true,求解惑

    終于懂了prototype了

    引用eagle的發言:

     alert(cat1.eat == cat2.eat); //false

    為什么這一句我測試的結果是true?

    alert(cat1.eat == cat2.eat); //false
    alert(cat1.eat() == cat2.eat()); //true

    真的不錯的文章,經久不衰啊,這是我看過的最好的一篇講解這個的

    引用雅蠛蝶的發言:

    alert(cat1.eat == cat2.eat); //false
    alert(cat1.eat() == cat2.eat()); //true

    可否這么理解: alert(cat1.eat == cat2.eat);比較的是方法本身,因為方法所屬的對象不同,所以兩者不相等; alert(cat1.eat() == cat2.eat());比較的是兩個方法的返回值,所以兩者相等。

    剛才試了一下發現,prototype方式添加的屬性在new 出來的對象中是可以修改的而且不影響別的對象和對象原型,貌似每個new出來的對象都存在prototype里面的屬性,那用prototype方式添加的屬性和直接在函數里面添加的屬性有什么區別了。。。求解

    引用Ruan YiFeng的發言:

    所以只能把不變的屬性和方法,綁在prototype對象上,而不能把可變屬性綁上去。


    雖然是10年的文章,但看起來還是有很多啟發。但是關于這段代碼,竊以為不是屬性可不可變的問題,而是,因為[1,2,3]是一個對象,所以var1屬性也只是對這個對象的一個地址引用,所以任何實例對象改變這個對象時,其它對象再去訪問該屬性,都是通過引用找到的內容,因此拿到的都是改變后的屬性值。如果var1賦值為基本類型,則不會出現這個問題。

    回頭再看的時候,發現很容易就理解的,簡單明了!

    引用axu的發言:

    這么一段代碼,ruan兄猜猜是啥結果?我覺得這是封裝中最容易犯的錯誤
    var a=function(){
    //empty
    }

    a.prototype.var1=[1,2,3];

    var b=new a();
    b.var1.push(4);

    var c=new a();
    alert(c.var1.join(","))


    今天重新看了阮老師寫的文章,感覺收獲很大,每次在不同的水平下看,總有不同體會,看到一條有意思的評論,上面這個人的在實例化后c居然可以改變a中的數組,按理不應這樣,求解答這是什么原理?

    之前也一直是看到 JavaScript 面向對象這塊真是頭大了,其實很多地方看起來很好理解,但是如果用自己的語言去組織這一部分內容,又什么都記不得了。如果一開始先開看簡單淺顯的文章,然后在去看這部分就很連貫了。

    prototype有一個注意點,如果是方法,可以共用內存,如果是一個像name這樣的屬性,還是會新開內存的。

    簡單明了,確是好文呀。

    贊,ruanyifeng老師果然是入門級指導的大師~

    略顯簡單吧

    不太理解 函數 和 函數相等 是空間相等 還是彈出的 值相等?

    簡單明白,終于知道js的對象了

    講到很清楚,個人看法是,屬性綁定在this上面,方法綁定在prototype上面。

    我們組長推薦的你的文章,果然不錯,淺顯易懂,又不缺乏內涵,贊一個!

    個人感覺,阮老師的博客受眾是非常非常初級入門的程序員,或者說,應該是鄰家大哥這種類型的,能幫一個不懂的孩子入門,或者產生興趣,這個階段不能深究晦澀的原理性知識。
    主要是因為文字里面有大量的原理性錯誤,軟件公司的員工或者碩士以上學歷的朋友建議還是帶著批判的眼光去學習比較好。
    但是不得不承認,阮老師的文字功底很好,就像有個人在你旁邊跟你聊天一樣,容易讓新手進入狀態,這方面必須贊。

    引用owen的發言:

    今天重新看了阮老師寫的文章,感覺收獲很大,每次在不同的水平下看,總有不同體會,看到一條有意思的評論,上面這個人的在實例化后c居然可以改變a中的數組,按理不應這樣,求解答這是什么原理?

    var1是a原型是的數組 b修改了原型導致var1成了[1,2,3,4] c繼承下來了,不是c可以改變a中的數組

    引用redspear的發言:

    prototype有一個注意點,如果是方法,可以共用內存,如果是一個像name這樣的屬性,還是會新開內存的。

    其實,關鍵還是javascript這門語言入門太簡單了,所以后面很多人都按自己的理解去做了,都不去看ECMA了,包括我,也包括阮老師!

    重新回頭看看語言權威的描述,根本沒有這些疑問,人家都說的很明白。

    你寫的這個不就是那個javascript高級編程里面的么(沒錯,就是你那參考資料),也沒有太大差別。其實這些 在javascript高級編程第3版寫的都很詳細。

    對于一個跨行學編程的小白來說,您的這篇文章對我太有意義了!

    寫的簡潔有力 通俗易懂 牛!

    入門級的,很適合初學者,簡單易懂!

    好文,能提供一個支付寶捐款鏈接嗎?我想donate!

    老師,您的這段代碼應該在name 和 color后面加上分號。很好的文章,讓我一下子就理解了

    /*二、 原始模式的改進
    我們可以寫一個函數,解決代碼重復的問題。
      function Cat(name,color){
        return {
          name:name,
          color:color
        }
      }
    */

    很棒的文章,感謝,茅塞頓開,看了你的文章再去看這個書更容易理解書了,因為書好晦澀

    深入淺出。

    好文章,太通俗易懂了!

      function Cat(name,color){

        this.name = name;

        this.color = color;

      }

      Cat.prototype.type = "貓科動物";

      Cat.prototype.eat = function(){alert(name)};
    如果在eat方法中用Cat中的name啊

    寫的真好 淺顯易懂

    引用richard的發言:

    這時所有實例的type屬性和eat()方法,其實都是一個內存地址,指向prototype對象,因此就提高了運行效率。

    也增大了風險,因為一個地方改變了,其他地方都變了。和他帶來的好處相比,風險更大,不應該推薦。

    ruan兄應該是有點“完美主義”的偏執吧

    所以說要把不變的屬性和方便綁在prototype對象上,可變的單獨封裝,作為私有屬性或方法。

    寫的真好 淺顯易懂

    引用owen的發言:

    今天重新看了阮老師寫的文章,感覺收獲很大,每次在不同的水平下看,總有不同體會,看到一條有意思的評論,上面這個人的在實例化后c居然可以改變a中的數組,按理不應這樣,求解答這是什么原理?

    這個貌似不是改變原a中的數組吧,而是數組的join方法會重新生成一個新的string。我也是初學者,不知道這樣說對不對!

    寫js也有一年多了~對于面向對象還是一知半解,沒有投入過真正的使用里面,一直搞不明白,有什么建議嗎,ruan大哥

    不太同意阮老師的標題“封裝”,封裝(encapsulation)又叫隱藏實現(Hiding the implementation)。就是只公開代碼單元的對外接口,而隱藏其具體實現。我們腦子里面很容易想到的是類似java里面的Public, Protected, Private 等訪問控制符,來控制成員變量的訪問權限,但是這篇文章只是在講創建一個類型,而不是講怎么控制屬性的訪問權限。

    寫得太好了,把復雜的東西簡單講明白了!
    請允許我永久收藏。

    寫的好,作為后端開發。對js知識了解皮毛。最近接觸到js的面向對象,看到這篇文章,感覺很有興趣

    封裝的意思不是這個吧?
    封裝,即隱藏對象的屬性和實現細節,僅對外公開接口 這才對吧

    非常適合入門!多謝分享!

    淺顯易懂,而且逐步深化,非常感謝!

    我寫了一篇關于Javascript中Prototype的文章,希望阮老師多多指教

    引用laoguo的發言:

    喜歡這篇。
    越來越能看出,今后,這種娓娓道來的知識描述形式,將把至今為止的,邏輯嚴謹機械正確但卻難懂的知識描述方式打入歷史的垃圾箱里。

    人,是有靈性的,是非線性的,是量子性的。
    至今為止所謂的“線性嚴謹二元邏輯性的學術描述方式”,只不過是一種違反人類本質天性的東西,必將在完成其歷史使命之后,退出歷史舞臺。

    爲了通俗而通俗,爲了容易理解而理解,只會使得讀者同樣流於表面,自以爲理解,實際上根本不懂。一旦難度稍微加大,舊有的思維不再好用,就難以提升了。

    反之一開始就接受抽象思維的訓練而不是套用舊有的思維,雖然一開始艱難,而後必會突飛猛進。

    建議這位朋友不要看任何低水平的「科普」文章,直接看英文維基百科、MDN、ECMAScript 草案比較好。

    真正的入門往往就是在一瞬間,過了這道坎,你就入門了。盲目地看再多低水平的文章,照樣毫無幫助。

    很喜歡,謝謝寫出這么好的文章

    在網上找了很多相關的內容,阮老師說的真的很好,很好理解,非常感謝阮老師分享!

    很好的文章,阮兄真的深入淺出。期待js方面更深的東西

    真的是言簡意賅,我這種菜鳥都能理解 ,感謝分享

    6.1 isPrototypeOf()
    這個方法用來判斷,某個“proptotype”對象和某個實例之間的關系。這個單詞寫錯了。

    引用Ruan YiFeng的發言:

    所以只能把不變的屬性和方法,綁在prototype對象上,而不能把可變屬性綁上去。



    var a=function(){
    //empty
    }
    a.prototype.var1=[1,2,3];
    a.prototype.var2 = "貓科動物";
    var b=new a();
    var d = new a();
    b.var1.push(4);
    b.var2 = "昆蟲";
    var c=new a();
    console.log(b.var2);
    console.log(c.var2);
    console.log(d.var2);
    console.log("--------------");
    console.log(c.var1.join(","));
    console.log(d.var1.join(","));

    不太理解,為什么var1影響了后續實例的對象,var2沒有影響到呢?

    @愛喝白開水:

    b.var1.push(4);b.var2 = "昆蟲"; 主要在這兩句的區別,js尋址的規則是先在對象的本地屬性下去找,如果找不到在去該對象的proto(不同瀏覽器的實現可能不一樣)中去找,proto指向的是a的prototype。

    第一句b.var1.push(4);首先在b的本地屬性(也就是上文提出的hasOwnProperty相關)找,沒找到,再去b的proto中去找,找到了,然后push一下,這里b.var1和a.prototype.var1引用的是同一個,修改了b.var1中的內容,也相當于修改了a.prototype.var1的內容。

    b.var2 = "昆蟲"; 這里不是改變了b.var2,而是新定義了b的本地屬性var2,你可以用hasOwnProperty判斷下,這里的var2是b的本地屬性。

    @愛喝白開水:

    某個實例改變var1,var2都不會改變之后實例的值
    那么var1怎么從[1,2,3]變成[1,2,3,4]了呢?
    這里說的"改變"是指"指針(引用)"的改變
    不能改變指針的值 但能改變指針的值的值
    而var1的指針確實沒變 都指向這個數組(指針的值)
    只不過是這個數組改變了(指針的值的值)

    a.prototype.var1 = [1,2,3,4];(修改指針的值,不生效)
    b.var1.push(4);(修改指針的值的值,生效)


    不知道你明白沒 同學生 正在學JS

    太棒啦,解答我不少的困惑。
    另外請教一下:Cat.prototype.type = "貓科動物";改寫成var type = "貓科動物";應該也是指向統一內存空間吧???

    老師把高級程序設計中的內容總結起來非常好,讓我很快就理解了

    厲害啊!淺顯易懂!

    一下看懂了,表白老師( ̄3 ̄)

    對我這樣的小白來說很有用,啃不懂大部頭,但是還得使用js做一些東西。這篇文章講解的很清楚。

    對提高自我的技術修養很是有用!

    比老師講的易理解

    阮老師真是我學習的榜樣,很耐心。

    真的是通俗易懂

    好文,豁然開朗的感覺

    其實在看這篇文章前我就懂這一塊了,但是還是想過來看看峰峰大神是如何講解原型對象和構造函數的,沒想到峰峰大神講的如此通俗易懂,膜拜大神!!!

    講的很明白,謝謝大神!

    引用redspear的發言:

    prototype有一個注意點,如果是方法,可以共用內存,如果是一個像name這樣的屬性,還是會新開內存的。

    這個主要是自有屬性和繼承屬性之間的來源差異

    茅塞頓開 只能說很幸運看到這篇文章

    很好理解,如果阮兄能用圖表示下每個內存塊里面的東西就更好啦

    通俗易懂 受益匪淺

    還是不太明白封裝在具體案例的時候怎么用

    全部是JavaScript高級程序設計的內容。

    @axu:

    簡單的理解的話,這個var1可以認為是a的靜態變量。
    a類的實際定義如果用java體現的話,大概是下面這樣的。
    JAVA --
    class A{public static int[] var1 = new int[]{1,2,3};}
    //本人js小白,以上理解不代表正確


    @愛喝白開水:

    剛才理解錯了,看到這個才大致明白啊。內存地址引用值的改變...不是全局變量的意思

    話說,留言部分能不能分頁啊,一進來就留意到滾動條老長老長。。。。。。

    為什么要滾到最下面才能留言,好麻煩

    實在話,現在2017年11月7日,這篇文章對我用處還是會很大

    引用ww的發言:

    剛才試了一下發現,prototype方式添加的屬性在new 出來的對象中是可以修改的而且不影響別的對象和對象原型,貌似每個new出來的對象都存在prototype里面的屬性,那用prototype方式添加的屬性和直接在函數里面添加的屬性有什么區別了。。。求解

    用prototype方式添加的屬性,是構造函數用于繼承的,也就是說,這些屬性是實例化對象所繼承的屬性;
    但是在函數內部添加的屬性如:this.name = name;這種方式添加的屬性是實例化對象創建后自身的直系屬性。
    說白了就是 繼承屬性和直系屬性的區別。

    引用wwz的發言:

    實在話,現在2017年11月7日,這篇文章對我用處還是會很大

    的確,看號稱最適合初學者的《JavaScript編程精解》也是云里霧里,閱讀了本篇后茅塞頓開。

    引用shimu的發言:

    直白易懂!逐步深入~這篇對于我這種初學者很有用啊!

    引用anoymous的發言:

    這么入門級的文章,被這么多人“捧”,真夠汗的~~

    大道至簡,把一個自己理解清楚的內容,用別人都容易理解的語言組織起來不是一件容易的事情

    謝謝教程!真是循序漸進,淺顯易懂,條理非常清晰,多謝 /抱拳

    function Cat(name, color) {
    this.name = name;
    this.color = color;
    this.type = "我是貓";
    this.eat = function () {
    console.log("貓吃老鼠")
    }
    }

    var cat1 = new Cat("小白", "white");
    var cat2 = new Cat("小黑", "black");

    console.log(cat1.type);
    cat1.eat();

    console.log(cat1.eat == cat2.eat);//false
    console.log(cat1.type == cat2.type);//true


    為什么方法是false,屬性是true呢

    @瓦力:

    兩個對象相比較的時候,如果不是指向同一個對象,就返回false。但使用prototype模式里的方法都引用的都是同一個,所以返回true

    太簡單了點吧。

    引用陳豪的發言:

    太簡單了點吧。

    阮大神2010年寫的,你2010年在干嘛?

    引用anoymous的發言:

    這么入門級的文章,被這么多人“捧”,真夠汗的~~

    2010年的你在干什么?你只是別人學的久一些,請別用一種比別人優越的感覺評價,搞技術需要的是不斷的學習

    在第三點構造函數模式那里"這時cat1和cat2會自動含有一個constructor屬性,指向它們的構造函數。"這句應該是cat1和cat2自己本身沒有constructor屬性,這個是在他們的構造函數的原型上也就是Cat.prototype上的構造函數,準確的說:應該是Cat.prototype.constructor === Cat

    第6.1 行 下面有個prototype 好像有個筆誤

    一直對object的概念有點模糊,還是來看阮老師的博客學習

    幾年前初學js,當時看原型鏈這塊看的云里霧里。就是因為講的時候對面向對象沒有一點了解。
    當時的材料也只直說原型鏈,執行上下文 bla bla bla... 沒有實際應用完全不知道在說什么。
    最近項目中遇到復雜的東西需要復用,以前簡單的封裝不能滿足需求了。
    再一看阮老師的js面向對象把以前初學時用不到的js原型鏈、構造函數都串了起來。
    真是邏輯清晰,簡單易懂。

    阮老師,大寫的服!通俗易懂,特別好理解!

    我有一個問題想請教一下:
    console.log()方法 和 alert()的方法在輸出cat對象的時候,
    前者輸出的是對象中的具體內容,后者輸出的是[object object],

    function catFun(name,color) {
    this.name=name;
    this.color=color;
    }
    var cat1 = new catFun("張三","黃色");
    console.log(cat1);
    alert(cat1);
    為什么輸出的是這樣子?


    我查到的資料是alert()輸出的是string類型,而console.log()是任何類型,
    執行console.log(cat1.toString())時,對象轉字符串的時候他會和alert()一致,不是很明白這一步為什么可以一致?
    希望得到解答,謝謝

    看了那么多原型、原型鏈文章都看不懂。看了這個才看懂,那些人也不知道寫的什么玩意,還敢寫文章。

    引用iliveido的發言:

    Cat和cat1都有兩個子對象:constructor和prototype
    cat1.constructor就是Cat
    cat1.prototype就是Cat.prototype
    問題是,Cat的另一個子對象Cat.constructor是什么呢?

    cat1有prototype嗎?你打印看看。

    @axu:

    function Animal(){
    this.superType="Animal";
    Animal.prototype.arguments=[1,2,3]
    var arr=Animal.prototype.arguments.push(4)
    console.log(arr)
    console.log(Animal.prototype)
    }
    var Dog=new Animal(
    )

    這樣就可以處理你說的那個問題了

    引用jaclon的發言:

    簡單易懂,對像我一樣的初學者來說很好

    這篇文章確實通俗易懂,有種茅塞頓開的感覺

    function F(){}
    F.prototype.name = 'jy'
    var _obj = new F()
    _obj.name 此時name屬性是調用的是從其構造函數F原型上繼承來的 屬性name

    而我們現在給_obj的name屬性賦值時
    _obj.name = 'ly'
    此時的name便是通過字面量創建對象的屬性
    現在的_obj具有本地name屬性,_obj的原型__prop__也具有一個name屬性

    破局,文章寫得很好。我這樣的小白都感覺自己學到了很多的東西。感謝阮一峰老師

    闊以撒!!!寫的很明白,對于初學者學習面向對象有幫助的.............

    真的牛,非常的通俗易懂

    @Ruan YiFeng:

    額,我運行了這段代碼,結果是1,2,3,4有什么不對嗎?本人小白,請不吝賜教

    我要發表看法

    «-必填

    «-必填,不公開

    «-我信任你,不會填寫廣告鏈接

    湖北快3湖北快3平台湖北快3主页湖北快3网站湖北快3官网湖北快3娱乐湖北快3开户湖北快3注册湖北快3是真的吗湖北快3登入湖北快3快三湖北快3时时彩湖北快3手机app下载湖北快3开奖 大庆 | 开封 | 沧州 | 营口 | 广元 | 锦州 | 漳州 | 荆州 | 宁德 | 莱州 | 运城 | 牡丹江 | 双鸭山 | 海南海口 | 和田 | 阳江 | 姜堰 | 怒江 | 七台河 | 灵宝 | 晋城 | 安徽合肥 | 东营 | 高密 | 克拉玛依 | 鹰潭 | 铜仁 | 德阳 | 鸡西 | 仙桃 | 晋江 | 大连 | 新余 | 安庆 | 嘉峪关 | 黔南 | 海西 | 台南 | 文昌 | 池州 | 临海 | 鄂州 | 河源 | 天水 | 新乡 | 阳泉 | 吉安 | 项城 | 澳门澳门 | 固原 | 北海 | 邢台 | 琼海 | 莒县 | 宁夏银川 | 海丰 | 嘉善 | 天水 | 乌兰察布 | 项城 | 丽江 | 松原 | 广安 | 陕西西安 | 醴陵 | 昆山 | 广安 | 滨州 | 湖州 | 仙桃 | 潍坊 | 张家口 | 南阳 | 桐乡 | 任丘 | 榆林 | 珠海 | 姜堰 | 临猗 | 石狮 | 定安 | 德州 | 保山 | 山南 | 乌海 | 广州 | 榆林 | 韶关 | 扬州 | 哈密 | 张家界 | 三沙 | 济南 | 平潭 | 南充 | 泉州 | 琼海 | 济南 | 昌吉 | 文山 | 沧州 | 常州 | 乐清 | 衢州 | 涿州 | 永州 | 台山 | 岳阳 | 马鞍山 | 喀什 | 盐城 | 玉树 | 任丘 | 克拉玛依 | 甘孜 | 铜陵 | 新乡 | 抚顺 | 商丘 | 白城 | 山东青岛 | 铜仁 | 遵义 | 宜昌 | 荆门 | 甘孜 | 武安 | 随州 | 忻州 | 阳江 | 定安 | 新泰 | 西藏拉萨 | 雅安 | 聊城 | 大理 | 长治 | 玉溪 | 天水 | 随州 | 黄山 | 阿勒泰 | 松原 | 仁怀 | 燕郊 | 保定 | 和田 | 喀什 | 丽江 | 烟台 | 江苏苏州 | 宜都 | 慈溪 | 吴忠 | 福建福州 | 丹阳 | 苍南 | 澳门澳门 | 开封 | 任丘 | 宁波 | 抚州 | 盐城 | 宿迁 | 海北 | 韶关 | 如皋 | 凉山 | 北海 | 山南 | 莱芜 | 湖北武汉 | 安岳 | 达州 | 海南海口 | 中山 | 咸宁 | 黔南 | 汉川 | 龙口 | 乐清 | 许昌 | 日照 | 宜宾 | 汉中 | 东莞 | 深圳 | 六安 | 达州 | 陕西西安 | 昭通 | 乌海 | 果洛 | 肇庆 | 东营 | 阳江 | 南安 | 梅州 | 锡林郭勒 | 贵州贵阳 | 台中 | 鹤壁 | 泰兴 | 宜昌 | 宿州 | 云南昆明 | 营口 |