温故而知新 - 重新认识JavaScript的原型
什么是prototype
?
实现面向对象的一个重要机制。
JavaScript
中对象有一个特殊的[[Prototype]]
内置属性,其实就是对于其他对象的引用。几乎所有的对象在创建时[[Prototype]]
属性都会被赋予一个非空的值,可以通过__proto__
访问该属性。此外,prototype
是JavaScript
中函数的一个属性。
所有普通的[[Prototype]]
链最终都会指向内置的Object.prototype
。
为什么需要prototype
?
记住:JavaScrip
没有类来作为对象的抽象模式。JavaScript
中只有对象。
反向思考,如果没有prototype
会怎样?
var obj = {}
obj.toString()
没有prototype
,那toString
就是undefined
的,毕竟obj
是个空对象。
总结来看,原型及其相关,最大的作用是复用能力,带来的好处最直观的就是节省内存空间:不再需要每个对象都重新定义一个相同逻辑功能或意义的属性。
关注点
1. 属性设置与屏蔽
myObject.foo = "bar";
若myObject
对象中包含名为foo
的普通数据访问属性,这条赋值语句只会修改已有的属性值。
若foo
不是myObject
自身的属性,[[Prototype]]
链就会被遍历,类似[[Get]]
操作。原型链上找不到foo
,则foo
会被直接添加到myObject
上。
但,foo
若存在于原型链上,赋值语句myObject.foo = "bar"
的行为存在差异:
- 倘若在
[[Prototype]]
链上存在foo
属性,同时不是只读(writable: false
),则会直接myObject
中添加一个名为foo
的新属性,它是屏蔽属性- 若在
[[Prototype]]
链上存在foo
,但是为只读(writable: false
),则无法修改已有属性或者在myObject
上创建屏蔽属性。在严格模式下,代码会抛出一个错误。否则,赋值语句会被忽略。总之,不会发生屏蔽。需要注意的是,这个限制只存在于=
赋值中,使用Object.defineProperty(...)
并不会受影响
来看代码,测试执行下:
class Parent {}
class Son extends Parent {}
Object.defineProperty(Parent, 'foo', {writable: false, value: 'bar'});
Son.foo; // 输出"bar"
Son.foo = "foo";
Son.foo; // 依旧输出"bar", 上一条赋值语句被忽略了
// 但是可以使用Object.defineProperty(...)
Object.defineProperty(Son, 'foo', {value: 'son_bar'});
Son.foo; // 输出"son_bar"
Parent.foo; // 输出"bar"
2. 关于class
function Foo() {}
var a = new Foo();
Object.getPrototypeOf(a) === Foo.prototype; // true
调用new Foo()
时会创建a
,其中一步就是将a
内部的[[Prototype]]
链接到Foo.prototype
所指向的对象。
需要关注:
1.在JavaScript中,不能创建一个类的多个实例,只能创建多个对象,它们[[Prototype]]
关联的是同一个对象。
2.并没有初始化一个类,实际上并没有从'类'中复制任何行为到一个对象中,只是让两个对象互相关联。
3. 继承的本质
由于JavaScrip
没有类来作为对象的抽象模式,JavaScript
中只有对象。继承的本质,可以理解为对象的关联
Object.create()
Object.create(null)
会创建一个拥有空(或者说null
)[[Prototype]]
链接的对象,这个对象无法进行委托。由于这个对象没有原型链,所以instanceof
操作符无法进行判断,因此总是会返回false。这些特殊的空[[Prototype]]
对象通常被称作"字典",他们完全不会受到原型链的干扰,因此非常适合用来存储数据。
// Object.create()的polyfill代码
if (!Object.create) {
Object.create = function(target) {
function F() {}
F.prototype = target;
return new F();
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。