我自己对此很好奇,找不到快速答案,所以这是我的细分:
它的作用
__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>() 创建的对象)使用的原型对象。
当您使用new 运算符构造新对象时,新对象的内部[[PROTOTYPE]] 又名__proto__ 被设置为函数的原型属性。
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.create 将对象的__prototype__ 设置为另一个对象。
当在当前对象上找不到属性或方法时,检查对象的[[PROTOTYPE]]。如果未找到,则检查该对象的原型。因此它会检查原型,直到到达最终的原型对象Object.prototype。请记住,没有什么是复制品。
注意事项 4
在 Javascript 中模拟继承时,会设置“构造函数”的原型。
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 的变量,其目的是保存一个函数来设置原型派生类的。
(this && this.__extends)
正在按照我的注释 1 解释。如果 this 为真且 this.__extends 为真,则变量 __extends 已存在,因此设置为自身的现有实例。如果不是,则设置为 || 之后的内容这是一个 iife(立即调用的函数表达式)。
现在是 gobbledygook,它是 __extends 的实际定义:
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]; };
在 Note 3 中,我讨论了 __proto__ aka [[PROTOTYPE]] 以及如何设置它。代码
{ __proto__: [] } instanceof Array
是通过比较文字对象的__proto__设置为文字数组与Array内置函数来确定当前环境是否允许设置此属性的测试。
从上面参考我的 注 1 并记住,如果环境评估一个对象的原型属性设置为内置数组,然后 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 官方标准的一部分,直到 ECMAScript 2015(并且根据 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto)才出现,只是为了向后兼容)。如果支持,则使用__proto__ 函数,否则它使用“滚动您自己的”版本,该版本为用户定义的属性从对象 b 复制到 d。
现在定义了 extendStatics 函数变量,返回一个调用 extendStatics 中的任何内容(以及其他一些内容)的函数。请注意,参数“d”是子类(继承者),“b”是超类(继承者):
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
将其分解为调用extendStatics,第一个参数对象(d)的原型设置为(b)(回忆上面的注3):
extendStatics(d, b);
在下一行中,声明了一个名为“__”的构造函数,将其构造函数指定为派生的 (d) 构造函数:
function __() { this.constructor = d; }
如果基 (b) constructor 函数恰好为 null,这将确保派生函数将保留其自己的 prototype。
来自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor,Object.prototype.constructor(所有对象都是javascript中的对象):
返回对创建的 Object 构造函数的引用
实例对象。请注意,此属性的值为
引用函数本身,而不是包含
函数名。
和
所有对象都有一个构造函数属性。没有创建的对象
显式使用构造函数(即对象和数组
literals) 将有一个构造函数属性指向
该对象的基本对象构造函数类型。
因此,如果 constructor 函数 '__' 是新的,它将创建一个派生对象。
最后是这一行:
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
如果基础constructor 函数恰好为空,则将派生(d) 的prototype 设置为新的空对象
// b is null here so creates {}
Object.create(b)
或
将__constructor函数的prototype设置为基类prototype,然后调用__(),其作用是将派生函数constructor设置为派生函数。
(__.prototype = b.prototype, new __()
所以基本上返回的最终函数创建了一个派生构造函数,该构造函数原型继承自基本构造函数。
为什么函数 B() 会返回这个?
return _super !== null && _super.apply(this, arguments) || this;
根据:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
apply() 方法调用具有给定 this 值的函数,并且
以数组(或类似数组的对象)形式提供的参数。
记住 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);