“JavaScript:The Good parts”一书中对“功能模式”的解释

时间:2016-12-13 04:32:55

标签: javascript

这些天我正在学习JS,我无法在the book的第52页上吸收这种功能模式。

  

功能

     

到目前为止,我们看到的继承模式的一个弱点是我们没有隐私。   对象的所有属性都是可见的。我们没有私有变量,也没有   私人方法。有时这无关紧要,但有时它很重要。令人沮丧的是,一些不知情的程序员采用了假装的模式   隐私的。如果他们有一个他们希望私有的属性,他们给它一个奇怪的名字,希望代码的其他用户会假装他们不能   看到看起来很奇怪的成员。幸运的是,我们有更好的选择   模块模式的应用。

     

我们首先创建一个会产生对象的函数。我们会给它一个名字   以小写字母开头,因为它不需要使用新的前缀。该   功能包含四个步骤:

     
      
  1. 它会创建一个新对象。有很多方法可以制作一个物体。它可以成为一个   object literal,或者它可以使用新前缀调用构造函数,或者它可以   使用Object.beget方法从现有对象生成新实例,或   它可以调用任何返回对象的函数。
  2.   
  3. 它可选地定义私有实例变量和方法。这些只是普通的   vars of the function。
  4.   
  5. 它用方法扩充了这个新对象。这些方法将具有特权   访问第二步中定义的参数和变量。
  6.   
  7. 返回该新对象。
  8.         

      这是一个功能构造函数的伪代码模板(添加了粗体文本   强调):

    var constructor = function (spec, my) {
       var that, //other private instance variables;
    
       my = my || {};
    
       // Add shared variables and functions to my 
    
       that = a new object; 
    
       // Add privileged methods to that 
    
       return that;
    }
         

    spec对象包含构造函数需要创建的所有信息   实例。 spec 的内容可以复制到私有变量中或转换   通过其他功能。或者方法可以从 spec 中访问信息   需要它。 (简化是用单个值替换spec。这在以下情况下很有用   正在构造的对象不需要整个 spec 对象。)

任何人都可以解释,那里发生了什么(以外行人的话说)以及这种模式有用的地方?

3 个答案:

答案 0 :(得分:2)

注意:虽然你所提到的这本书确实是一本非常有用的书,但却非常古老。在最新版本的JavaScript中,一些“好”(甚至“坏”)部分已被更好的替代品和功能所取代。

  

到目前为止,我们看到的继承模式的一个弱点是   我们没有隐私。对象的所有属性都是可见的。我们没有   私有变量,没有私有方法。

Javascript对象具有“属性”,可以是其他对象或函数。考虑:

var obj =  {a: 1, do: function(){console.log('done');} }

没有什么能阻止您拨打obj.a = 5obj.done()

但有人可能反驳说,这不是创建对象的好方法。我们最好有一个原型或类,我们可以从中创建新实例:

function Animal(name) {
   this._name = name;
}

Animal.prototype.print = function(){console.log(this._name)};

或更新的JavaScript版本:

class Animal {
    constructor(name){
     this._name = name;
    }

    print(){
       console.log(this._name);
    }

 }
  

令人沮丧的是,一些不知情的程序员采用了一种模式   假装隐私。如果他们有自己想要的财产   私人,他们给它一个奇怪的名字,希望其他   代码的用户会假装他们看不到奇怪的样子   成员。

这是对上述代码的评论。在声明JavaScript类或函数时,没有官方的,标准的,“傻瓜证明和语法优雅”保持实例变量私有的方式。也就是说,一种简单,干净的方式来声明一个只能在该类或原型(See this answer)中定义的方法访问的变量。因此,人们遵循一些商定的模式,其中一个模式为变量名称添加前缀_。这实际上没有为类实例的内部变量提供隐私。

随着module system的出现,人们可以在单独的文件/容器中编写JavaScript代码,并选择仅对外部世界可见的特定对象。一个CommonJS例子:

Animal.js:

const props = new WeakMap();

class Animal {
   constructor(name){
     props.set(this,{});
     props.get(this).name = name;
   }

   set age(n){
     props.get(this).age = age;
   }

   function print(){
     console.log(props.get(this));
   }
}

module.exports = Animal;

上面是其中一个声明具有私有属性的类的方法,除非故意泄露,否则无法从外部访问。注意对象props如何不导出到外部世界。

  

幸运的是,我们在应用程序中有更好的选择   模块模式。

您可能认为上述模块代码实际上是本文的含义,但上述实现是使用最新功能的较新版本。文本中的点的旧学校方式是暴露对象创建者(a.k.a工厂)功能。在创建者函数内部和创建对象外部声明的所有内容都是设计私有的:

function createAnimal(name){
   var age = 0;
   var animal = {};

   animal.setAge = function(a){age = a;};
   animal.getName = function(){return name;};
   animal.print = function(){console.log({'name':name,'age':age});};
}

这里的继承是对超级创建者的调用并修改超级实例:

function createDog(name, color){
   var breed = 'unknown';
   var dog = createAnimal(name);

   dog.setBreed = function(b){breed = b;};
}

答案 1 :(得分:0)

基本上,这个想法是隐藏隐藏中的私有变量。被关闭的变量是(未示出)“其他私有实例变量”,实际关闭这些变量的方法是(也未示出)“特权方法”。

例如,使用此功能:

var createBoard = function (rows, cols) {
    var cells = [];
    var board = {};

    for (var i = 0; i < rows; i++) {
        cells[i] = [];
        for (var j = 0; j < cols; j++) {
            cells[i][j] = { player: null };
        }
    }

    board.play = function (row, col, player) {
        if (cells[row][col].player === null) {
            cells[row][col].player = player;
        }
    };

    board.getPlayer = function (row, col) {
        return cells[row][col].player;
    };

    return board;
};

让我们假设我们调用此函数来创建一个8x8游戏板:

var board = createBoard(8,8);
board.play(1,2,"Player1");
console.log(board.getPlayer(1,2));
board.play(1,2,"Player2"); // Doesn't actually do anything
// Note this code has no direct access to cells[][] except via the two
// methods we defined on the board object.

在这种情况下,我们返回一个棋盘对象。董事会内部可以访问一个单元格数组,但我们不会让任何人修改它,除非使用我们的两种方法,play(只有在以前没有占用时占用一个板空间)和一个getPlayer方法返回给定的给定空间。 Cells [] []对此代码的用户完全隐藏 - 他们无法通过直接更改我们的单元格来作弊。

答案 2 :(得分:0)

  

JavaScript是一种基于原型的基于对象的语言,而不是基于类的。 1

将其与基于PHP,Java等类的面向对象语言进行比较。在这些语言中,可以定义,并且成员变量可以在类的内部和外部具有各种可见性。例如,PHP的visibility设置为三个级别: public protected private

  

声明为public的类成员可以随处访问。声明受保护的成员只能在类本身和继承的类中访问。声明为私有的成员只能由定义该成员的类访问。 2

class MyClass {
    public $public = 'Public';
    protected $protected = 'Protected';
    private $private = 'Private';
    function printHello() {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}
$obj = new MyClass();
echo $obj->public; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error

但是在JavaScript中,我们并没有真正拥有私有变量的概念(在同一意义上)。这就是作者在描述模块模式时所谈论的内容。

因此,如果我们想在Javascript中创建一个类似的构造,我们可以做这样的事情:

var MyClass = function (rows, cols) {
    //this could be used in prototype functions
    var private = 'Private';
    var members = {
        public: 'Public';
        getHello: function() {
             return 'MyClass _ ' + private;
        }
    };
    return members;
};