Можно ли рассказать классы из функций во время выполнения? [Дубликат]

По крайней мере, вплоть до CSS3, включая CSS3, вы не можете выбрать это. Но это может быть сделано довольно легко в настоящее время в JS, вам просто нужно добавить немного ванильного JavaScript, обратите внимание, что код довольно короткий.

      cells = document.querySelectorAll('div');
              [].forEach.call(cells, function (el) {
              //console.log(el.nodeName)
                if (el.hasChildNodes() && el.firstChild.nodeName=="A") {
                console.log(el)};
            });
            <div>Peter</div>
            <div><a href="#">Jackson link</a></div>
            <div>Philip</div>
            <div><a href="#">Pullman link</a></div>

17
задан Michał Perłakowski 27 October 2016 в 11:32
поделиться

6 ответов

Я думаю, что самый простой способ проверить, является ли эта функция классом ES6, - проверить результат метода .toString() . Согласно es2015 spec :

Строковое представление должно иметь синтаксис FunctionDeclaration FunctionExpression, GeneratorDeclaration, GeneratorExpression, ClassDeclaration, ClassExpression, ArrowFunction, MethodDefinition или GeneratorMethod в зависимости от по фактическим характеристикам объекта

Таким образом, функция проверки выглядит довольно просто:

function isClass(func) {
  return typeof func === 'function' 
    && /^class\s/.test(Function.prototype.toString.call(func));
}
17
ответ дан alexpods 19 August 2018 в 20:09
поделиться
  • 1
    @Moogs, конечно, это не работает в jsfiddle. Он использует некоторый компилятор, такой как babel, класс компиляции ES6, который должен функционировать. – alexpods 17 March 2015 в 09:23
  • 2
    @Moogs, вы уверены, что es6fiddle не конвертирует ваш javascript в es5? – Hacketo 17 March 2015 в 09:23
  • 3
    @Moogs вы можете проверить, что он работает в io.js с флагом harmony или в Chrome Canary – alexpods 17 March 2015 в 09:24
  • 4
    @Moogs, вы можете увидеть преобразованный код es5 в источниках, файл называется _1.js для меня – Hacketo 17 March 2015 в 09:26
  • 5
    @alexpods извините, да, это работает. Upvote – Miguel Mota 17 March 2015 в 09:26

Определить некоторые эталонные показатели производительности на разных подходах, упомянутых в этом потоке, вот обзор:


Native Class - метод реквизита (быстрее всего на 56x на больших примерах , и 15x по тривиальным примерам):

function isNativeClass (thing) {
    return typeof thing === 'function' && thing.hasOwnProperty('prototype') && !thing.hasOwnProperty('arguments')
}

Это работает, потому что верно следующее:

> 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' ]

Метод Native Class - String (быстрее, чем метод regex by by около 10%):

/**
 * Is ES6+ class
 * @param {any} value
 * @returns {boolean}
 */
function isNativeClass (value /* :mixed */ ) /* :boolean */ {
    return typeof value === 'function' && value.toString().indexOf('class') === 0
}

Это также может быть полезно для определения обычного класса:

// 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
}

Я также рекомендую проверить my typechecker package , который включает в себя варианты использования выше - с помощью метода isNativeClass, isConventionalClass и метода isClass, который проверяет оба типа.

5
ответ дан balupton 19 August 2018 в 20:09
поделиться
  • 1
    Ты прав. И вы не можете добавить свойства caller или arguments в класс после факта, так как оба являются ограниченными функциями функции , и попытка их добавления выдает TypeError. – Pierre Arnaud 2 December 2015 в 05:58
  • 2
    С моей установкой и Babel 6 функции и классы возвращают ['length', 'name', 'prototype']. Таким образом, это не работает для меня . – Pierre Arnaud 2 December 2015 в 06:05
  • 3
    Возможно, это не работает в строгом режиме? – nils 6 January 2016 в 16:23
  • 4
    Это вернет ложные срабатывания для функций стрелок, которые также не имеют свойства arguments или caller. То есть (() => {}).hasOwnProperty('arguments') возвращает true. – Dave W. 21 March 2017 в 04:59
  • 5
    @PierreArnaud, как и следовало ожидать, поскольку он уже не является родным классом при компиляции - поэтому проверка на родном классе не то, что вы хотите, вместо этого вы можете использовать isClass из пакета typechecker который проверяет как родной, так и обычный классы. – balupton 22 March 2017 в 02:32

Я провел некоторое исследование и выяснил, что объект прототипа [ spec 19.1.2.16 ] классов ES6, по-видимому, не записывается , неперечислимый , неконфигурируемый .

Вот способ проверки:

class F { }

console.log(Object.getOwnPropertyDescriptor(F, 'prototype'));
// {"value":{},"writable":false,"enumerable":false,"configurable":false

Регулярная функция по умолчанию доступна для записи , неперечислимые , неконфигурируемые .

function G() { }

console.log(Object.getOwnPropertyDescriptor(G, 'prototype'));
// {"value":{},"writable":true,"enumerable":false,"configurable":false}

ES6 Fiddle: http://www.es6fiddle.net/i7d0eyih/

Таким образом, дескриптор класса ES6 всегда будет иметь эти свойства на false и будет вызывать ошибку, если вы попытаетесь определить дескрипторы.

// Throws Error
Object.defineProperty(F, 'prototype', {
  writable: true
});

Однако с помощью обычной функции вы все равно можете определить эти дескрипторы.

// Works
Object.defineProperty(G, 'prototype', {
  writable: false
});

