令人惊讶的实例化模式——用于包装函数的 new 关键字
Surprising instantiation pattern - new keyword used on a wrapper function
在 Paper.js
库上工作时,我在代码库中发现了一种实例化我以前从未使用过的 class 的方法。它用于例如 here 实例化 Shape
class.
这个实例化模式可以简化为:给定一个 Ball
class 和一个实例化它的 createBall()
方法:
function Ball() {}
function createBall() {return new Ball()}
当然,我们可以通过调用获得球实例:
var ball = createBall();
但更令人惊讶的是,我们还可以通过调用(注意 new
关键字)获得球实例:
var ball = new createBall();
或者更抽象的方式:
var ball = new function() {return new Ball()};
由于 createBall()
returns 一个 Ball
实例,它给人的印象是我们正在使用 new
实例化 Ball
class它的一个实例上的关键字。
但是正如我们在下面的代码中看到的那样,这是不允许的,如果我们手动执行会抛出错误:
var ball1 = new Ball();
var ball2 = new ball1();
// error: ball1 is not a constructor
谁能给我解释一下这背后的逻辑是什么?
下面是不同实例化方式的对比示例:
// Class
function Ball() {}
// Method creating an instance of the class
function createBall() {
// Instantiate the class
var ball = new Ball();
// Return the instance
return ball;
}
// Expected: instantiating directly works
var ball1 = new Ball();
console.log('ball1', ball1 instanceof Ball); // outputs true
// Expected: instantiating through the creation method works
var ball2 = createBall();
console.log('ball2', ball2 instanceof Ball); // outputs true
// Unexpected: instantiating like this surprisingly works
var ball3 = new createBall();
console.log('ball3', ball3 instanceof Ball); // outputs true
// Expected: instantiating like this throws an error
var ball4 = new ball1();
// error: ball1 is not a constructor
编辑
看完@robert-zigmond的评论后,我发现了另一个误导案例:
function Dog() {}
function Ball() {
return new Dog();
}
var instance = new Ball();
console.log('is instance a Ball ?', instance instanceof Ball); // false
console.log('is instance a Dog ?', instance instanceof Dog); // true
ball1
甚至都不是一个函数,所以这就是你在那里得到错误的原因。
Javascript 中的任何函数都可以使用 new 运算符调用 - 本质上是构造一个新对象,该对象被视为函数的 this
引用,returns 它(但前提是该函数还没有 return 一个对象——如果是,构造的对象将被丢弃)。
请参阅此处了解 new
运算符的具体作用:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new#Description
你的 "other misleading case" 起初看起来确实令人困惑,因为 instance
被定义为 new Ball()
但 instance instanceof Ball
return 是 false
。这是因为,正如我上面所说,"constructor function" 只有 return 是新构造的对象,否则 return 是一个根本不是对象的值。在大多数设计用作构造函数的函数中,实际上没有显式的 return 值 - 所以 return 值隐含地是 undefined
,因此新构造的对象是 returned.
但在这个例子中,函数将 return 一个对象,它是 Dog
的一个实例 - 所以这个对象最终成为 instance
。虽然是通过 new Ball()
创建的,但有些间接,它 实际上是 通过调用 new Dog()
创建的,因此 Dog.prototype
在其原型链中,并且不是 Ball.prototype
。这解释了您观察到的 instanceof
运算符的行为。
当 new Function
被调用时
- 创建了一个新对象,继承自Function.prototype
- 调用构造函数并将其绑定到第一步创建的新对象。
- 如果没有 returned,这个对象将被 returned 或者你可以 return 像你的例子那样的新对象
function createBall() {
console.log('origin this', this);
return new Ball();
}
当调用 new createBall()
时,它会创建一个继承自 createFoo.prototype this
的新对象,如果没有 returned,则此对象成为 new createBall()
的结果但是这里你 return 一个 new Ball()
,所以 this
对象更改为 new Foo()
和 returned
在 Paper.js
库上工作时,我在代码库中发现了一种实例化我以前从未使用过的 class 的方法。它用于例如 here 实例化 Shape
class.
这个实例化模式可以简化为:给定一个 Ball
class 和一个实例化它的 createBall()
方法:
function Ball() {}
function createBall() {return new Ball()}
当然,我们可以通过调用获得球实例:
var ball = createBall();
但更令人惊讶的是,我们还可以通过调用(注意 new
关键字)获得球实例:
var ball = new createBall();
或者更抽象的方式:
var ball = new function() {return new Ball()};
由于 createBall()
returns 一个 Ball
实例,它给人的印象是我们正在使用 new
实例化 Ball
class它的一个实例上的关键字。
但是正如我们在下面的代码中看到的那样,这是不允许的,如果我们手动执行会抛出错误:
var ball1 = new Ball();
var ball2 = new ball1();
// error: ball1 is not a constructor
谁能给我解释一下这背后的逻辑是什么?
下面是不同实例化方式的对比示例:
// Class
function Ball() {}
// Method creating an instance of the class
function createBall() {
// Instantiate the class
var ball = new Ball();
// Return the instance
return ball;
}
// Expected: instantiating directly works
var ball1 = new Ball();
console.log('ball1', ball1 instanceof Ball); // outputs true
// Expected: instantiating through the creation method works
var ball2 = createBall();
console.log('ball2', ball2 instanceof Ball); // outputs true
// Unexpected: instantiating like this surprisingly works
var ball3 = new createBall();
console.log('ball3', ball3 instanceof Ball); // outputs true
// Expected: instantiating like this throws an error
var ball4 = new ball1();
// error: ball1 is not a constructor
编辑
看完@robert-zigmond的评论后,我发现了另一个误导案例:
function Dog() {}
function Ball() {
return new Dog();
}
var instance = new Ball();
console.log('is instance a Ball ?', instance instanceof Ball); // false
console.log('is instance a Dog ?', instance instanceof Dog); // true
ball1
甚至都不是一个函数,所以这就是你在那里得到错误的原因。
Javascript 中的任何函数都可以使用 new 运算符调用 - 本质上是构造一个新对象,该对象被视为函数的 this
引用,returns 它(但前提是该函数还没有 return 一个对象——如果是,构造的对象将被丢弃)。
请参阅此处了解 new
运算符的具体作用:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new#Description
你的 "other misleading case" 起初看起来确实令人困惑,因为 instance
被定义为 new Ball()
但 instance instanceof Ball
return 是 false
。这是因为,正如我上面所说,"constructor function" 只有 return 是新构造的对象,否则 return 是一个根本不是对象的值。在大多数设计用作构造函数的函数中,实际上没有显式的 return 值 - 所以 return 值隐含地是 undefined
,因此新构造的对象是 returned.
但在这个例子中,函数将 return 一个对象,它是 Dog
的一个实例 - 所以这个对象最终成为 instance
。虽然是通过 new Ball()
创建的,但有些间接,它 实际上是 通过调用 new Dog()
创建的,因此 Dog.prototype
在其原型链中,并且不是 Ball.prototype
。这解释了您观察到的 instanceof
运算符的行为。
当 new Function
被调用时
- 创建了一个新对象,继承自Function.prototype
- 调用构造函数并将其绑定到第一步创建的新对象。
- 如果没有 returned,这个对象将被 returned 或者你可以 return 像你的例子那样的新对象
function createBall() {
console.log('origin this', this);
return new Ball();
}
当调用 new createBall()
时,它会创建一个继承自 createFoo.prototype this
的新对象,如果没有 returned,则此对象成为 new createBall()
的结果但是这里你 return 一个 new Ball()
,所以 this
对象更改为 new Foo()
和 returned