Javascript 如何检查ECMAScript 6类和函数之间的差异?
在ECMAScript 6中,根据规范,类的Javascript 如何检查ECMAScript 6类和函数之间的差异?,javascript,ecmascript-6,es6-class,Javascript,Ecmascript 6,Es6 Class,在ECMAScript 6中,根据规范,类的类型是“函数” 但是,根据规范,不允许将通过类语法创建的对象作为普通函数调用调用。换句话说,您必须使用new关键字,否则将抛出TypeError TypeError:无法调用函数类 因此,如果不使用try-catch(这将非常难看并破坏性能),您如何检查函数是否来自类语法或函数语法?如果我正确理解了ES6,那么使用类的效果与您键入的相同 var Foo = function(){} var Bar = function(){ Foo.call(thi
类型是“函数”
但是,根据规范,不允许将通过类语法创建的对象作为普通函数调用调用。换句话说,您必须使用new
关键字,否则将抛出TypeError
TypeError:无法调用函数类
因此,如果不使用try-catch(这将非常难看并破坏性能),您如何检查函数是否来自类
语法或函数
语法?如果我正确理解了ES6,那么使用类
的效果与您键入的相同
var Foo = function(){}
var Bar = function(){
Foo.call(this);
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
键入MyClass()
而不键入关键字new
时出现语法错误只是为了防止对象使用的变量污染全局空间
var MyClass = function(){this.$ = "my private dollar"; return this;}
如果你有
// $ === jquery
var myObject = new MyClass();
// $ === still jquery
// myObject === global object
但如果你这样做了
var myObject = MyClass();
// $ === "My private dollar"
因为这个
在构造函数中被称为函数是指全局对象,但是当用关键字new
调用时,Javascript首先创建新的空对象,然后调用构造函数。我做了一些研究,发现ES6类的原型对象[]似乎不可写,不可枚举,不可配置
以下是一种检查方法:
class F { }
console.log(Object.getOwnPropertyDescriptor(F, 'prototype'));
// {"value":{},"writable":false,"enumerable":false,"configurable":false
常规函数默认为可写、不可枚举、不可配置
ES6小提琴:
因此,ES6类描述符将始终将这些属性设置为false,并且如果您试图定义描述符,将抛出错误
// Throws Error
Object.defineProperty(F, 'prototype', {
writable: true
});
// Works
Object.defineProperty(G, 'prototype', {
writable: false
});
但是,使用常规函数,您仍然可以定义这些描述符
// Throws Error
Object.defineProperty(F, 'prototype', {
writable: true
});
// Works
Object.defineProperty(G, 'prototype', {
writable: false
});
在常规函数上修改描述符并不常见,因此您可能可以使用它来检查它是否是类,但这当然不是真正的解决方案
@alexpods对函数进行字符串化并检查class关键字的方法可能是目前最好的解决方案。我认为检查函数是否为ES6类的最简单方法是检查方法的结果。根据报告:
字符串表示必须具有FunctionDeclaration FunctionExpression、GeneratorDeclaration、GeneratorExpression、ClassDeclaration、ClassExpression、ArrowFunction、MethodDefinition或GeneratorMethod的语法,具体取决于对象的实际特征
所以check函数看起来非常简单:
function isClass(func) {
return typeof func === 'function'
&& /^class\s/.test(Function.prototype.toString.call(func));
}
在本线程中提到的不同方法上运行了一些,下面是一个概述:
本机类-Props方法(在大型示例中最快速度为56倍,在普通示例中最快速度为15倍):
这是有效的,因为以下是正确的:
> Object.getOwnPropertyNames(class A {})
[ 'length', 'name', 'prototype' ]
> Object.getOwnPropertyNames(class A { constructor (a,b) {} })
[ 'length', 'name', 'prototype' ]
> Object.getOwnPropertyNames(class A { constructor (a,b) {} a (b,c) {} })
[ 'length', 'name', 'prototype' ]
> Object.getOwnPropertyNames(function () {})
[ 'length', 'name', 'arguments', 'caller', 'prototype' ]
> Object.getOwnPropertyNames(() => {})
> [ 'length', 'name' ]
本机类-字符串方法(比regex方法快约10%):
这也可用于确定常规类别:
// Character positions
const INDEX_OF_FUNCTION_NAME = 9 // "function X", X is at index 9
const FIRST_UPPERCASE_INDEX_IN_ASCII = 65 // A is at index 65 in ASCII
const LAST_UPPERCASE_INDEX_IN_ASCII = 90 // Z is at index 90 in ASCII
/**
* Is Conventional Class
* Looks for function with capital first letter MyClass
* First letter is the 9th character
* If changed, isClass must also be updated
* @param {any} value
* @returns {boolean}
*/
function isConventionalClass (value /* :any */ ) /* :boolean */ {
if ( typeof value !== 'function' ) return false
const c = value.toString().charCodeAt(INDEX_OF_FUNCTION_NAME)
return c >= FIRST_UPPERCASE_INDEX_IN_ASCII && c <= LAST_UPPERCASE_INDEX_IN_ASCII
}
//字符位置
函数名=9/“函数X”的常数索引,X位于索引9处
常量第一个大写字母索引在ASCII=65//A在ASCII的索引65处
const LAST_大写字母_INDEX_IN_ASCII=90//Z位于ASCII的索引90处
/**
*这是传统的课程
*查找首字母大写的函数MyClass
*第一个字母是第9个字符
*如果更改,还必须更新iClass
*@param{any}值
*@returns{boolean}
*/
函数isConventionalClass(value/*:any*/)/*:boolean*/{
if(typeof value!=“function”)返回false
const c=value.toString().charCodeAt(函数名的索引)
return c>=FIRST\u UPPERCASE\u INDEX\u IN\u ASCII&&c查看由生成的编译代码,我认为无法判断函数是否用作类。在当时,JavaScript没有类,每个构造函数都只是一个函数。今天的JavaScript类关键字没有引入“类”的新概念,而是一个语法糖
ES6代码:
// ES6
class A{}
ES5产生于:
当然,如果您热衷于编码约定,您可以解析函数(类),并检查其名称是否以大写字母开头
function isClass(fn) {
return typeof fn === 'function' && /^(?:class\s+|function\s+(?:_class|_default|[A-Z]))/.test(fn);
}
编辑:
已经支持class关键字的浏览器可以在解析时使用它。否则,您只能使用大写字母1
编辑:
正如balupton指出的,Babel为匿名类生成函数_class(){}
,并在此基础上改进了正则表达式
编辑:
在正则表达式中添加了\u default
,以检测导出默认类{}
警告
BabelJS正在大量开发中,不能保证在这种情况下它们不会更改默认函数名。真的,你不应该依赖它。因为现有的答案从ES5环境中解决了这个问题
我认为从ES2015的角度提供一个答案是值得的+
观点;最初的问题没有具体说明,今天很多人没有
不再需要传输类,这稍微改变了情况
我特别想指出的是,可以明确回答这个问题
问题“这个值可以构造吗?”不可否认,这通常是没有用的
就其本身而言,同样的基本问题仍然存在,如果你需要知道的话
如果可以调用某个值
什么东西是可构造的?
首先,我认为我们需要澄清一些术语,因为询问
值是一个构造函数,它可以表示多种含义:
从字面上看,该值是否有[[construct]]插槽?如果有,则为
可构造的。如果不可构造,则不可构造
这个函数是要构造的吗?我们可以产生一些否定的东西:函数
不可能被建造的,我们不打算被建造,但我们不能
也可以说(不使用启发式检查)函数
constructable并不打算用作构造函数
使2无法回答的是,使用fu创建的函数
// ES5
"use strict";
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var A = function A() {
_classCallCheck(this, A);
};
function isClass(fn) {
return typeof fn === 'function' && /^(?:class\s+|function\s+(?:_class|_default|[A-Z]))/.test(fn);
}
const isConstructable = fn => {
try {
new new Proxy(fn, { construct: () => ({}) });
return true;
} catch (err) {
return false;
}
};
isConstructable(class {}); // true
isConstructable(class {}.bind()); // true
isConstructable(function() {}); // true
isConstructable(function() {}.bind()); // true
isConstructable(() => {}); // false
isConstructable((() => {}).bind()); // false
isConstructable(async () => {}); // false
isConstructable(async function() {}); // false
isConstructable(function * () {}); // false
isConstructable({ foo() {} }.foo); // false
isConstructable(URL); // true
const isCallable = fn => typeof fn === 'function';
// Demonstration only, this function is useless:
const isCallable = fn => {
try {
new Proxy(fn, { apply: () => undefined })();
return true;
} catch (err) {
return false;
}
};
isCallable(() => {}); // true
isCallable(function() {}); // true
isCallable(class {}); // ... true!
const isDefinitelyCallable = fn =>
typeof fn === 'function' &&
!isConstructable(fn);
isDefinitelyCallable(class {}); // false
isDefinitelyCallable(class {}.bind()); // false
isDefinitelyCallable(function() {}); // false <-- callable
isDefinitelyCallable(function() {}.bind()); // false <-- callable
isDefinitelyCallable(() => {}); // true
isDefinitelyCallable((() => {}).bind()); // true
isDefinitelyCallable(async () => {}); // true
isDefinitelyCallable(async function() {}); // true
isDefinitelyCallable(function * () {}); // true
isDefinitelyCallable({ foo() {} }.foo); // true
isDefinitelyCallable(URL); // false
const isProbablyNotCallable = fn =>
typeof fn !== 'function' ||
fn.toString().startsWith('class') ||
Boolean(
fn.prototype &&
!Object.getOwnPropertyDescriptor(fn, 'prototype').writable // or your fave
);
isProbablyNotCallable(class {}); // true
isProbablyNotCallable(class {}.bind()); // false <-- not callable
isProbablyNotCallable(function() {}); // false
isProbablyNotCallable(function() {}.bind()); // false
isProbablyNotCallable(() => {}); // false
isProbablyNotCallable((() => {}).bind()); // false
isProbablyNotCallable(async () => {}); // false
isProbablyNotCallable(async function() {}); // false
isProbablyNotCallable(function * () {}); // false
isProbablyNotCallable({ foo() {} }.foo); // false
isProbablyNotCallable(URL); // true
class Foo{
constructor(){
this.someProp = 'Value';
}
}
Foo.prototype.isClass = true;
function Foo(){
this.someProp = 'Value';
}
Foo.prototype.isClass = true;
if(Foo.prototype.isClass){
//It's a class
}
class Person1 {
constructor(name) {
this.name = name;
console.log(new.target) // => // => [Class: Person1]
}
}
function Person2(){
this.name='cc'
console.log(new.target) // => [Function: Person2]
}