Это не очень что дескрипторы изменяются на регулярные функции, поэтому вы, вероятно, можете использовать это, чтобы проверить, является ли это классом или нет, но, конечно, это не настоящее решение.

Метод @alexpods для подбора функции и проверки для ключевого слова class, вероятно, является лучшим решением на данный момент.

8
ответ дан Pierre Arnaud 19 August 2018 в 20:09
поделиться
  • 1
    Интересное решение, но это не будет работать во всех случаях, поэтому это не настоящее решение. – Marco Bonelli 17 March 2015 в 09:09
  • 2
    Да, я упомянул об этом, но это на шаг ближе. – Miguel Mota 17 March 2015 в 09:10
  • 3
    вы пытаетесь решить функцию языка здесь, ребята ... в JS нет классов, даже если они ввели класс ключевых слов. – webduvet 17 March 2015 в 09:19
  • 4
    @webduvet Я не согласен. Хотя классы, введенные в ES6, конечно, не соответствуют уровню других языков, и на самом деле они просто повторно используют уже существующие функции ECMAScript, семантика того, как вещи, созданные с помощью синтаксиса класса, отличаются от других методов. Поэтому это отличная функция, которую нельзя назвать «классами JS». – Moncader 17 March 2015 в 09:22
  • 5
    @Moogs Я думаю, что ваша цитата неправильная. F.prototype устанавливается, когда оценивается тело класса, которое описано здесь: people.mozilla.org/~jorendorff/… ; дескриптор свойства для Object.prototype - это нечто совершенно другое (хотя иногда он влияет на связанный выше алгоритм). Этот алгоритм слишком сложный для меня, чтобы быстро увидеть, правы ли вы в F.prototype, всегда будучи неперечислимыми, не записываемыми и т. Д. Но если кто-то хочет изучить его больше, это то, где они должны проверять. – Ethan 19 October 2015 в 05:53
3
ответ дан Semicolon 19 August 2018 в 20:09
поделиться

Глядя на скомпилированный код, сгенерированный Babel , я думаю, что вы не можете определить, используется ли функция как класс. В то время JavaScript не имел классов, и каждый конструктор был просто функцией.

Код ES6:

// ES6
class A{}

ES5, сгенерированный Babel :

// 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);
}

EDIT:

Браузеры, которые уже поддерживают ключевое слово класса, могут использовать его при разборе. В противном случае вы застряли с заглавной буквой.

EDIT:

Как отметил Балуптон, Бабель генерирует function _class() {} для анонимных классов.

Добавлено _default в регулярное выражение, чтобы обнаружить такие классы, как export default class {} g13] BabelJS находится в стадии разработки, и нет гарантии, что они не изменят имена функций по умолчанию в этих случаях. На самом деле, вы не должны полагаться на это.

2
ответ дан Tamas Hegedus 19 August 2018 в 20:09
поделиться
  • 1
    Это все еще работает с анонимными классами, скомпилированными в Babel? Таким образом, анонимные функции скомпилированы в function _class () {} - исходный код function a ( ) { return class {} && class {} }, регулярное выражение может быть соответствующим образом изменено. – balupton 26 August 2015 в 22:02
  • 2
    Обновлено регулярное выражение для анонимных классов Babel: return typeof fn === 'function' && /^(?:class|function (?:[A-Z]|_class))/.test(fn) – balupton 26 August 2015 в 22:08
  • 3
    Hrmm, анонимные классы отличаются от export default class {}, возможно, просто проверка на _classCallCheck является лучшим вариантом. – balupton 26 August 2015 в 22:14
  • 4
    Я думаю, что проверка на _classCallCheck во всей функции является излишним и имеет незначительный эффект. Проверка начального количества имен в столице - слабое звено здесь – Tamas Hegedus 26 August 2015 в 22:19

, если я правильно понял ES6, используя class, имеет такой же эффект, как если бы вы вводили синтаксическую ошибку

var Foo = function(){}
var Bar = function(){
 Foo.call(this);
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;

при вводе MyClass() без keyword 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"

, поскольку this в Конструктор, называемый функцией, относится к глобальному объекту, но при вызове с ключевым словом new Javascript сначала создает новый пустой объект, а затем вызывает на нем конструктор.

0
ответ дан webduvet 19 August 2018 в 20:09
поделиться
  • 1
    Да, возможно, одна из причин. Однако, как вы можете проверить, является ли это классом или функцией, подобной заданию? – Moncader 17 March 2015 в 08:48
  • 2
    Как я уже сказал, использование класса делает указанную последовательность шагов. поэтому нет такого понятия, как typeof 'class'. это тип функции, потому что это функция. Проверка на время выполнения, использовать ли «новое» или нет, действительно действительно очень плохая практика. Настолько плохо, что создатели ES6 решили выбросить синтаксическую ошибку в этом случае. – webduvet 17 March 2015 в 08:58
  • 3
    Создатели ES6 решили выбросить синтаксическую ошибку, если вы попытаетесь вызвать функцию, а не проверить ее. Предположим, вы создаете библиотеку, которая получит объекты функции и решит их выполнить или сохранить в некоторой базе данных классов (просто случайная идея). У вас нет способа узнать, какой именно объект вы получили сейчас. Таким образом, без выполнения, как вы можете проверить, может ли он быть выполнен или нет? – Moncader 17 March 2015 в 09:02
  • 4
    любая функция может быть вызвана как конструктор, а именно как работает javascript. поэтому, если вы строите библиотеку классов, любая функция так же хороша, как и класс ES6. – webduvet 17 March 2015 в 09:13
Другие вопросы по тегам:

Похожие вопросы: