声明Javascript对象的方法没有" new"关键词kosher?

时间:2016-12-08 18:00:52

标签: javascript javascript-objects

我在过去的几个月里一直在学习Javascript,有时会感觉我编写代码的速度比实际掌握语言的细微差别要快,所以请在这里请耐心等待。

无论如何,我的问题与我一直在使用的某种方法的行为有关。基本要点是能够设计可以构造和使用的对象,而无需使用" new"关键词。

以下是一个例子:

<!DOCTYPE html>
<html>
    <head>
        <script>

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

        example.prototype.test = function()
        {
            var
                result = example();
            if(result instanceof example)
            {
                console.log("Type: example");
                if(result === this)
                    console.log("Unexpected: (result === this)");    
                else
                    console.log("Expected: (result !== this)");    

            }
            else 
                console.log("Type: ?");
            return result;
        }    

        function run()
        {
            var
                one = new example(), 
                two = example(), 
                three = one.test(),
                four = two.test();    
            three.test();
            four.test();
        }

        </script>
    </head>
    <body onload = "run()">
    </body>
</html>

我得到的输出是:

Type: example
Expected: (result !== this)
Type: example
Expected: (result !== this)
Type: example
Expected: (result !== this)
Type: example
Expected: (result !== this)

......这正是我想要看到的。我的问题,特别是,它是否是:

(1)标准行为

(2)所有浏览器都支持

(3)容易出现任何不良副作用

2 个答案:

答案 0 :(得分:1)

你的模式

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

非常普遍且众所周知(是的,它是犹太洁食)。但它有一些缺点,主要围绕处理传递争论。它也是不必要的,JavaScript具有更高阶的功能,因此不需要为你编写的每个函数添加样板:

let wrapConstructor = fn => (...args) => {
  return new (fn.bind.apply(fn, [fn, ...args]));
};

let Foo = function Foo() {
  if (!(this instanceof Foo)) {
    throw new TypeError('Not a Foo...');
  }
};

let wrappedFoo = wrapConstructor(Foo);

let foo = wrappedFoo(); // doesn't throw

使用内置构造函数(如Date

)还有其他好处
let wrappedDate = wrapConstructor(Date);
let myDate = wrappedDate(2016, 11, 8); // today's date

就浏览器兼容性而言,显然它写得不是很好,但任何具有Function.prototype.bindFunction.prototype.apply(IE 9+)的环境都可以在一点点babel魔法之后运行。

至于它的标准行为,嗯,如果你的意思是符合规范,那肯定会。如果你的意思是这是一种常见的做法,那么,这取决于你运行的JavaScript群体。如果你倾向于使用很多高阶函数(比如我的小wrapConstructor实用程序),那么new就会有些痛苦,需要避免。

对于包含此类功能的库,请查看ramda

更新

另请注意,您可以轻松地将这两种模式组合在一起,只需稍微修改一下帮助程序:

let construct = (fn, args) => {
  return new (fn.bind.apply(fn, [fn, ...args]));
};

function Foo() {
  if (!(this instanceof Foo)) {
    return construct(Foo, Array.from(arguments));
  }
}

现在可以使用或不使用new调用Foo,并且函数的参数会自动传递,而不必提前知道有多少。此方法适用于任何arity的构造函数,包括可变构造函数。

答案 1 :(得分:0)

从技术角度来看,它有效。但是你应该小心改变预期的语义,除非你有令人信服的理由,在这种情况下(没有双关语意),我认为你没有。特别是如果你的代码被其他开发人员使用 - 最好坚持使用约定,比如使构造函数大写,而不是常规函数。

如果未使用throw调用此功能,您是否只会new出现错误?