我不了解Crockford的JavaScript:前进之路

rid*_*elf 16 javascript object

在一篇名为"未来之路"的演讲中,道格拉斯·克罗克福德表示,他不再在他的JavaScript中使用"新",而是将自己从"这个"中解脱出来.他说JavaScript的优点不是原型继承,而是无类继承.他提供了以下片段,这是他如何定义他的对象:

function constructor(init) {
    var that = other_constructor(init),
        member,
        method = function () {
            // init, member, method
        };
    that.method = method;
    return that;
}
Run Code Online (Sandbox Code Playgroud)

他解释说:


我有一个函数,它将需要一些值来初始化它.我建议将它作为一个对象,(这样你就可以使用JSON文本来创建新实例).如果你想继承它的东西,你可以调用另一个构造函数; 无论如何,你要创建一个对象并将其放在一个名为"that"的变量中.您将创建所有成员变量(将成为对象的属性,方法变量的属性,将作用于对象的事物......)这些方法将是将关闭初始化值的函数覆盖所有成员变量和所有方法变量.

它也可以使用"this"和"that",但我建议不要.的理由,否则即是,如果不使用"这个"或"是",那么你可以采取任何的功能出来的对象和independantly打电话给他们,他们还是做同样的事情.这样可以提高语言的可靠性.也意味着你可以传递任何这些东西,使用它作为回调而不必绑定任何东西(它变得更可靠).然后,任何需要公开或特权的方法只需将它们附加到对象并返回对象.这是一个非常灵活的模式 - 你可以获得多重继承,你可以获得方面,你可以获得工厂 - 你可以用这个基本模式做很多事情.


为什么此代码段会进行以下分配?

that.method = method
Run Code Online (Sandbox Code Playgroud)

方法有什么用途?它是如何初始化的?以这种方式声明对象的目的是什么?

他的演讲非常笼统,他只花了一分钟时间来讨论这部分内容而没有详细说明他的推理或引用资源.任何人都可以阐明这种编程风格的动机吗?

视频链接:https: //www.youtube.com/watch?v = 3WgVHE5Augc约37分钟开始

T.J*_*der 22

为什么此代码段会进行以下分配?

that.method = method
Run Code Online (Sandbox Code Playgroud)

有什么用途method?它是如何初始化的?

它被初始化为一个变量,进一步向上几行:

method = function () { ... }
Run Code Online (Sandbox Code Playgroud)

然后,您引用的行是将该变量的值(对函数的引用)赋给对象that引用的属性,因此它可以用作对象的"方法".所以你可以这样做:

var x = constructor(42);
x.method(); // <== Here's where we use the function assigned to the property as a method
Run Code Online (Sandbox Code Playgroud)

更多(在我的博客上):神话方法

以这种方式声明对象的目的是什么?

Crockford不喜欢JavaScript的构造函数,因此不使用它们.所以他这样做了.关于JavaScript的一个非常棒的事情是它的灵活性.你可以使用它作为一个几乎纯粹的功能性语言,你可以使用它作为一个典型的语言,你甚至可以用它很多喜欢,即使它不是基于类的,因为它的原型功能提供一个基于类的语言所需的一切(从ES2015 +开始;在ES2015之前,几乎所有东西都是如此).只要您认为适合这样做,您就可以混合使用这些方法.它就是那么灵活.

与Crockford不同,我喜欢构造函数和new关键字.但是我也非常喜欢在它们不是正确的工具时不必使用它们,而它们通常不是.


你的评论如下:

您是否有机会在实际使用中提供Crockford构造函数代码段的示例?那个方法用来初始化那个成员,还是我完全不在这里?

没有that.member.member在你展示的代码中,它不是对象的属性,它只是一个变量.在method调用中创建的函数可以constructor访问该变量,因为它是对调用上下文的闭包constructor,但是只有访问返回对象的任何东西都可以看到member.因此对于在其中创建的功能member真正私密constructor.还有两篇可能在这里有用的文章:闭包并不复杂,我的博客解释了什么是"闭包",而Crockford的私有成员用JavaScript描述了私有信息与他在示例中使用的对象相关联的方式引.(我在这个答案的最后提到了一种不同的方式来获取有关对象的私人信息.)

你引用的例子是展示两个很大程度上无关的东西:

  1. 一种创建增强(非衍生)对象的方法

  2. 具有与这些对象相关联的真正私人信息的手段

他展示的模式并不是做这些事情的唯一方式,但这就是它所展现的.

举一个具体的例子:

假设我们想要创建"东西"对象.它们的含义并不重要,但它们有一个名为"name"的属性(不必是私有的).(他们可能也有方法,但那些并不重要,所以为了简洁和清晰,我们会把它们留下来.)所以我们从这样的事情开始:

// Very simple Crockford-style constructor
function createThing(name) {
    return {name: name}; // Again, there'd probably be more to it, this is simple on purpose
}

// Usage
var t = createThing("foo");
console.log(t.name); // "foo"
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.现在,我们希望能够创建可以添加计数器的东西,以及"使用"事物并计算使用的方法,返回新的使用次数.(是的,这一个人为的例子.)一个天真的版本,再次使用类似于Crockford这样做的方式,可能看起来像这样:

// Naive approach
function createThingWithCounter(name) {
    var that = createThing(name);
    that.useCounter = 0;
    that.use = function() {
        // ...do something with `that`...

        // Return the new number of times we've "used" the thing
        return ++that.useCounter;
    };
    return that;
}

