javascript - 理解 typescript 生成的 __extends 函数?

标签 javascript prototype javascript-objects proto javascript-inheritance

我在玩Typescript并试图理解编译器生成的已编译 Javascript 代码

typescript 代码:

class A { }
class B extends A { }

生成的 Javascript 代码:
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var A = /** @class */ (function () {
    function A() {
    }
    return A;
}());
var B = /** @class */ (function (_super) {
    __extends(B, _super);
    function B() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return B;
}(A));

按照 Mozilla docs 的 Javascript 继承这是:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

我在 Typescript 生成的代码中不明白的部分是这个

1.这条线的目的是什么?貌似是把A的所有key都复制到B了?这是对静态属性的某种黑客攻击吗?
var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };

2. 这是在做什么?
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());

我不明白这部分:(__.prototype = b.prototype, new __())
为什么函数 B() 返回这个?
return _super !== null && _super.apply(this, arguments) || this;

如果有人可以逐行向我解释这一点,我将不胜感激。

最佳答案

我自己对此很好奇,找不到快速答案,所以这是我的分割:

它是做什么的

__extends 是一个函数,它模拟面向对象语言中的单类继承,并为派生函数返回一个新的构造函数,该函数可以创建从基对象继承的对象。

注 1:

我自己实际上并没有意识到这一点,但是如果您执行以下操作,其中所有值都为真,则变量将设置为正在测试的最后一项的值,除非其中一项为假,在这种情况下变量设置为假:

// value1 is a function with the definition function() {}
var value1 = true && true && function() {};

// value2 is false
var value2 = true  && false && function() {};

// value3 is true
var value3 = true && function() {} && true;

我提到这一点是因为当我看到这个 javascript 时,这是最让我困惑的事情,它在 __extends 函数定义中使用了几次。

注 2:
参数 d(可能代表派生)和 b(可能代表基)都是构造函数而不是实例对象。

注 3:
prototype是函数的属性,它是“构造函数”函数(即使用 new <function name>() 创建的对象)使用的原型(prototype)对象。

当您使用 new运算符构造一个新对象,新对象的内部[[PROTOTYPE]]又名 __proto__被设置为函数的原型(prototype)属性。
function Person() {  
}

// Construct new object 
var p = new Person();

// true
console.log(p.__proto__ === Person.prototype);

// true
console.log(Person.prototype.__proto__ === Object.prototype);

它不是副本。它是对象。

当你创建一个文字对象时
var o = {};

// true    
console.log(o.__proto__ === Object.prototype);

新对象的 __proto__设置为 Object.prototype (内置的 Object 构造函数)。

您可以设置对象的 __prototype__到另一个对象,但是使用 Object.create .

当在当前对象上找不到属性或方法时,对象的 [[PROTOTYPE]]被检查。如果未找到,则检查该对象的原型(prototype)。所以它会检查原型(prototype),直到它到达最终的原型(prototype)对象,Object.prototype .请记住,没有什么是副本。

注释 4
在 Javascript 中模拟继承时,设置了“构造函数”原型(prototype)。
function Girl() {  
}

Girl.prototype = Object.create(Person.prototype);

// true
console.log(Girl.prototype.__proto__ === Person.prototype);

// true
console.log(Girl.constructor === Function);

// Best practices say reset the constructor to be itself
Girl.constructor = Girl;

// points to Girl function
console.log(Girl.constructor);

注意我们如何将构造函数指向 Girl 因为它 Person 的构造函数指向内置的 Function .

您可以在以下位置查看上面的代码:http://jsbin.com/dutojo/1/edit?js,console

原文:
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();

故障:
var __extends = (this && this.__extends) || (function () {
   // gobbledygook
})();

保持我的 注释 1 如上所述,这第一部分(和结尾)是创建一个名为 __extends 的变量,它的目的是保存一个函数来设置派生类的原型(prototype)。
(this && this.__extends) 

正在做我的 注释 1 解释。如果 this 为真,而 this.__extends 为真,则变量 __extends 已存在,因此设置为自身的现有实例。如果不是,则设置为 || 之后的内容这是一个 iife(立即调用的函数表达式)。

现在对于 __extends 的实际定义 gobbledygook:
var extendStatics = Object.setPrototypeOf ||

一个名为 extendStatics 的变量被设置为脚本运行所在环境的内置 Object.setPrototypeOf 函数( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf )



