这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
一、原型
原型是一个对象
如图所示,我们来 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 的过程:
- 新生成了一个对象
- 链接到原型
- 绑定 this
- 返回新对象
在调用 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