oop patterns中文 - JavaScript類




learning design (8)

我理解基本的JavaScript偽類:

function Foo(bar) {
    this._bar = bar;
}

Foo.prototype.getBar = function() {
    return this._bar;
};

var foo = new Foo('bar');
alert(foo.getBar()); // 'bar'
alert(foo._bar); // 'bar'

我也理解模塊模式,它可以模擬封裝:

var Foo = (function() {
    var _bar;

    return {
        getBar: function() {
            return _bar;
        },
        setBar: function(bar) {
            _bar = bar;
        }
    };
})();

Foo.setBar('bar');
alert(Foo.getBar()); // 'bar'
alert(Foo._bar); // undefined

但是這兩種模式都有類似OOP的屬性。 前者不提供封裝。 後者不提供實例化。 可以修改這兩種模式以支持偽繼承。

我想知道的是,是否有任何模式允許:

  • 遺產
  • 封裝(支持“私有”屬性/方法)
  • 實例化(可以有多個“類”實例,每個實例都有自己的狀態)

Answers

Javascript肯定是OOP。 你總是有多態,但你必須犧牲封裝或實例化,這是你遇到的問題。

試試這個就可以了解你的選擇。 http://www.webmonkey.com/2010/02/make_oop_classes_in_javascript/也是我收藏的一個老問題: JavaScript面向對象嗎?


我認為你所尋找的是“揭示原型模式”。

Dan Wahlin有一篇很棒的博客文章: http://weblogs.asp.net/dwahlin/archive/2011/08/03/techniques-strategies-and-patterns-for-structuring-javascript-code-revealing-prototype-pattern.aspx ://weblogs.asp.net/dwahlin/archive/2011/08/03/techniques-strategies-and-patterns-for-structuring-javascript-code-revealing-prototype-pattern http://weblogs.asp.net/dwahlin/archive/2011/08/03/techniques-strategies-and-patterns-for-structuring-javascript-code-revealing-prototype-pattern.aspx

甚至更好的Pluralsight關於這個和其他相關JavaScript結構的課程: http://pluralsight.com/training/courses/TableOfContents?courseName=structuring-javascript&highlight=dan-wahlin_structuring-javascript-module1!dan-wahlin_structuring-javascript-module2!dan-wahlin_structuring-javascript-module5!dan-wahlin_structuring-javascript-module4!dan-wahlin_structuring-javascript-module3#structuring-javascript-module1 ://pluralsight.com/training/courses/TableOfContents?courseName = struct-javascript&highlight / = light -wahlin_structuring-javascript-module1!dan -wahlin_structuring -javascript-module2!dan http://pluralsight.com/training/courses/TableOfContents?courseName=structuring-javascript&highlight=dan-wahlin_structuring-javascript-module1!dan-wahlin_structuring-javascript-module2!dan-wahlin_structuring-javascript-module5!dan-wahlin_structuring-javascript-module4!dan-wahlin_structuring-javascript-module3#structuring-javascript-module1


ECMAScript 6中引入了JavaScript類,並且是JavaScript現有的基於原型的繼承的語法糖。 類語法沒有向JavaScript引入新的面向對象的繼承模型。 JavaScript類提供了更簡單,更清晰的語法來創建對象和處理繼承。

您可以在此鏈接中看到更多Mozilla社區

Github


那這個呢 :

var Foo = (function() {
    // "private" variables 
    var _bar;

    // constructor
    function Foo() {};

    // add the methods to the prototype so that all of the 
    // Foo instances can access the private static
    Foo.prototype.getBar = function() {
        return _bar;
    };
    Foo.prototype.setBar = function(bar) {
        _bar = bar;
    };

    return Foo;
})();

現在我們有實例化,封裝和繼承。
但是,仍然存在問題。 private變量是static因為它在所有Foo實例中共享。 快速演示:

var a = new Foo();
var b = new Foo();
a.setBar('a');
b.setBar('b');
alert(a.getBar()); // alerts 'b' :(    

更好的方法可能是使用私有變量的約定:任何私有變量都應該以下劃線開頭。 這個約定是眾所周知和廣泛使用的,所以當另一個程序員使用或改變你的代碼並看到以下劃線開頭的變量時,他會知道它是私有的,僅供內部使用,他不會修改它。
這是使用此約定的重寫:

var Foo = (function() {
    // constructor
    function Foo() {
        this._bar = "some value";
    };

    // add the methods to the prototype so that all of the 
    // Foo instances can access the private static
    Foo.prototype.getBar = function() {
        return this._bar;
    };
    Foo.prototype.setBar = function(bar) {
        this._bar = bar;
    };

    return Foo;
})();

