JavaScript 构造函数与原型链

创建对象

JavaScript 创建对象的方法:1. 使用构造函数 2. 使用字面量 3. Object.create()

使用构造函数

使用构造函数作为对象的模板生成实例对象,函数内用 this 关键字代表生成的对象实例。就是操作一个空对象,将其”构造”为需要的样子。

new 的过程:

  • 创建一个新的空对象;
  • 新对象的 __proto__ 指向构造函数的 prototype;
  • 新对象赋值给内部的 this 并执行;
  • 返回新对象 this;

使用 new 命令,构造函数内部的 this 代表了新生成的实例对象,this.price 表示实例对象有一个 price 属性,值是 10。

1
2
3
4
5
6
//执行构造函数,返回一个实例对象。
var Apple = function () {
this.price = 10;
};
var v = new Apple();
v.price // 10

不使用 new 命令,直接调用构造函数,构造函数就变成了普通函数,并不会生成实例对象。

1
2
3
4
5
6
7
8
var Apple = function (){
this.price = 10;
};
var v = Apple();
v // undefined
price // 10
window.price //10
//上面代码中,变量v变成了undefined,price属性变成了全局变量。

使用字面量

1
2
3
4
5
6
7
8
9
10
11
12
// getPrototypeOf() 返回指定对象的原型

//使用数组字面量创建一个数组:
let arr = [1,2,3,4]
Object.getPrototypeOf(arr) === Array.prototype //true

//使用对象字面量创建一个对象:
let obj = {name: 'jack'}
Object.getPrototypeOf(obj) === Object.prototype //true

//类似字面量方式
var o2 = new Object({name:'o2'});

使用字面量创建对象时,实际上就调用了这些构造函数生成实例对象 (如Object、Array、Function等)。

使用 Object.create()

create 是 Object 构造函数的一个方法,该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。该实例对象完全继承参数对象的属性和方法:

1
2
3
4
var A = {name:'jack'}
var B = Object.create(A)
B.__proto__ === A // true
B.name // jack

Object.create 方法生成的新对象,动态继承了原型。在原型上添加或修改任何方法,会立刻反映在新对象之上。

1
2
3
4
5
A.age = 24
A // {name: "jack", age: 24}
A.__proto__ === Object.prototype //true
B //{}
B.__proto__ // {name: "jack", age: 24}

原型链

每个构造函数都有一个 prototype 属性指向一个对象,用来存放共有属性的地址,每个实例对象都有一个 __proto__ 属性指向它的构造函数的 prototype 属性。

每个原型对象都有一个 constructor 属性,指向相关联的构造函数,所以构造函数和构造函数的 prototype 是可以相互指向的。

1
Array.__proto__ === Function.prototype //true

构造函数的原型对象的所有属性和方法,都能被实例对象共享,如果属性和方法定义在构造函数的 prototype 上,那么所有实例对象就能共享,不仅节省了内存,还体现了实例对象之间的联系。

1
2
3
4
5
let arr = [1,2,3,4] 
arr.__proto__ === Array.prototype //true
Array.prototype.__proto__ === Object.prototype //true
Object.prototype.__proto__ === null //true
// arr ---> Array.prototype ---> Object.prototype ---> null
  • Array.prototype 继承了 Object.prototype 的属性和方法。arr 继承了 Array.prototype 的属性和方法,同时也继承了 Object.prototype 的属性和方法。(所有对象都有 valueOftoString 方法的原因是从 Object.prototype 继承的)
  • Object.prototype 的原型是null,没有任何属性和方法,也没有自己的原型。所以原型链的尽头就是null。
  • 所有对象都有一个 __proto__ 属性指向一个原型(prototype),从实例对象到原型,再到原型的原型…… 这样就形成了一个原型链(prototype chain)。

总结:

  1. Object 是所有对象的爸爸,所有对象都可以通过 __proto__ 找到它;
  2. Function 是所有函数的爸爸,所有函数都可以通过 __proto__找到它;
  3. Function.prototype 和 Object.prototype 是两个特殊的对象,他们由引擎来创建;
  4. 除了以上两个特殊对象,其他对象都是通过构造函数 new 出来的;
  5. 函数的 prototype 是一个对象,也就是原型;
  6. 对象的 __proto__ 指向原型, __proto__ 将对象和原型连接起来组成了原型链;
  7. 关于继承,实例对象可以访问这根链条上的属性和方法,其实数组都继承于 Array.prototype,函数都继承于 Function.prototype;(判断是否为直接生成的实例,用 constructor 比用 instanceof 更严谨)

构造函数、原型、实例、原型链之间的关系:

1544807307596-1e74bf82-9587-458b-bcff-62dfd57b0c87.png

1
2
3
4
5
6
7
8
//Object 的构造函数是 Function。
Object.__proto__ === Function.prototype //true
//Function 的构造函数是它自己。
Function.__proto__ === Function.prototype //true
//Function.prototype 的构造函数是 Object。
Function.prototype.__proto__ === Object.prototype //true
//Object.prototype 的原型是 null
Object.prototype.__proto__ === null; //true

Reference:
https://wangdoc.com/javascript/oop/index.html
https://github.com/KieSun/Dream/issues/2