构造函数

constructor返回创建实例对象时构造函数的引用,此属性的值是对函数本身的引用。

  1. // 普通函数
  2. function parent2(age) {
  3. this.age = age;
  4. }
  5. var p2 = parent2(50);
  6. // undefined
  7. // 普通函数
  8. function parent3(age) {
  9. return {
  10. age: age
  11. }
  12. }
  13. var p3 = parent3(50);

构造函数与普通函数,使用new生成实例的就是构造函数,直接调用就是普通函数。

Symbol是构造函数吗

Symbol是基本数据类型。不支持new。chrome默认其不是构造函数。

symbol实例可以获取constructor属性值,来自Symbol.prototype.constructor。

constructor值只读吗

对于引用来说constructor属性值是可以修改的,但是对于基本类型来说是只读的。

例子:原型继承方案

  1. function Foo() {
  2. this.value = 42;
  3. }
  4. Foo.prototype = {
  5. method: function() {}
  6. };
  7. function Bar() {}
  8. // 设置 Bar 的 prototype 属性为 Foo 的实例对象
  9. Bar.prototype = new Foo();
  10. Bar.prototype.foo = 'Hello World';
  11. Bar.prototype.constructor === Object;
  12. // true
  13. // 修正 Bar.prototype.constructor 为 Bar 本身
  14. Bar.prototype.constructor = Bar;
  15. var test = new Bar() // 创建 Bar 的一个新实例
  16. console.log(test);

对于基本类型来说是只读的,比如:”muyiy”,true,Symbol,null和undefined是没有constructor属性的。因为创建他们的是只读的原声构造函数。说明依赖一个对象的constructor属性不安全。

new的模拟

  1. function create() {
  2. // 1、创建一个空的对象
  3. var obj = new Object(),
  4. // 2、获得构造函数,同时删除 arguments 中第一个参数
  5. Con = [].shift.call(arguments);
  6. // 3、链接到原型,obj 可以访问构造函数原型中的属性
  7. Object.setPrototypeOf(obj, Con.prototype);
  8. // 4、绑定 this 实现继承,obj 可以访问到构造函数中的属性
  9. var ret = Con.apply(obj, arguments);
  10. // 5、优先返回构造函数返回的对象
  11. return ret instanceof Object ? ret : obj;
  12. };

原型

5.1 构造函数,原型,原型链 - 图1

注意点

proto属性在es6时才被标准化,并且有性能问题,不推进使用。推荐Object.getPrototypeOf()

通过改变一个对象的 [[Prototype]] 属性来改变和继承属性会对性能造成非常严重的影响,并且性能消耗的时间也不是简单的花费在 obj.proto = … 语句上, 它还会影响到所有继承自该 [[Prototype]] 的对象,如果你关心性能,你就不应该修改一个对象的 [[Prototype]]。

如果要读取或修改对象的 [[Prototype]] 属性,建议使用如下方案,但是此时设置对象的 [[Prototype]] 依旧是一个缓慢的操作,如果性能是一个问题,就要避免这种操作。

  1. // 获取
  2. Object.getPrototypeOf()
  3. Reflect.getPrototypeOf()
  4. // 修改
  5. Object.setPrototypeOf()
  6. Reflect.setPrototypeOf()

创建一个新对象,同时继承另一个对象的 [[Prototype]] ,推荐使用 Object.create()。

  1. function Parent() {
  2. age: 50
  3. };
  4. var p = new Parent();
  5. var child = Object.create(p);

child 是一个新的空对象,有一个指向对象 p 的指针 proto

优化new

不适用proto

  1. function create() {
  2. // 1、获得构造函数,同时删除 arguments 中第一个参数
  3. Con = [].shift.call(arguments);
  4. // 2、创建一个空的对象并链接到原型,obj 可以访问构造函数原型中的属性
  5. var obj = Object.create(Con.prototype);
  6. // 3、绑定 this 实现继承,obj 可以访问到构造函数中的属性
  7. var ret = Con.apply(obj, arguments);
  8. // 4、优先返回构造函数返回的对象
  9. return ret instanceof Object ? ret : obj;
  10. };

原型链

每个对象拥有一个原型对象,通过 proto 指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null。这种关系被称为原型链 (prototype chain),通过原型链一个对象会拥有定义在其他对象中的属性和方法

总结

  • Symbol 作为构造函数来说并不完整,因为不支持语法 new Symbol(),但其原型上拥有 constructor 属性,即 Symbol.prototype.constructor。
  • 引用类型 constructor 属性值是可以修改的,但是对于基本类型来说是只读的,当然 null 和 undefined 没有 constructor 属性。
  • proto 是每个实例上都有的属性,prototype 是构造函数的属性,这两个并不一样,但 p.proto 和 Parent.prototype 指向同一个对象。
  • proto 属性在 ES6 时被标准化,但因为性能问题并不推荐使用,推荐使用 Object.getPrototypeOf()。
  • 每个对象拥有一个原型对象,通过 proto 指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null,这就是原型链。