11

背景:我最近的项目不能使用大型库,这让我很伤心。我想从任何库中获得一些东西,例如缺少的函数、、、addClass兼容等。所以我创建了一个小对象,我想在其他时间发表一些意见,但我有一个设置它的方式有点麻烦。hasClassremoveClassaddEventListener

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

鉴于:

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

    return this;
};

console.log($());

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

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

    return new obj();
};

console.log($());

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

还有哪些其他方法可以做到这一点?有没有更优雅的解决方案?我毫不犹豫地重新思考我的整个过程。我认为自己非常擅长使用 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

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

4

4 回答 4

6

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

$ = 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都将始终是$

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

也许这个博客条目可以提供额外的信息。

于 2011-05-21T23:53:19.053 回答
5

jQuery 这样做的方式是首先测试 if thisis 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设置为该对象。

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

于 2011-05-21T22:47:14.333 回答
4
>  $ = function() {
>     this.name = "levi";
> 
>     return this; };
> 
> console.log($());

我们得到 DOMWindow 而不是 $

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

由于 JavaScript 中 this 的古怪性质

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

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

当您将$作为函数调用时,它的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(); };

控制台.log($()); 这给了我想要的东西,但是将对象包装在创建它的函数中似乎有点不必要。此外,它是 obj 类型而不是 $ 类型

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

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

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

于 2011-05-21T22:56:52.073 回答
1

尝试这样的事情:

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

更新

@李维莫里森

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

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

var log = function(message){
    if(message){
        document.body.appendChild(document.createTextNode(message));
    }
    document.body.appendChild(document.createElement('br'));
}
于 2011-05-21T23:04:21.510 回答