>웹 프론트엔드 >JS 튜토리얼 >JS의 새로운 호출 기능 원리 소개

JS의 새로운 호출 기능 원리 소개

不言
不言원래의
2018-07-10 17:38:102391검색

이 글은 JS의 새로운 호출 기능의 원리를 주로 소개합니다. 이제는 모든 사람과 공유합니다. 도움이 필요한 친구들이 참조할 수 있습니다.

생성자는 종종 를 통해 객체를 생성하는 데 사용됩니다. new 연산자를 사용하여 함수 호출), new를 사용하여 함수를 호출하면 정확히 무슨 일이 발생하나요? 뒤에서 무슨 일이 일어나고 있는지 설명하기 전에 몇 가지 예를 살펴보겠습니다. new 操作符调用一个函数),那在使用 new 调用一个函数的时候到底发生了什么?先看几个例子,再解释背后发生了什么。

1)看三个例子

1.1 无 return 语句

构造函数最后没有 return 语句,这也是使用构造函数时默认情况,最后会返回一个新对象,如下:

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

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

这是常见的使用构造函数创建对象的过程,打印出来的是 {age: 111}

1.2 return 对象类型数据

构造函数最后 return 对象类型数据:

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

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

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

打印出来的是 {type: '我是显式返回的'},也就是说,return 之前的工作都白做了,最后返回 return 后面的对象。

1.3 return 基本类型数据

那是不是只要构造函数体内最后有 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)如果 resultObject 类型,返回 result

这也就解释了如果构造函数显式返回对象类型,则直接返回这个对象,而不是返回最开始创建的对象。

最后在看第 10 步:

10)如果 F 返回的不是对象类型(第 9 步不成立),则返回创建的对象 obj

如果构造函数没有显式返回对象类型(显式返回基本数据类型或者直接不返回),则返回最开始创建的对象。

2.2 箭头函数的情况

那如果构造函数是箭头函数怎么办?

箭头函数中没有 [[Construct]] 方法,不能使用 new 调用,会报错。

NOTICE:其中 [[Construct]] 就是指构造函数本身。

相关规范在 ES6 的官方文档 中有提,但自从 ES6 以来的官方文档巨难懂,在此不做表述。

3)new 调用函数完整过程

3.1 中文描述及相关代码分析

除了箭头函数之外的任何函数,都可以使用 new

1) 세 가지 예를 보세요

1.1 return 문 없음

생성자 끝에 return 문이 없습니다. 이것이 기본값이기도 합니다. 생성자를 사용할 때의 상황. 마지막으로 다음과 같이 새 개체가 반환됩니다. 🎜
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
🎜이것은 생성자를 사용하여 개체를 만드는 일반적인 프로세스이며 인쇄되는 내용은 {age: 111}입니다. >. 🎜

1.2 객체 유형 데이터 반환

🎜생성자는 최종적으로 객체 유형 데이터를 반환합니다: 🎜
const Foo = function() {};

const o = new Foo();

o instanceof Foo; // true

// 重写 Foo 原型
Foo.prototype = {};
o instanceof Foo; // false
🎜인쇄되는 내용은 {type: '나는 명시적으로 반환했습니다' }, 즉 <code>return 이전의 모든 작업이 헛수고로 이루어지고 return 이후의 개체가 최종적으로 반환됩니다. 🎜

1.3 기본 유형 데이터 반환

🎜그렇다면 생성자 본문 끝에 return이 있는 한 return 이후의 데이터는 반환될 것인가? 🎜🎜기본형 데이터를 반환하는 상황을 살펴보겠습니다: 🎜rrreee🎜인쇄되는 내용은 {age: 333}이며, 이는 return이 없는 것과 동일한 효과를 갖습니다. . 기대와는 다릅니다. 그 원리는 아래 분석을 참조하시기 바랍니다. 🎜

2) 원리

2.1 화살표가 아닌 함수의 경우

