JavaScript 系列之原型(一)

355 阅读3分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

一、原型

原型是一个对象

image.png

如图所示,我们来 new 两个 Foo 的实例来看看:

functionFoo(){

}
let f1 = new Foo()
let f2 = new Foo()

1.1 prototype

每个函数都有一个 prototype 属性(除了 Function.prototype.bind()),该属性指向原型。

function Foo(){

}

Foo.prototype.msg = 'hello';
var f1 = new Foo();
var f2 = new Foo();
console.log(f1.msg) // hello
console.log(f2.msg) // hello

基本上所有函数都有这个属性,但是也有一个例外

let fun = Function.prototype.bind()

如果你以上述方法创建一个函数,那么可以发现这个函数是不具有 prototype 属性的。

当我们声明一个函数时,这个属性就被自动创建了。

function Foo(){}

构造函数的 prototype 属性指向原型对象

原型是一个对象,每一个 JavaScript 对象(null 除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个实例对象都会从原型继承属性。

如上栗子,Foo 函数也是一个对象,该构造函数对象有一个 prototype 属性,指向实例 f1 和 f2 的原型对象即 Foo.prototype 。而在原型对象上挂在的属性和方法都属于公共属性或者公共方法,由所有该构造函数 new 的实例对象所共有,即 f1 和 f2。

1.2 __proto__

每个对象都有 __proto__ 属性,指向了创建该对象的构造函数的原型。

console.log(f1.__proto__ === Foo.prototype); // true

那原型也是对象,构造函数也是对象,它们的 __proto__ 属性又指向哪里?答案依然还是指向了创建该对象的构造函数的原型

1.2.1 构造函数的 __proto__

函数也是一个对象,函数的构造函数是 Function ,因此 __proto__ 指向了 Function.prototype

这是每个对象都有的隐式原型属性,指向了创建该对象的构造函数的原型。其实这个属性指向了 [[prototype]],但是 [[prototype]] 是内部属性,我们并不能访问到,所以使用 __proto__ 来访问。

因为在 JS 中是没有类的概念的,为了实现类似继承的方式,通过 __proto__ 将对象和原型联系起来组成原型链,得以让对象可以访问到不属于自己的属性。

当我们使用 new 操作符时,生成的实例对象拥有了 __proto__ 属性。

function Foo(){}
// 这个函数是 Function 的实例对象
// function 就是一个语法糖
// 内部调用了 new Function(...)

所以可以说,在 new 的过程中,新对象被添加了 __proto__ 并且链接到构造函数的原型上。

new 的过程:

  1. 新生成了一个对象
  2. 链接到原型
  3. 绑定 this
  4. 返回新对象

在调用 new 的过程中会发生以上四件事情,我们也可以试着来自己实现一个 new,在这之前先来重温一个知识点:

// this指向调用的对象,当用了call后,能够改变this的指向,也就是指向传进来的对象,这是关键
// shift()函数是将数组的第一个值删除,并返回,这里是得到obj
[].shift.call(arguments)

因为 arguments 构造如下,第一个是长度
{length:2,0:'first',1:'second'};
function create() {
  // 创建一个空的对象
  let obj = new Object();
  // 获得构造函数:将第一个参数 Person 删除并返回
  let Con = [].shift.call(arguments);
  // 链接到原型
  obj.__proto__ = Con.prototype;
  // 绑定 this,执行构造函数
  let result = Con.apply(obj, arguments);
  // 确保 new 出来的是个对象    return
  typeof result === "object" ? result : obj;
}

function Person(name, age) {
  this.name = name;
  this.age = age;
}
var a = create(Person, "小花", "16");
console.log(a); // Person {name: "小花", age: "16"}

对于实例对象来说,都是通过 new 产生的,无论是 function Foo() 还是 let a = { b : 1 }

function Foo(){}
// function 就是个语法糖
// 内部等同于 new Function()

let a = { b: 1 }
// 这个字面量内部也是使用了 new Object()

1.2.2 原型的 __proto__

原型也是个对象,原型的构造函数是 Object,因此 __proto__ 指向了 Object.prototype,而 Object.prototype 这个原型对象最终指向 null。

1.3 constructor

  • 实例的属性 constructor 指向构造函数
function Person() {

}
var person = new Person();
console.log(person.constructor === Person); // true
  • 构造函数的原型的 constructor 会指向这个函数
console.log(Person.prototype.constructor === Person); // true