外观
原型
基础
原型是所有对象共有的一个属性,它们都指向唯一的原型对象。类实例化为对象时,每个对象之间都是相互独立的,但它们的原型对象都是同一个,因此原型对象可用于定义公有方法,多个对象使用的方法在内存中都是同一个方法。
function Obj() {
this.func = () => {}
}
let obj1 = new Obj();
let obj2 = new Obj();
console.log(obj1.func === obj2.func) //false
function Obj2() {}
Obj2.prototype.func = () => {}
let obj3 = new Obj2();
let obj4 = new Obj2();
console.log(obj3.func === obj4.func) //true构造函数和原型函数的this都指向实例化的对象。
通过原型,可以实现为对象/类拓展功能,如给数组扩展一个累加功能。
Array.prototype.sum = function () {
return this.reduce((prev, curr) => {
return prev + curr
})
}
arr = [1, 2, 3, 4];
console.log(arr.sum()) //10constructor属性
每个原型对象都有个constructor属性,它指向该原型对象的构造函数。
对象原型__proto__
每个对象都有一个__proto__指向构造函数的prototype原型对象。之所以对象可以使用构造函数Prototype原型对象的属性和方法,就是因为此属性存在。
__proto__是JS非标准属性,它和[[prototype]]意义相同,用于指明当前实例对象指向哪个原型对象prototype。__proto__属性里也有一个constructor属性,指向创建该实例的构造函数。
原型继承
继承是面向对象的一个基本特点,JavaScript的继承就是通过原型实现的。
若一个子类的prototype属性指向父类,则子类就继承自父类。
若我们使用function和this创建对象,则类的继承是通过将子类函数的prototype属性指向父类的实例实现的。
function Father() {
this.key1 = "父类"
}
function Son() {
this.key2 = "子类"
}
Son.prototype = new Father();
let obj1 = new Father();
console.log(obj1.key1)
let obj2 = new Son();
console.log(obj2.key1)
console.log(obj2.key2)实现原理就是多个子类都同时使用了一个父类,若对父类的行为做更改,所有继承自该父类的子类也会被修改。
原型链和instanseof运算符
下面的图和例子描述了JavaScript中如何通过原型实现继承的。
function Fn () {}
为了方便区分__proto__属性和prototype对象,我们习惯称前者为隐式原型,称后者为显式原型。而隐式原型还等同于[[prototype]]。
JavaScript规定任何对象都有自己的原型对象,函数也是对象,函数实际上是由构造函数Function实例化而来的。且任何对象都可以充当其他对象的原型对象,原型对象也有自己的原型对象,因此原型链就形成了。
在上方的例子中,定义Fn函数的过程就相当于const Fn = new Function()。在这里,Fn是一个对象,Function是一个构造函数。__proto__是对象具有的属性,在本例中,Fn、Function.prototype、Object.prototype都有这个属性。prototype是一个类型为对象的属性,在本例中,Function、Object两个构造函数拥有此对象属性。此外,Fn是Function构造函数的实例。
理清以上关系后,我们就可以梳理这些对象、构造函数中原型是如何串联起来的。Fn对象有一个__proto__属性,它指向的是Function构造函数的prototype对象,即Fn.__proto__ === Function.prototype。
console.log(Fn.__proto__ === Function.prototype) // trueFunction.prototype又是一个对象,它也有__proto__属性,它指向的是Object的prototype对象,所以Function继承自Object。
console.log(Function.prototype.__proto__ === Object.prototype) // true由于Object为最顶层父类,它不继承自任何类,因此Object.prototype.__proto__也就不指向任何原型对象了,为null。
console.log(Object.prototype.__proto__) // null我们在继承时使用了如下的语法。
function MyClass() {}
MyClass.prototype = new Object();
console.log(MyClass.prototype.__proto__ === Object.prototype) // true可以看到在指定原型对象实现继承时,原型链就已经形成了。而JavaScript在寻找对象的一个属性或方法时,若构造函数中没有,则沿着原型链向上寻找,继承的功能就实现了。