JavaScript进阶笔记(五):构造函数、原型和原型链
JavaScript进阶笔记(五):构造函数、原型和原型链
镇长一、什么是构造函数?
constructor
返回创建实例对象时构造函数的引用。此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。
构造函数和普通函数没有区别,不同点是构造函数使用 new 来生成实例对象,直接调用就是普通函数。
注意:构造函数首字母通常大写。
1.1 Symbol 是构造函数吗?
Symbol
是基本数据类型,他不支持 new Symbol()
操作。
生成 Symbol
实例直接使用 Symbol()
即可。
虽然不是构造函数,但可以获取到 constructor
属性值。
1 | var sym = Symbol('sym') |
这个 constructor
是哪里来的?其实是 Symbol
原型上的。Symbol.prototype.construnctor
返回创建实例原型的函数。
1.2 constructor 值是只读的吗?
分为两种情况:属性值为基本数据类型是只读。属性值为引用类型是可以修改的。
可以直接对 constructor 进行赋值。
new
的实现:
1 | function create() { |
二、原型
JavaScript 是一种基于原型的语言 (prototype-based language),这个和 Java 等基于类的语言不一样。
每个对象拥有一个原型对象,对象以其原型为模板,从原型继承方法和属性,这些属性和方法定义在对象的构造器函数的 prototype 属性上,而非对象实例本身。
1 | function Person () { |
构造函数有个指针指向原型,原型有个指针指向构造函数。
2.1 __proto__
在原型(Person.prototype)中有个 __proto__
属性,它是访问器属性。通过它可以访问内部的 [[Prototype]]
。[[Prototype]]
是对象的内部属性,外部代码无法直接访问。
1 | var p = new Person() |
p.__proto__
可以直接访问到对象的原型,__proto__
是每个实例都有的属性。prototype
是构造函数的属性。它们指向同一个对象。
注意:__proto__
是 ES6 的标准,兼容性问题和性能问题,推荐使用 Object.getPrototypeOf()
。考虑性能问题,避免修改 [[prototype]]
。
如果创建一个新对象,同时继承另一个对象的 [[prototype]]
,推荐用 Object.create()
。
优化 new 的实现:
1 | function create() { |
三、原型链
每个对象拥有一个原型对象,通过 proto 指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null。这种关系被称为原型链 (prototype chain),通过原型链一个对象会拥有定义在其他对象中的属性和方法。
1 | var p = new Person() |
p
实例也有 constructor
,并不是。实例本身是没有 constructor
属性的,是通过原型链向上查找 __proto__
,最终查找到 constructor
属性,该属性指向 Person
。
1 | p.constructor === Person //true |
小结
Symbol
作为构造函数来说并不完整,因为不支持语法new Symbol()
,但其原型上拥有constructor
属性,即Symbol.prototype.constructor
。- 引用类型
constructor
属性值是可以修改的,但是对于基本类型来说是只读的,当然 null 和 undefined 没有constructor
属性。 __proto__
是每个实例上都有的属性,prototype 是构造函数的属性,在实例上并不存在,所以这两个并不一样,但p.__proto__
和Parent.prototype
指向同一个对象。__proto__
属性在 ES6 时被标准化,但因为性能问题并不推荐使用,推荐使用Object.getPrototypeOf()
。- 每个对象拥有一个原型对象,通过
__proto__
指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null,这就是原型链。