// Usage
var t = createThingWithCounter("foo");
console.log(t.name);  // "foo"
console.log(t.use()); // 1
console.log(t.use()); // 2
Run Code Online (Sandbox Code Playgroud)

再次,到目前为止一切顺利.但问题是,useCounter对象是公共财产.所以我们可以从createThingWithCounter代码之外搞乱它:

var t = createThingWithCounter("foo");
console.log(t.name);  // "foo"
console.log(t.use()); // 1
t.useCounter = 0;
console.log(t.use()); // 1 -- uh oh!
Run Code Online (Sandbox Code Playgroud)

我们不想useCounter公开.现在,有各种方法可以将其设为私有,包括根本不使用私有,而是使用命名约定(通常以下划线开头,例如_useCounter),意思是" 不管这个或其他!",但我们正在寻找的模式at让我们通过利用这个方法是对调用的上下文的闭包这一事实来实现useCounter 真正的私有.加上一些重新排列源代码以更好地适应引用的模式,这给了我们:usecreateThingWithCounter

function createThingWithCounter(name) {
    var that = createThing(name),
        useCounter = 0,
        use = function() {
            // ...do something with `that`...

            // Return the new number of times we've "used" the thing
            return ++useCounter;
        };
    that.use = use;
    return that;
}
Run Code Online (Sandbox Code Playgroud)

现在,useCounter根本不是对象的属性.它是真正私密的,除了createThingWithCounter可以看到或改变它之外没有任何东西:

var t = createThingWithCounter("foo");
console.log(t.name);  // "foo"
console.log(t.use()); // 1
t.useCounter = 0;     // <== Has absolutely no effect on the real counter
console.log(t.use()); // 2
Run Code Online (Sandbox Code Playgroud)

这就是我们具体的(如果做出的)例子.以下是它映射到引用模式的方式:

  • constructor = createThingWithCounter
  • otherConstructor = createThing
  • member = useCounter
  • method = use

现在,我想强调的是,上面没有任何内容可以new代替使用普通的构造函数.它甚至看起来不同:

// Doing the same thing with normal constructor functions and `new`
function Thing(name) {
    this.name = name;
}

// Usage
var t = new Thing("foo");
console.log(t.name); // "foo"

// Augmented things
function ThingWithCounter(name) {
    var useCounter = 0;

    Thing.call(this, name);
    this.use = function() {
        // ...do something with `this`...

        // Return the new number of times we've "used" the thing
        return ++useCounter;
    };
}

// Usage of augmented things
var t = new ThingWithCounter("foo");
console.log(t.name);  // "foo"
console.log(t.use()); // 1
t.useCounter = 0;     // <== Has absolutely no effect on the real counter
console.log(t.use()); // 2
Run Code Online (Sandbox Code Playgroud)

他们只是实现类似目标的不同方式.

还有另一种方式:推导,而不是增加.和扩充一样,推导可以通过Crockford风格或通过标准构造函数完成.这又是语言的灵活性.:-)

关于私人信息的最后一点:在上面的两种风格中,它useCounter都是真正的私有,但它不是对象的属性.它根本不在对象上.我们如何获得隐私会产生一些成本:首先,我们必须为每个实例创建一个use函数.这些函数不会像附加到原型的函数(以及其他各种方式)那样在实例之间共享.2014年的成本相当低,现代发动机在优化时非常聪明; 在15年左右的时间里会花费更多的成本.

另一个代价是该use函数不能在其他地方重用,这使得它与JavaScript中的绝大多数函数不一致.如果您查看规范,您会在几乎所有预定义的JavaScript方法上看到这种语言:

注意:xyz函数是故意通用的; 它不要求它的这个值是一个Whatsit对象.因此,它可以转移到其他类型的对象以用作方法.

所以,如果我有什么事情,是很多一个数组,但不是一个数组(如JavaScript的arguments对象),我可以把从方法Array.prototype上,并提供我的目标是阵列状不够,他们会工作得很好:

var arrayLikeObject = {
    length: 2,
    0: "one",
    1: "two",
    indexOf: Array.prototype.indexOf
};
console.log(arrayLikeObject.indexOf("two")); // 1
Run Code Online (Sandbox Code Playgroud)

我们的use方法不能以这种方式重用.它被锁定在与之相关的"与计数器有关的事物"的实例上.如果我们确实试图以这种方式使用它,我们最终会在我们所使用的对象与我们从中获取的原始"具有计数器的对象"之间进行奇怪的串扰.这种奇怪的串扰是在任何规模的项目中产生真正令人不愉快,耗时,笨拙的错误的方法.

JavaScript标准的下一个版本ECMAScript6为我们提供了一种拥有私有属性的方法.也就是说,私有对象(或信息私有的实际属性在现代语言/环境中获得).因为属性实际上是对象的属性,所以我们不必依赖use函数作为闭包,我们可以在原型对象上定义它和/或在其他对象上重用它 - 也就是说,上面的成本都不是适用.

更好的是,他们用来添加该功能的非常聪明的模式现在可以用来获得它提供的约90%的好处.因此,如果这是您感兴趣的话题,最后一篇博文:ES6中的私有属性 - 以及ES3和ES5.

  • 我喜欢你博客上的这篇文章,它非常具有启发性,并且以非常意想不到的方式与我的问题相关.谢谢! (2认同)