Подробные JS Позвоните в новый принцип функции

внешний интерфейс JavaScript

Конструкторы часто используются в JavaScript для создания объектов (черезnewоператор вызывает функцию), которая используетnewЧто именно происходит при вызове функции? Давайте рассмотрим несколько примеров, прежде чем объяснять, что происходит за кулисами.

1) Посмотрите на три примера

1.1 Нет оператора возврата

Конструктор не имеет в концеreturnоператор, который также используется по умолчанию при использовании конструктора, в конечном итоге вернет новый объект, как показано ниже:

function Foo(age) {
  this.age = age;
}

var o = new Foo(111);
console.log(o);

Это обычный процесс создания объекта с помощью конструктора, и распечатка{age: 111}.

1.2 возвращать данные типа объекта

Конструктор последнийreturnДанные типа объекта:

function Foo(age) {
  this.age = age;

  return { type: "我是显式返回的" };
}

var o = new Foo(222);
console.log(o);

что распечатывает{type: '我是显式返回的'}, то есть,returnПредыдущая работа была проделана напрасно и, наконец, вернуласьreturnобъект позади.

1.3 возвращать данные примитивного типа

Это до тех пор, пока тело конструктора имеет в концеreturn, возвращает обаreturnА как насчет данных, стоящих за ним?

Давайте рассмотрим случай возврата данных примитивного типа:

function Foo(age) {
  this.age = age;

  return 1;
}

var o = new Foo(333);
console.log(o);

что распечатывает{age: 333}, и нетreturnтот же временной эффект. Это не то же самое, что ожидалось См. анализ ниже для вашего принципа, стоящего за этим.

2) Принцип

2.1. Случай нестрелочных функций

когда используешьnewОператоры создают объекты, официальная документация ES5 находится вопределение функцииВ разделе даются следующие определения.13.2.2 [[Construct]]:

When the [[Construct]] internal method for a Function object F is called with a possibly empty list of arguments, the following steps are taken:

  1. Let obj be a newly created native ECMAScript object.
  2. Set all the internal methods of obj as specified in 8.12.
  3. Set the [[Class]] internal property of obj to Object.
  4. Set the [[Extensible]] internal property of obj to true.
  5. Let proto be the value of calling the [[Get]] internal property of F with argument "prototype".
  6. If Type(proto) is Object, set the [[Prototype]] internal property of obj to proto.
  7. If Type(proto) is not Object, set the [[Prototype]] internal property of obj to the standard built-in Object prototype object as described in 15.2.4.
  8. Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.
  9. If Type(result) is Object then return result.
  10. Return obj.

См. шаги 8 и 9:

8) Вызовите функциюF, присвойте возвращаемое значениеresult;в,FФактические аргументы во время выполнения передаются в[[Construct]](которыйFсебя) параметры,Fвнутреннийthisнаправлениеobj; 9) ЕслиresultдаObjectТип, возвратresult;

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

Наконец, глядя на шаг 10:

10) ЕслиFВозвращаемый объект не имеет типа (шаг 9 не выполняется), тогда возвращается созданный объектobj.

Если конструктор явно не возвращает тип объекта (явно возвращает примитивный тип данных или не возвращает напрямую), возвращается изначально созданный объект.

2.2. Случай стрелочных функций

Что, если конструктор является стрелочной функцией?

Стрелочные функции не имеют[[Construct]]метод нельзя использоватьnewвызов, будет сообщено об ошибке.

Уведомление: что[[Construct]]Он относится к самой функции-конструктору.

Соответствующие спецификации находятся вОфициальная документация для ES6Упоминается, но официальная документация начиная с ES6 очень сложна для понимания, поэтому я не буду ее здесь описывать.

3) Полный процесс новой вызывающей функции

3.1 Описание на китайском языке и соответствующий анализ кода

Можно использовать любую функцию, кроме стрелочных.newЗвонок сделан, что за ним произошло, очень понятно английское описание в предыдущем разделе, а далее на китайском описано так:

1) Создать собственный объект ECMAScriptobj;
2) датьobjУстановите внутренние свойства нативного объекта (в отличие от свойств прототипа, внутренние свойства представлены в виде[[PropertyName]], имя атрибута заключено в две квадратные скобки, а имя атрибута пишется с заглавной буквы, например, common[[Prototype]],[[Constructor]])
3) Настройкиobjвнутренние свойства[[Class]]дляObject;
4) Настройкиobjвнутренние свойства[[Extensible]]дляtrue;
5) будетprotoЗначение установлено наFизprototypeзначение атрибута;
6) Еслиprotoтип объекта, наборobjвнутренние свойства[[Prototype]]значениеproto;(Проведите ассоциацию цепочки прототипов и реализуйте ключ к наследованию)
7) Еслиprotoне является типом объекта, тогда установитеobjвнутренние свойства[[Prototype]]Значение является внутренней конструкционной функциейObjectизprototypeстоимость;(функцияprototypeсвойства могут быть переопределены, если изменено на необъектный тип,objиз[[Prototype]]указать на прототип объекта Object)
8) 9) 10) См. анализ в предыдущем разделе. (решить, что вернуть)

В случае шага 7 см. код ниже:

function Foo(name) {
  this.name = name;
}

var o1 = new Foo("xiaoming");
console.log(o1.__proto__ === Foo.prototype); // true

// 重写构造函数原型属性为非对象类型,实例内部 [[Prototype]] 属性指向 Object 原型对象
// 因为实例是一个对象类型的数据,默认会继承内建对象的原型,
// 如果构造函数的原型不满足形成原型链的要求,那就跳过直接和内建对象原型关联
Foo.prototype = 1;
var o2 = new Foo("xiaohong");
console.log(o2.__proto__ === Foo.prototype); // false
console.log(o2.__proto__ === Object.prototype); // true

3.2 Более краткое описание языка

Если выполняетсяnew Foo(), процесс выглядит следующим образом:

1) Создать новый объектo;
2) Присвоить значения внутренним свойствам нового объекта, ключ дать[[Prototype]]Присвоение свойств, построение цепочки прототипов (если прототип конструктора имеет тип Object, то он указывает на прототип конструктора, в противном случае — на прототип объекта Object);
3) Выполнить функциюFoo, внутри процесса выполненияthisуказывает на вновь созданный объектo;
4) ЕслиFooЕсли данные типа объекта явно возвращаются внутренне, то данные возвращаются, и выполнение завершается; в противном случае возвращается только что созданный объект.o.

4) Несколько замечаний

4.1 Определите, является ли это типом объекта

О том, являются ли данныеObjectтип, который может бытьinstanceofоператор судить: еслиx instanceof Objectвернутьtrue,ноxдляObjectТипы.

Как видно из вышеизложенного,null instanceof Objectвернутьfalse,такnullнетObjectТип, несмотря наtypeof nullВозвращает «Объект».

4.2 Принцип instanceof

instanceofПринцип работы: в выраженииx instanceof Foo, еслиFooпрототип (т.Foo.prototype)Появляться вxв цепочке прототипов, затем вернитесьtrue, иначе вернутьfalse.

Поскольку прототип функции может быть переопределен, он появится вxпройти черезFooновинкаПозжеполностью переписанFooПрототипx instanceof FooвернутьfalseСлучай. Поскольку прототип конструктора переписывается после создания экземпляра, прототип, на который указывает экземпляр, больше не является новым прототипом конструктора, см. следующий код:

const Foo = function() {};

const o = new Foo();

o instanceof Foo; // true

// 重写 Foo 原型
Foo.prototype = {};
o instanceof Foo; // false

использованная литература

What values can a constructor return to avoid returning this?
[[Construct]] internal method