JavaScript原型与原型链

与大部分面向对象语言不同,JavaScript中并没有引入类(class)的概念,但JavaScript仍然大量地使用了对象,为了保证对象之间的联系,JavaScript引入了原型与原型链的概念。

QQ截图20200526181505.png

构造函数与原型

在Java中,声明一个实例的写法是这样的:

ClassName obj = new ClassName()

JavaScript中也加入了类似的new操作符:

var obj = new FunctionName()

与Java不同的是,JavaScript中的new操作符后面跟的是函数名,而不是类名。因为JavaScript使用过构造函数而非类来创建实例。

声明一个构造函数并通过构造函数创建实例的过程:

function Dog(namecolor){
   this.name = name
   this.color = color
   this.bark = () => {
       console.log('wangwang~')
   }
}

const dog1 = new Dof('dog1''black')
const dog2 = new Dof('dog2''black')

但上面的例子有一个大问题:实例中的属性继承自构造函数,且都是独立不共享的,这无疑会造成浪费。对于namecolor来说,由于属性一定不同,属性必须独立,但是bark()方法都是一样的,这就需要原型(prototype),使其共享使用。

原型的一些基本概念:

  • 每一个构造函数都拥有一个prototype属性,这个属性指向一个对象,也就是原型对象。当使用这个构造函数创建实例的时候,prototype属性指向的原型对象就成为实例的原型对象。

  • 原型对象默认拥有一个constructor属性,指向指向它的那个构造函数(也就是说构造函数和原型对象是互相指向的关系)。

  • 每个对象都拥有一个隐藏的属性[[prototype]],指向它的原型对象,这个属性可以通过 Object.getPrototypeOf(obj)obj.__proto__ 来访问。

  • 实际上,构造函数的prototype属性与它创建的实例对象的[[prototype]]属性指向的是同一个对象,即 对象.__proto__ === 函数.prototype

  • 如上文所述,原型对象就是用来存放实例中共有的那部分属性。

  • 在JavaScript中,所有的对象都是由它的原型对象继承而来,反之,所有的对象都可以作为原型对象存在。

  • 访问对象的属性时,JavaScript会首先在对象自身的属性内查找,若没有找到,则会跳转到该对象的原型对象中查找。

学会了原型,就可以对上面的例子进行改造,把bark()放到Dog构造函数的原型中:

function Dog(namecolor){
   this.name = name
   this.color = color
}

Dog.prototype.bark() = () => {
   console.log('wangwang~')
}

const dog1 = new Dog('dog1''black')
const dog2 = new Dog('dog2''white')
dog2.bark = () => {
   console.log('miaomiaomiao???')
}

dog1.bark()  // wagnwang~
dog2.bark()  // miaomiaomiao???

dog2重写了bark()方法,但没有对dog1造成影响,因为它重写的bark()方法的操作实际上是对自己添加了一个新的方法,使原型中的bark()方法被覆盖了,原型中的方法是不会受影响的。若想修改原型中的方法,需要通过构造函数的prototype属性:

Dog.prototype,bark = () => {
   console.log('haha ~')
}
dog1.bark()  //  haha ~
dog2.bark()  //  haha ~

小结

将实例中共有的属性放到原型对象中,让所有实例共享这部分属性。如果想统一修改所有实例继承的属性,只需要直接修改原型对象中的属性即可。

同时,每个实例仍然可以重写原型中已经存在的属性来覆盖这个属性,并且不会影响到其他的实例。

原型链与继承

JavaScript中的所有对象都是由它的原型对象继承而来的,而原型对象自身也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链。

所有原型链的终点都是Object函数的prototype属性,因为在JavaScript中的对象都默认有Object()构造。Obejct.prototype指向的原型对象,同样拥有原型,不过它的原型是null,而null则没有原型。

通过原型链就可以在JavaScript中实现继承,JavaScript中的继承相当灵活,有多种继承方法,比如组合继承。

function Dog(namecolor){
   this.name = name
   this.color = color
}

Dog.prototype.bark = () => {
   console.log('wangwang~')
}

function Husky(namecolorweight){
   Dog.call(thisnamecolor)
   this.weight = weight
}

Husky.prototype = new Dog()


这里声明了一个新的构造函数Husky,通过call()方法继承Dog中的属性(call方法的作用可以简单理解为将Dog中的属性添加到Husky中),并添加了一个weight属性,然后用Dog函数创建了一个实例作为Husky的原型对象并赋值给Husky.prototype以继承方法。这样,通过Husky函数创建的实例就拥有了Dog中的属性和方法。

对我有帮助
40人认为有帮助

相关帮助