javascript - 在没有 "new"关键字 kosher 的情况下声明 Javascript 对象的方法?

标签 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) 容易产生任何不良副作用

最佳答案

你的模式

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,并且函数的参数会自动传递,而无需提前知道有多少。此方法适用于任何数量的构造函数,包括可变参数构造函数。

关于javascript - 在没有 "new"关键字 kosher 的情况下声明 Javascript 对象的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41045916/

相关文章:

javascript - 组合 JSON 请求的结果

javascript - 使用点表示法访问对象的数字属性

Javascript fetch() 不止一次 ping 我的 golang 休息端点?

javascript - 在 javascript 代码中排列多个类

javascript - 检查对象是否是构造函数 - IsConstructor

javascript - jQuery 方法抛出新错误 - 未被外部 try/catch block 捕获

javascript - 从 service worker 访问 localStorage

javascript - 在哪里可以找到有关 DFP 广告管理系统代码和参数的详细信息

javascript - 在 JavaScript 中将值数组转换为对象数组(键值)对

javascript - 在 React 中渲染嵌套导航