現在我們有實例化,繼承,但是我們已經失去了封裝以支持約定:

var a = new Foo();
var b = new Foo();
a.setBar('a');
b.setBar('b');
alert(a.getBar()); // alerts 'a' :) 
alert(b.getBar()); // alerts 'b' :) 

但私人vars是可訪問的:

delete a._bar;
b._bar = null;
alert(a.getBar()); // alerts undefined :(
alert(b.getBar()); // alerts null :(

我最近在考慮這個特定主題以及各種方法的局限性。 我能夠提出的最佳解決方案如下。

它似乎解決了繼承,實例化和封裝的問題(至少從谷歌Chrome v.24的測試中解決),儘管可能以內存使用為代價。

function ParentClass(instanceProperty) {
  // private
  var _super = Object.create(null),
      privateProperty = "private " + instanceProperty;
  // public
  var api = Object.create(_super);
  api.constructor = this.constructor;
  api.publicMethod = function() {
     console.log( "publicMethod on ParentClass" );
     console.log( privateProperty );
  };
  api.publicMethod2 = function() {
     console.log( "publicMethod2 on ParentClass" );
     console.log( privateProperty );
  };
  return api;
}

function SubClass(instanceProperty) {
    // private
    var _super = ParentClass.call( this, instanceProperty ),
        privateProperty = "private sub " + instanceProperty;
    // public
    var api = Object.create(_super);
    api.constructor = this.constructor;
    api.publicMethod = function() {
       _super.publicMethod.call(this); // call method on ParentClass
        console.log( "publicMethod on SubClass" );
        console.log( privateProperty );
    }
    return api;
}

var par1 = new ParentClass(0),
    par2 = new ParentClass(1),
    sub1 = new SubClass(2),
    sub2 = new SubClass(3);

par1.publicMethod();
par2.publicMethod();
sub1.publicMethod();
sub2.publicMethod();
par1.publicMethod2();
par2.publicMethod2();
sub1.publicMethod2();
sub2.publicMethod2();

許多JS類的一個問題是它們不保護它們的字段和方法,這意味著使用它的任何人都可能意外地替換方法。 例如代碼:

function Class(){
    var name="Luis";
    var lName="Potter";
}

Class.prototype.changeName=function(){
    this.name="BOSS";
    console.log(this.name);
};

var test= new Class();
console.log(test.name);
test.name="ugly";
console.log(test.name);
test.changeName();
test.changeName=function(){
    console.log("replaced");
};
test.changeName();
test.changeName();

將輸出:

ugly
BOSS
replaced 
replaced 

如您所見,changeName函數被覆蓋。 以下代碼將保護類方法和字段,並且將使用getter和setter來訪問它們,使其更像是在其他語言中找到的“常規”類。

function Class(){
    var name="Luis";
    var lName="Potter";

    function getName(){
         console.log("called getter"); 
         return name;
    };

    function setName(val){
         console.log("called setter"); 
         name = val
    };

    function getLName(){
         return lName
    };

    function setLName(val){
        lName = val;
    };

    Object.defineProperties(this,{
        name:{
            get:getName, 
            set:setName, 
            enumerable:true, 
            configurable:false
        },
        lastName:{
            get:getLName, 
            set:setLName, 
            enumerable:true, 
            configurable:false
        }
    });
}

Class.prototype.changeName=function(){
    this.name="BOSS";
};   

Object.defineProperty(Class.prototype, "changeName", {
    writable:false, 
    configurable:false
});

var test= new Class();
console.log(test.name);
test.name="ugly";
console.log(test.name);
test.changeName();
test.changeName=function(){
    console.log("replaced")
};
test.changeName();
test.changeName();

這輸出:

called getter
Luis
called setter 
called getter 
ugly 
called setter 
called setter 
called setter 

現在,您的類方法不能被隨機值或函數替換,​​並且在嘗試讀取或寫入字段時,getter和setter中的代碼始終會運行。


此閉包允許實例化和封裝,但不允許繼承。

function Foo(){
    var _bar = "foo";

    return {
        getBar: function() {
            return _bar;
        },
        setBar: function(bar) {
            _bar = bar;
        }
    };
};

a = Foo();
b = Foo();

a.setBar("bar");
alert(a.getBar()); // "bar"
alert(b.getBar()); // "foo"

考慮創建一個沒有"regex"屬性的新對象,因為原始對象總是可以被程序的其他部分引用。因此你應該避免操縱它。

const myObject = {
    "ircEvent": "PRIVMSG",
    "method": "newURI",
    "regex": "^http://.*"
};

const { regex, ...newMyObject } = myObject;

console.log(newMyObject);





javascript oop design-patterns