[[Prototype]]
定义一个对象,当访问对象内属性时,可以找到就可以相当于调用的是对象
[[GET]]
方法,找到属性对应的值,当找不到属性时,这个时候就会去寻找[[Prototype]]
链上的属性了,如果还是找不到那就会继续在链上找[[Prototype]]
,从而继续查找,直到查找完整个链,最终如果还是未曾找到,那么就会返回undefined
。如:
function Person(){
this.name = 'Lee';
}
Person.prototype.age = 18;
var person = new Person();
console.log(person); // Person { name: "Lee", [[Prototype]]: { age: 18 } }
console.log(person.name); // Lee
console.log(person.age); // 18
console.log(person.sex); // undefined
Object.prototype
内置
[[Prototype]]
链最终都会指向Object.prototype
,它包含了js中许多通用的功能(如:toString、hasOwnProperty等)
属性设置和屏蔽
- 属性设置并不是在原型上进行修改或设置的,而是在对象上进行设置的(属性的默认参数下)
function Person(){ } Person.prototype.name = 'Tom'; var person = new Person(); console.log(person.name); // Tom person.name = 'Lee'; console.log(person); // Person { name: "Lee", [[Prototype]]: { name: "Tom" } } console.log(person.name); // Lee
- 定义的name属性屏蔽了原型上的name属性
function Person(){ this.name = 'Lee'; } Person.prototype.name = 'Tom'; var person = new Person(); console.log(person.name); // Lee
针对
person.name = 'Lee';
的三种情况
- 属性设置时,当原型上的属性
writable
值为true
或false
时- 【1】
writable = true
function Person(){ } Object.defineProperty(Person.prototype, 'name', { value: 'Tom', writable: true }) var person = new Person(); person.name = 'Lee'; console.log(person); // Person { name: "Lee", [[Prototype]]: { name: "Tom" } } console.log(person.name); // Lee
- 【2】
writable = false
function Person(){ } Object.defineProperty(Person.prototype, 'name', { value: 'Tom', writable: false }) var person = new Person(); person.name = 'Lee'; console.log(person); // Person { [[Prototype]]: { name: "Tom" } } console.log(person.name); // Tom
- 【1】
- 【3】属性设置时,当原型上的属性配置了
setter
属性时function Person() { } var _name = 'Tom'; Object.defineProperty(Person.prototype, 'name', { get: () => _name, set: value => _name = value }) var person = new Person(); console.log(person.name); // Tom person.name = 'Lee'; console.log(person); // Person { name: "Lee", [[Prototype]]: { name: "Lee" } } console.log(person.name); // Lee
- 产生隐式屏蔽的另一种写法
“类”
"类"函数
调用
new Person()
时会创建person
,其中一部就是给person
一个内部的[[Prototype]]
链接,关联到Person.prototype
指向的那个对象。
function Person() {
this.name = 'Lee'; }
var person = new Person();
console.log(Person.prototype); // { constructor: ƒ Person() }
console.log(Object.getPrototypeOf(person)); // { constructor: ƒ Person() }
console.log(Object.getPrototypeOf(person) === Person.prototype); // true
一个类可以实例化多个对象,但多个对象的
[[Prototype]]
关联的实际上是同一个对象,因此这些对象并不是完全失去联系的,他们是相互关联着的。
function Person() {
this.name = 'Lee'; }
var lee = new Person();
var tom = new Person();
console.log(Object.getPrototypeOf(lee) === Object.getPrototypeOf(tom)); // true
- 关于名称
- 关联对象(“类”)—> 这个机制被称为
原型继承
function Foo(){ this.name = "Foo"; } function Bar(){ this.age = 18; } Bar.prototype = Foo; // Bar 继承 Foo var foo = new Foo(); var bar = new Bar(); console.log(bar.name); // Foo console.log(Object.getPrototypeOf(bar)); // ƒ Foo(){ this.name = "Foo"; }
- 这种继承机制实际上并不是复制操作,而是在两个对象间创建的关联操作,这样就可以让一个对象
委托访问
另一个对象的的属性和函数。
- 关联对象(“类”)—> 这个机制被称为
“构造函数”
我们认为
Person
是一个类的原因是因为我们使用了关键词new
,实际上我们的构造函数指向的是原型属性.constructor
上的值。如下示例:
function Foo() {
}
console.log(Foo.prototype.constructor); // ƒ Foo() { }
var foo = new Foo();
console.log(foo.constructor); // ƒ Foo() { }
console.log(Object.getPrototypeOf(foo)); // {constructor: ƒ Foo()}
function Person() {
}
Person.prototype.constructor = null;
var person = new Person();
console.log(person.constructor); // null
console.log(Object.getPrototypeOf(person)); // {constructor: null}
- 构造函数还是调用
函数不是构造函数
,但是当使用new
修饰时,函数调用就会变成构造函数调用
技术
this.name = name;
为每个对象添加了一个name
属性foo1.say()
没有在但前对象上找到函数,就会通过委托去Foo原型
上去寻找function Foo(name) { this.name = name; } Foo.prototype.say = function(){ return this.name; } var foo1 = new Foo('Lee'); var foo2 = new Foo('Tom'); console.log(foo1, foo1.say()); // Lee console.log(foo2, foo2.say()); // Tom
(原型)继承 - 四种继承方式
function FunA(a) {
this.a = a;
}
FunA.prototype.logA = function () {
console.log(this.a);
}
function FunB(b) {
this.b = b;
}
FunB.prototype.logB = function () {
console.log(this.b);
}
FunB.prototype = Object.create(FunA.prototype);
// FunB.prototype = FunA.prototype;
// FunB.prototype = new FunA('aaa');
// Object.setPrototypeOf(FunB.prototype, FunA.prototype);
console.log(FunB.prototype);
FunB.prototype = Object.create(FunA.prototype);
- 创建一个新的
FunB.prototype
对象并把它关联到FunA.prototype
- 创建一个新的
FunB.prototype = FunA.prototype;
- 不会创建一个关联到
FunB.prototype
的新对象,他只是让FunB.prototype
直接引用FunA.prototype
本身
- 不会创建一个关联到
FunB.prototype = new FunA();
- 会创建一个关联到
FunB.prototype
的新对象,但是它使用的是FunA
的构造函数调用
,如果FunA
本身存在一些副作用(如写日志、修改状态、注册到其他对象、给this添加数据属性,等等)
的话,就会影响到FunB
的后代,后果不堪设想
- 会创建一个关联到
- ES6
Object.setPrototypeOf(FunB.prototype, FunA.prototype);
Object.create
会抛弃之前的FunB.prototype
创建一个新的对象Object.setPrototypeOf
会直接修改现有的FunB.prototype
检查"类"关系
instanceof
- 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
isPrototypeOf
- 方法用于测试一个对象是否存在于另一个对象的原型链上
Object.getPrototypeOf
- 方法返回指定对象的原型(内部[[Prototype]]属性的值)
__proto__
(不推荐)- 暴露了通过它访问的对象的内部[[Prototype]] (一个对象或 null)
对象关联(原型链)
如果在对象上没有找到需要的属性或者方法引用,引擎就会继续在
[[Prototype]]
关联的对象上进行查找。同理,如果在后者中也没有找到需要的 引用就会继续查找它的[[Prototype]]
,以此类推。这一系列对象的链接被称为“原型链”。
Object.create()
的polyfill
代码(兼容写法)// Object.create() 的 polyfill 代码 if (!Object.create) { Object.create = function (o) { function F() { } F.prototype = o; return new F(); } }