它创建了自己的版本
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };

在注释 3 中,我讨论了 __proto__又名 [[PROTOTYPE]]以及如何设置。编码
{ __proto__: [] } instanceof Array

是通过比较文字对象的 __proto__ 来确定当前环境是否允许设置此属性的测试。使用 Array 内置函数设置为文字数组。

引用上面的注释 1 并记住,如果环境评估一个对象的原型(prototype)属性设置为内置数组,则 javascript instanceof 运算符返回 true 或 false ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof ),然后 extendsStatics 设置为
function (d, b) { d.__proto__ = b; })

如果环境没有以这种方式评估它,那么 extendStatics 设置为:
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }

这样做是因为 __proto__直到 ECMAScript 2015(根据 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto )才成为官方 ECMAScript 标准的一部分,只是为了向后兼容)。如果支持 __proto__函数被使用,否则它使用'滚动你自己的'版本',它为用户定义的属性从对象 b 复制到 d。

现在定义了extendStatics 函数变量,返回一个调用extendStatics 中的任何内容(以及其他一些东西)的函数。请注意,参数 'd' 是子类(继承的那个),而 'b' 是父类(super class)(继承自的那个):
return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };

分解它调用 extendStatics 并且第一个参数对象 (d) 的原型(prototype)设置为 (b)(记忆 注释 3 以上):
extendStatics(d, b);

在下一行中,声明了一个名为 '__' 的构造函数,该函数将其构造函数指定为派生 (d) 构造函数:
function __() { this.constructor = d; }

如果基 (b) constructor函数恰好为空,这将确保派生的将保留自己的 prototype .

来自 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor ,Object.prototype.constructor(所有对象都是javascript中的对象):

Returns a reference to the Object constructor function that created the instance object. Note that the value of this property is a reference to the function itself, not a string containing the function's name.





All objects will have a constructor property. Objects created without the explicit use of a constructor function (i.e. the object and array literals) will have a constructor property that points to the Fundamental Object constructor type for that object.



所以如果 constructor函数 '__' 是新的,因为它会创建一个派生对象。

最后是这一行:
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());

其中设置了 prototype如果基 constructor,派生 (d) 将成为新的空对象函数恰好为空
//  b is null here so creates {}
Object.create(b)

或者

设置 __ constructor函数的 prototype成为基类 prototype然后调用具有设置派生函数作用的__() constructor为派生函数。
(__.prototype = b.prototype, new __()

所以基本上返回的最终函数创建了一个派生的构造函数,该函数原型(prototype)继承自基本构造函数。

为什么函数 B() 返回这个?
return _super !== null && _super.apply(this, arguments) || this;

根据:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

The apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object).



记住 B 是一个构造函数,这就是 B 定义中返回的内容。

如果你有一个 Person 类(构造函数)在构造函数中接受一个名称参数,那么你可以调用一个派生的 Girl 类(构造函数),并将女孩的名字作为参数。
// Base constructor function
function Person(n) {
  // Set parameter n to the name property of a Person
  this.name = n;
}

function Girl() {
   // Call the Person function with same arguments passed to new Girl
   Person.apply(this, arguments);
   // Set it so all Girl objects created inherit properties and methods from Person
   Girl.prototype = Object.create(Person.prototype);  
   // Make sure the constructor is not set to Person
   Girl.prototype.constructor =  Girl;
}

var p = new Person("Sally");
var g = new Girl("Trudy");
console.log(p.name);
console.log(g.name);

关于javascript - 理解 typescript 生成的 __extends 函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45954157/

相关文章:

javascript - CSS对齐以适应不同的屏幕分辨率

javascript - __proto__ VS。 JavaScript 中的原型(prototype)

ios - Storyboard上 UITableViewController 的动态原型(prototype)单元格高度大小

javascript - 如何在不改变原始数组的情况下更改对象键

javascript - 有人可以解释这个 getOwnPropertyDescriptor 和删除行为吗?

javascript - 查找数组集合中没有重复项的元素的索引

javascript - 使用 AJAX 将变量从 jQueryUI 对话框发布到 PHP

javascript - 在 y 轴上添加点 HIGHCHARTS

javascript原型(prototype)方法assign VS underscore _.extend

javascript - 从 javascript 对象复制数据并为其分配新键