【问题标题】:An Object that returns an instance of itself返回自身实例的对象
【发布时间】:2011-05-21 22:34:13
【问题描述】:

背景:我最近的项目不能使用大型库,这让我很伤心。我想从任何库中获得一些东西,例如缺少的函数addClasshasClassremoveClass、兼容的addEventListener 等。所以我创建了a little object,我想要一些其他时间的意见,但我在设置它时遇到了一些麻烦。

为了使用方便,我希望一个对象在创建时返回它自己的一个新实例。

给定:

 $ = function() {
    this.name = "levi";

    return this;
};

console.log($());

由于 JavaScript 中 this 的古怪性质,我们得到 DOMWindow 而不是 $对我来说更奇怪的是console.log(new $().name) 正确返回“levi”。如果this 绑定到window,为什么对象正确获取了值?。我们可以添加新的console.log(new $()) 并且它可以工作。但是,我不想每次都写新的。所以我尝试了:

$ = function() {
    var obj = function() {
        this.name = "levi";
    };

    return new obj();
};

console.log($());

这给了我想要的东西,但是将对象包装在创建它的函数中似乎有点不必要。此外,返回的对象是obj 而不是$。比较测试会失败。

还有哪些其他方法可以做到这一点?有没有更优雅的解决方案?我毫不犹豫地重新思考我的整个过程。我认为自己很擅长使用 JavaScript,但创建新的 JavaScript 是我非常陌生的事情。


有人认为以下解决方案有什么问题吗?

$a = function() {};

$ = function() {
    if (!(this instanceof $)) {
        return new $();
    }

    this.name = "levi";

    return this;
};

//helper function
var log = function(message) {
    document.write((message ? message : '') + "<br/>");
};

log("$().name == window.name: " + ($().name == window.name)); //false
log("$().name: " + $().name); //levi
log("window.name: " + window.name); //result

log();

log("$a instanceof $: " + ($a instanceof $)); //false
log("typeof $a: " + (typeof $a)); //function
log("typeof $: " + (typeof $)); //function

它似乎在我的所有测试中都有效。

【问题讨论】:

  • 您不能使用任何大型库,或者不能使用特定大型库(jQuery)?
  • 任何,我的朋友。它适用于非常特定的移动浏览器,我的客户不想要大型库。我对 JavaScript 很熟悉,可以在没有库的情况下工作,但是这几个方法在这个项目中非常有用。
  • 更新了帖子,删除了一些在我的使用中没有正确含义的保留字。

标签: javascript library-design


【解决方案1】:

做你想做的最简单的方法是(我认为):

$ = function(){
    if (!(this instanceof $)){
     return new $;
    }
    this.name = 'levi'; 
    return this;
}

仅返回 this 并不会创建 $ 实例的事实是因为 this 的创建方式将 $ 作为常规函数执行:在这种情况下,this 的值指向全局对象(在浏览器中:window,实际上调用执行$()window.$() 相同)。可以这么说,这是 javascript 生活的一个事实。 console.log(new $().name) 显示正确值的事实是因为您将函数作为构造函数调用,它返回该构造函数的实例(即 $ 的新实例)。但是console.log($().name) 也会打印'levi',因为它返回具有属性name 的全局对象,即window.name。试试$(); console.log(name) 你会看到name 现在是一个全局变量。因此,如果您不想每次都使用 new 关键字,请检查您的函数是作为常规函数调用的,还是作为构造函数中的实例 (=== instanceof $) 的构造函数调用的。使用上述方法,实例构造函数,无论是否使用new 实例化,都将始终为$

也许您应该将问题的标题改写为:“返回自身实例的对象 [构造函数]

也许this blog entry 可以提供额外的亮点。

【讨论】:

  • 您的代码正在“构造”this,即设置属性,即使它返回一个新实例。至于其余的,你是完全正确的。
  • @Zecc,你是对的,应该首先进行 instanceof 检查。代码改编。
  • 很遗憾你花了这么长时间。我接受了我自己使用这种技术的帖子,因为它不存在。 编辑:决定删除自己的帖子以接受这个。
【解决方案2】:

jQuery 的做法是首先测试this 是否为window(作为函数调用),如果是,则返回其自身的一个新实例。例如:

var $ = function() {
    if(this === window) {
        return new $();
    }
    this.name = "levi";
    return this;
};

console.log($());

