为通过构造函数创建的对象设置[[Prototype]]的方法
一、为通过构造函数创建的对象设置[[Prototype]]的方法:添加/删除属性到默认”prototype”,而不是将其整个覆盖。
prototype:F的一个名为”prototype”的常规属性
一、可以使用诸如new F()这样的构造函数来创建一个新对象。
1、如果F.prototype是一个对象,那么new操作符会使用它为新对象设置[[Prototype]]。
二、JavaScript 从一开始就有了原型继承。这是 JavaScript 编程语言的核心特性之一。
1、但是在过去,没有直接对其进行访问的方式。唯一可靠的方法是构造函数的”prototype”属性。目前仍有许多脚本仍在使用它。
三、这里的F.prototype指的是F的一个名为”prototype”的常规属性。这听起来与“原型”这个术语很类似,但这里我们实际上指的是具有该名字的常规属性。
| 【示例】```javascript let animal = { eats: true };
function Rabbit(name) { this.name = name; }
Rabbit.prototype = animal;
let rabbit = new Rabbit(“White Rabbit”); // rabbit.proto == animal
alert( rabbit.eats ); // true
1、设置Rabbit.prototype = animal的字面意思是:“当创建了一个new Rabbit时,把它的[[Prototype]]赋值为animal”。<br />2、这是结果示意图:<br /><br />(1)在上图中,"prototype"是一个水平箭头,表示一个常规属性,[[Prototype]]是垂直的,表示rabbit继承自animal。 |
| --- |
四、注意点
- F.prototype属性(不要把它与[[Prototype]]弄混了)仅在new F被调用时使用,它为新对象的[[Prototype]]赋值。
- 如果在创建之后,F.prototype属性有了变化(F.prototype = <another object>),那么通过new F创建的新对象也将随之拥有新的对象作为[[Prototype]],但已经存在的对象将保持旧有的值。
- F.prototype的值要么是一个对象,要么就是null:其他值都不起作用。
- "prototype"属性仅在设置了一个构造函数(constructor function),并通过new调用时,才具有这种特殊的影响。
五、在常规对象上,prototype没什么特别的:
```javascript
let user = {
name: "John",
prototype: "Bla-bla" // 这里只是普通的属性
};
默认的 F.prototype,构造器属性
一、每个函数都有”prototype”属性,即使我们没有提供它。
二、默认情况下,所有函数都有F.prototype = {constructor:F},所以我们可以通过访问它的”constructor”属性来获取一个对象的构造器。
1、默认的”prototype”是一个只有属性constructor的对象,属性constructor指向函数自身。像这样:
function Rabbit() {}
/* default prototype
Rabbit.prototype = { constructor: Rabbit };
*/
2、我们可以检查一下:
function Rabbit() {}
// by default:
// Rabbit.prototype = { constructor: Rabbit }
alert( Rabbit.prototype.constructor == Rabbit ); // true
3、通常,如果我们什么都不做,constructor属性可以通过[[Prototype]]给所有 rabbits 使用:
function Rabbit() {}
// by default:
// Rabbit.prototype = { constructor: Rabbit }
let rabbit = new Rabbit(); // inherits from {constructor: Rabbit}
alert(rabbit.__proto__.constructor == Rabbit); // true (from prototype)
三、我们可以使用constructor属性来创建一个新对象,该对象使用与现有对象相同的构造器。
【示例1】
function Rabbit(name) {
this.name = name;
alert(name);
}
let rabbit = new Rabbit("White Rabbit");
let rabbit2 = new rabbit.constructor("Black Rabbit");
1、当我们有一个对象,但不知道它使用了哪个构造器(例如它来自第三方库),并且我们需要创建另一个类似的对象时,用这种方法就很方便。
四、但是,关于”constructor”最重要的是,JavaScript 自身并不能确保正确的”constructor”函数值。
1、是的,它存在于函数的默认”prototype”中,但仅此而已。之后会发生什么 —— 完全取决于我们。
2、特别是,如果我们将整个默认 prototype 替换掉,那么其中就不会有”constructor”了。
例如:
function Rabbit() {}
Rabbit.prototype = {
jumps: true
};
let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // false
3、因此,为了确保正确的”constructor”,我们可以选择添加/删除属性到默认”prototype”,而不是将其整个覆盖:
function Rabbit() {}
// 不要将 Rabbit.prototype 整个覆盖
// 可以向其中添加内容
Rabbit.prototype.jumps = true
// 默认的 Rabbit.prototype.constructor 被保留了下来
(1)或者,也可以手动重新创建constructor属性:
Rabbit.prototype = {
jumps: true,
constructor: Rabbit
};
// 这样的 constructor 也是正确的,因为我们手动添加了它
[[Prototype]]
一、在 JavaScript 中,所有对象都有一个特殊的隐藏属性[[Prototype]](如规范中所命名的),它要么为null,要么就是对另一个对象的引用。
二、通过[[Prototype]]引用的对象被称为“原型”
三、当我们从object中读取一个缺失的属性时,JavaScript 会自动从原型中获取该属性。在编程中,这种行为被称为“原型继承”。
四、如果我们想要读取obj的一个属性或者调用一个方法,并且它不存在,那么 JavaScript 就会尝试在原型中查找它。
四、属性[[Prototype]]是内部的而且是隐藏的,但是这儿有很多设置它的方式。
见原型#设置原型的方法:https://www.yuque.com/tqpuuk/yrrefz/ygba8h
prototype 和 Object.getPrototypeOf
一、 prototype 是用于类的,而 Object.getPrototypeOf() 是用于实例的(instances),两者功能一致。
二、类的方法在constructor的prototype中。
示例
一、function A 有一个叫做 prototype 的特殊属性。
1、该特殊属性可与 JavaScript 的 new 操作符一起使用。
2、对原型对象的引用被复制到新实例的内部 [[Prototype]] 属性。
二、
1、执行 var a1 = new A();
(1)JavaScript(在内存中创建对象之后,和在运行函数 A() 把 this 指向对象之前)设置 a1.[[Prototype]] = A.prorotype
2、访问实例的属性
(1)JavaScript 首先会检查它们是否直接存在于该对象上,如果不存在,则会 [[Prototype]] 中查找。
① 这意味着你在 prototype 中定义的所有内容都可以由所有实例有效地共享,你甚至可以稍后更改部分 prototype,并在所有现有实例中显示更改(如果有必要的话)。
三、
var a1 = new A();
var a2 = new A();
a1.doSomething // 指向 Object.getPrototypeOf(a1).doSomething,它就是你在 A.prototype.doSomething 中定义的内容
// 也就是说,Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething
a1.doSomething(); // 相当于执行:Object.getPrototypeOf(a1).doSomething.call(a1)==A.prototype.doSomething.call(a1)
四、[[Prototype]] 看起来就像递归引用, 如 a1.doSomething
、Object.getPrototypeOf(a1).doSomething
、Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething
等等, 直到它被找到或 Object.getPrototypeOf 返回 null。
五、当你执行:
var o = new Foo();
JavaScript 实际上执行的是:
var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);
(或者类似上面这样的)
六、当你执行:
o.someProp;
1、它检查 o 是否具有 someProp 属性。
2、如果没有,它会查找 Object.getPrototypeOf(o).someProp,
3、如果仍旧没有,它会继续查找 Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp。