如何解决javascript:新关键字在内部做了什么
我知道可能已经有一百万个类似的问题,例如
- 此处:what is the new keyword
- 此处:typeerror: x is not a constructor
- 此处:
__proto__
vs prototype - 此处:MDN doc: new operator
但是请听到我的声音。
代码:
let f = function(){console.log(".f.")};
fn = new f();
// Now:
typeof fn === "object" //true
//! fn() //TypeError: fn is not a function
//! new fn() //TypeError: fn is not a constructor
一般的问题是:是否可以通过操纵函数fn
来创建“新的”对象f
。
该问题细分为“ new”关键字的内部。
根据MDN文档,我的疑问是,当使用new
关键字时,会调用constructor
或class
中的function
。但是,即使fn.__proto__.constructor === f
与其他所有JavaScript functions
一样,fn
的类型为'object'
(我们可以将其更改为'function'
吗?),并且new fn()
引发TypeError。
我们甚至可以通过以下方式添加更多信息:
fn.constructor = f.constructor
fn.__proto__ = f.__proto__
fn.prototype = f.prototype
// f.constructor === Function //true
//! fn.call(this) //fn.call is not a function
fn()
仍然不起作用,new fn
或new fn()
也不起作用。
为什么?
解决方法
JavaScript中的新对象是:
- 使用
function
关键字创建的函数(不包括生成器函数) - 类(可以视为函数)
- 绑定函数奇异对象(“绑定函数”)
- 一些宿主对象
- 代理(如果应用于上述其中之一)
我知道这是因为new
operator的唯一对象类型与are "constructors"(规范术语)一起使用。构造函数是具有[[Construct]]
内部方法的对象,您可以搜索ECMAScript规范以找出具有[[Construct]]
内部方法的对象。
因此,要使构造函数的结果可更新,必须返回上面列出的一种对象。
请注意,规范specifically says规定所有构造函数都是定义函数,因为它们必须支持[[Call]]
内部方法(另请注意以下有关宿主对象的警告)。
如果您想变得非常高级,那么您可能有兴趣了解host objects do not appear to share the ordinary limitations for constructors(大概是出于旧版Web兼容性的原因),但这是例外。
.constructor
属性的说明
声明了可启用的功能f
时,two objects are created:功能对象f
本身,以及.prototype
自身属性{上的默认对象{1}}。运行时会自动将此默认f
对象的.constructor
属性设置为.prototype
。我相信课程的工作方式非常相似。请注意,选择此属性的名称为“原型”这一事实使讨论原型在JavaScript中非常混乱(因为它与函数的f
不同)。
位于[[prototype]]
属性上的对象的constructor
自有财产,从未被任何内置函数或操作(据我所知)读取。我从JavaScript的早期开始就将其视为残余物-它的初衷是为了在构建作为开发人员提供的对象的“类”之间保持链接。主机环境(例如浏览器)有时会使用它来推断对象的“类型”,以便与用户进行通信(例如控制台输出),该属性是可写的,因此不可靠。
.prototype
运算符执行的步骤
在较高级别上,当对构造函数调用new
时,将发生以下步骤(spec contains full details):
- 创建了一个新对象
new
-
o
的{{1}}(“原型”)被设置为构造函数的[[Prototype]]
属性的值(请注意,这意味着o
属性是继承的通过新对象) - 构造函数主体的目标(即
.prototype
)设置为.constructor
- 构造函数以上面定义的
this
值运行 - 如果没有显式的 object-type 返回值,则默认返回
o
范围:我们仅在new
上检查Function
。因为这是最令人困惑的部分。在new
上调用Class
会产生与其他主要OOP语言相似的结果。
原始问题可以分解为以下两个问题:
-
调用
new
关键字时的详细构建过程是什么? -
JavaScript如何确定对象是否可调用? (感谢@BenAston提到
new
关键字只能与一组有限的对象一起使用(例如,前缀为Class
或Function
的对象))
- 第一个问题的答案:
返回到MDN Document,
执行代码new Foo(...)时,会发生以下情况:
- 已创建一个继承自Foo.prototype的新对象。
- 使用指定的参数调用构造函数Foo,并将其绑定到新创建的对象。 new Foo等同于new Foo(),即,如果未指定参数列表,则调用Foo时不带参数。
- 构造函数返回的对象(非null,false,3.1415或其他原始类型)成为整个新表达式的结果。如果构造函数未显式返回对象,则使用在步骤1中创建的对象。(通常,构造函数不返回值,但是如果他们想覆盖常规值,则可以选择这样做。对象创建过程。)
这些词可能含糊不清, 但是PoC代码如下:
// Case1,function has no return value;
// A new object is created,f0n.__proto__ === f0.prototype
let f0 = function() {};
f0.prototype.f0p = "f0p";
let f0n = new f0();
console.log(f0n) // f0n is a new Object,inheriting from f0.prototype
console.log(f0n.__proto__.f0p); // "f0p"
// Case3,function has an explicit return value,the value is an object
// (not null,false,3.1415 or other primitive types);
// the return value becomes the new object value.
let f3 = function() {
return {
"f3": "f3"
}
};
f3.prototype.f3p = "f3p";
let f3n = new f3();
console.log(f3n) // {f3: "f3"}
// f3n is an Object,the return value of its constructor function `f3`
console.log(f3n.__proto__.f3p); // undefined
// Case4 (or Case1 again),function has an **implicit** return value.
let f4 = function(a) {
return (a + a)
};
f4.prototype.f4p = "f4p";
let f4n = new f4();
console.log(f4n.__proto__.f4p); // "f4p"
2。回答第二个问题:
我仍然不知道JavaScript如何确定对象是否可调用。答案应该隐藏在ECMAScripts spec中。 (感谢@BenAston指出)
假设只有Function
是可调用的,这可能是合法的。以下文章提供了一种解决方法:
How to make an object callable
- 额外:如何返回可赎回债券?
使用Case3,let f = Function(){return Function(){}}
由于返回值是非原始显式对象,因此它成为new
指令的结果。结果是一个函数,然后可以调用它。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。