🎜 new 연산자를 사용하여 객체를 생성할 때 ES5 공식 문서 함수에 있습니다. 정의 섹션 13.2.2 [[Construct]]에서 다음 정의가 이루어집니다. 🎜🎜[[Construct]] 내부 메서드가 Function 객체 F가 빈 인수 목록과 함께 호출되는 경우 다음 단계가 수행됩니다.🎜
  1. 🎜obj를 새로 생성된 네이티브 ECMAScript 객체로 둡니다.🎜
  2. 🎜obj의 모든 내부 메서드를 8.12.🎜에 지정된 대로 설정합니다.
  3. 🎜Set obj의 [[Class ]] 내부 속성을 Object로 설정하세요.🎜
  4. 🎜obj의 [[Extensible]] 내부 속성을 true로 설정하세요.🎜
  5. 🎜proto를 값으로 설정하세요 인수 "prototype"을 사용하여 F의 [ [Get]] 내부 속성을 호출합니다.🎜
  6. 🎜Type(proto)가 Object인 경우 [[Prototype을 설정합니다. ]] obj의 내부 속성을 proto로.🎜
  7. 🎜Type(proto)이 Object가 아닌 경우 obj의 [[Prototype]] 내부 속성을 표준 내장 객체 프로토타입 객체 15.2.4.🎜에 설명된 대로
  8. 🎜결과를 F의 [[Call]] 내부 속성을 호출한 결과로 두고 obj를 this로 제공합니다. value 및 [[Construct]]에 전달된 인수 목록을 args로 제공합니다.🎜
  9. 🎜Type(result)이 Object이면 결과를 반환합니다.🎜 li>
  10. 🎜Return obj.🎜
🎜8단계와 9단계를 살펴보세요. 🎜
8) F 함수를 호출하고 해당 반환 값을 result; 그 중 F가 실행될 때 실제 매개변수는 [[Construct]]에 전달되는 매개변수입니다(즉, F 자체), F 내부 thisobj를 가리킵니다.
9) result가 다음과 같은 경우 Object Type, return result;
🎜이는 또한 생성자가 명시적으로 객체 유형을 반환하는 경우 반환하는 대신 객체를 직접 반환한다는 것을 설명합니다. 원래 생성된 개체입니다. 🎜🎜마지막으로 10단계를 살펴보세요. 🎜
10) F가 개체 유형을 반환하지 않는 경우(9단계는 true가 아님) 생성된 개체 obj를 반환합니다. 코드>.
🎜생성자가 객체 유형을 명시적으로 반환하지 않는 경우(기본 데이터 유형을 명시적으로 반환하거나 직접 반환하지 않는 경우) 처음 생성된 객체가 반환됩니다. 🎜

2.2 화살표 함수의 경우

🎜생성자가 화살표 함수라면 어떨까요? 🎜🎜화살표 함수에는 [[Construct]] 메소드가 없고 new를 사용하여 호출할 수 없으며 오류가 보고됩니다. 🎜🎜참고: 여기서 [[Construct]]는 생성자 자체를 나타냅니다. 🎜
관련 사양은 ES6 공식 문서에 언급되어 있지만, ES6 이후의 공식 문서는 이해하기가 매우 어렵기 때문에 여기서는 설명하지 않겠습니다.

3) 새로운 호출 함수의 전체 과정

3.1 중국어 설명 및 관련 코드 분석

🎜화살표 함수를 제외한 모든 함수는 new를 사용할 수 있습니다. 뒤에서 무슨 일이 일어나는지는 이전 섹션에서 영어로 매우 명확하며 중국어로 다음과 같이 설명됩니다. 🎜

1)创建 ECMAScript 原生对象 obj;  
2)给 obj 设置原生对象的内部属性;(和原型属性不同,内部属性表示为 [[PropertyName]],两个方括号包裹属性名,并且属性名大写,比如常见 [[Prototype]][[Constructor]])  
3)设置 obj 的内部属性 [[Class]]Object;  
4)设置 obj 的内部属性 [[Extensible]]true;  
5)将 proto 的值设置为 Fprototype 属性值;  
6)如果 proto 是对象类型,则设置 obj 的内部属性 [[Prototype]] 值为 proto;(进行原型链关联,实现继承的关键)  
7)如果 proto 是不对象类型,则设置 obj 的内部属性 [[Prototype]] 值为内建构造函数 Objectprototype 值;(函数 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 类型

关于一个数据是否是 Object 类型,可以通过 instanceof 操作符进行判断:如果 x instanceof Object 返回 true,则 xObject 类型。

由上可知,null instanceof Object 返回 false,所以 null 不是 Object 类型,尽管typeof null 返回 "Object"。

4.2 instanceof 原理

instanceof 的工作原理是:在表达式 x instanceof Foo 中,如果 Foo 的原型(即 Foo.prototype)出现在 x 的原型链中,则返回 true,不然,返回 false

因为函数的原型可以被改写,所以会出现在 x 通过 Foo new 出来之后完全改写 Foo 的原型 x instanceof Foo 返回 false 的情况。因为实例创建之后重写构造函数原型,实例指向的原型已经不是构造函数的新的原型了,见下面代码:

const Foo = function() {};

const o = new Foo();

o instanceof Foo; // true

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

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

위 내용은 JS의 새로운 호출 기능 원리 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.