问题 返回其自身实例的Object


背景: 我的最新项目不能使用一个让我很难过的大型图书馆。我希望从任何库中获得一些东西,比如缺少的功能 addClasshasClassremoveClass,兼容 addEventListener等等我创造了 一个小小的对象 在其他时间我想要一些意见,但是我在设置它时会遇到一些麻烦。

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

鉴于:

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

    return this;
};

console.log($());

我们得到DOMWindow而不是 $ 因为它的古怪性质 this 在JavaScript中。 对我来说更奇怪的是 console.log(new $().name) 正确地返回“levi”。如果 this 绑定到窗口,为什么对象正确获取值?。我们可以添加新的 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

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


10095
2018-05-21 22:34


起源

你不能用 任何 大型图书馆,还是不能用的 具体 大型库(jQuery)? - JK.
好的,我的朋友。它适用于非常特定的移动浏览器,而我的客户端不需要大型库。我很乐于在没有库的情况下使用JavaScript,但这些方法在这个项目中非常有用。 - Levi Morrison
更新了帖子以删除一些在我使用它们时没有正确含义的保留字。 - Levi Morrison


答案:


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

$ = 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 总之是 $

也许你应该将问题的标题改为:'返回的对象[构造函数] 自己的一个例子

也许 这篇博客文章 可以减轻光线。


4
2018-05-21 23:53



你的代码是“构建” this,即设置属性,即使它返回一个新实例。至于其他方面,你完全正确。 - Zecc
@Zecc,你说得对,应该首先检查实例。代码改编。 - KooiInc
你花了这么久的耻辱。我接受了自己使用这种技术的帖子,因为它不存在。 编辑: 决定删除自己的帖子来接受这个。 - Levi Morrison


jQuery的方式是首先测试是否 this 是 window (称为函数),如果是,则返回自身的新实例。例如:

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

console.log($());

这是有效的,因为当您正常调用函数时(func()this 将与来电者相同 this。 (相关但不相关: obj.method() 会有的 this 是 obj)因为默认范围是 windowthis 内 $ 将会 window 当你把它称为 $()

使用时调用函数 new,会发生什么是JavaScript创建一个新对象,然后调用您的函数 this 设置为该对象。

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


5
2018-05-21 22:47



在澄清之前我理解你的解决方案,但无论如何它们都很好。谢谢。 - Levi Morrison
上面的工作,你可以控制如何调用构造函数,但它可能会产生不良后果,其中函数可能与其他一些对象一起调用 this。这也意味着你可以不用调用该函数 新 并且它可能仍然是构造函数,但是其他不使用该模式的函数仍然需要 新。它还意味着您必须查看代码以确定该函数是否是构造函数。 - RobG
@RobG True,但正在使用 new 在那种情况下不会伤害任何东西。如果他们知道函数是构造函数,那么他们可以放弃 new。如果不这样做,不会造成伤害。 编辑: 我刚刚意识到我没有解决你的第一个陈述。我只是说你没有因使用而受到惩罚 new。 - Levi Morrison


>  $ = function() {
>     this.name = "levi";
> 
>     return this; };
> 
> console.log($());

我们得到DOMWindow而不是$

你打电话时 $ 作为一个功能,然后它 这个 关键字设置为全局对象,就像任何被调用的函数一样。

因为这个古怪的本质   在JavaScript中

JavaScript的 这个 关键字*按照指定的方式工作。它与其他语言不同,但这就是它的工作原理。

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

你打电话时 $ 作为一种功能,它 这个 keyword是全局对象,因此:

this.name = 'levi';

创建一个名为的全局对象的属性 名称 价值'levi'。当你知道发生了什么事时并不奇怪。 :-)

我们可以添加新的console.log(新的   $()),它的工作原理。

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

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

的console.log($());这给了我什么   我想要,但似乎有点不必要   将对象包装在一个   创造它的功能。进一步   更多,它是obj类型而不是$

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

这有什么其他方式   做了什么?

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


4
2018-05-21 22:56



如果你创建了一个属性 this,然后它将被分配给任何对象 this 引用。如果你了解如何 this 设置,然后您知道将在其上创建属性的对象。你的帖子中并不清楚这种理解。如果你的问题是真的 如何在不使用'new'的情况下将函数用作构造函数那就是你应该问的问题 - 我也回答了这个问题。 :-)哦, this 不 界 对于任何事情,它都是通过电话设定的。 - RobG


尝试这样的事情:

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

更新

@Levi Morrison

编辑:有没有人看到以下解决方案有任何问题:

既然你问过, 我并不完全爱上document.write
试试这个:

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

1
2018-05-21 23:04



JSLint说要避免使用arguments.callee。通过命名函数可以轻松解决这个问题。 - Levi Morrison
@Levi Morrison我只使用arguments.callee来更容易地重命名该函数。但我明白这是不赞成的。 - Zecc
我嘲笑document.write()异议。我只用它们来表明对象正常工作。它们实际上不是代码的一部分。 - Levi Morrison