这是有效的,因为当您正常调用函数时 (func()),this 将与调用者的 this 相同。 (相关但并非不相关:obj.method() 将具有 thisobj)由于默认范围是 windowthis$ 内将是 window,当您将其称为 $()

当您使用 new 调用函数时,JavaScript 会创建一个新对象,然后调用您的函数并将 this 设置为该对象。

此解决方案之所以有效,是因为它首先测试this 是否为window,因此被称为$()。如果它被称为$(),它将返回new $()。否则,它会使用new $() 调用,并且会按预期工作。

【讨论】:

  • 在您澄清之前我已了解您的解决方案,但无论如何它们都是很好的注释。谢谢。
  • 上述方法适用于您可以控制如何调用构造函数的情况,但如果函数可能与this 的其他对象一起调用,则可能会产生不良后果。这也意味着您可以在没有 new 的情况下调用该函数,并且它可能仍然是构造函数,但其​​他不采用该模式的函数仍然需要 new。这也意味着您必须查看代码以确定函数是否为构造函数。
  • @RobG 是的,但在这种情况下使用new 不会造成任何伤害。如果他们知道该函数是构造函数,那么他们可以删除new。如果他们不这样做,就不会造成伤害。 编辑: 我刚刚意识到我没有解决你的第一句话。我只是说你不会因为使用new而受到惩罚。
【解决方案3】:
>  $ = function() {
>     this.name = "levi";
> 
>     return this; };
> 
> console.log($());

我们得到的是 DOMWindow 而不是 $

当您将 $ 作为函数调用时,它的 this 关键字将设置为全局对象,就像任何这样调用的函数一样。

因为它的古怪性质 在 JavaScript 中

Javascript 的 this 关键字* 按照指定的方式工作。它与其他语言不同,但它就是这样工作的。

对我来说更奇怪的是 console.log($().name) 正确返回 “列维”。

当你把$作为函数调用时,它的this关键字就是全局对象,所以:

this.name = 'levi';

创建名为 name 的全局对象的属性,其值为“levi”。当您知道发生了什么时,这并不奇怪。 :-)

我们可以添加新的 console.log(new $()) 并且它有效。

这就是构造函数在 javascript 中的调用方式。当一个函数被 new 调用时,它的 this 关键字被设置为一个新构造的对象,所以this.name 将创建该对象的一个​​新属性。顺便说一句,return this 是多余的,构造函数默认返回 this

> $ = function() {
>     var obj = function() {
>         this.name = "levi";
>     };
> 
>     return new obj(); };

console.log($());这给了我什么 我想要,但似乎有点不必要 将对象包裹在 a 创建它的函数。更远 而且,它是 obj 类型而不是 $ 类型

大概您正在使用 typeof,它只能返回 ECMA-262 指定的值之一。该简短列表(包括对象、数字、字符串等)不包括 $

还有哪些其他方法可以做到这一点 完成了吗?

您可以使用您已经发现的方法,Lasse Reichstein Nielsen 的克隆(也被 Doublas Crockford 推广为“beget”)和 Richard Cornford 的模块模式。使用 Google,关于以上所有内容的帖子有很多很多。

【讨论】:

  • 如果您创建了this 的属性,那么它将分配给this 引用的任何对象。如果您了解this 的设置方式,那么您就知道将在哪个对象上创建该属性。在你的帖子中,这种理解并不清楚。如果你的问题真的是如何在不使用'new'的情况下使用函数作为构造函数,那么这就是你应该问的问题——我也回答了这个问题。 :-) 哦,this 没有绑定任何东西,它是由调用设置的。
【解决方案4】:

试试这样的:

function $(name){
    if( !(this instanceof arguments.callee) ){
        return new arguments.callee(name);
    }
    this.name = name;
    return this;
}

更新

@李维·莫里森

编辑:是否有人认为以下解决方案有任何问题:

既然你问了,I'm not exactly in love with document.write.
试试这个:

var log = function(message){
    if(message){
        document.body.appendChild(document.createTextNode(message));
    }
    document.body.appendChild(document.createElement('br'));
}

【讨论】:

  • JSLint 说要避免 arguments.callee。这很容易通过命名函数来解决。
  • @Levi Morrison 我只使用了 arguments.callee 以便更轻松地重命名函数。但我知道这是不受欢迎的。
  • 我嘲笑 document.write() 反对。我只是用它们来证明这些对象工作正常。它们实际上并不是代码的一部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多