JavaScript原型、原型链、继承

原型prototype与__proto__

先简单理解以下几句话:

  • 每一个对象都有一个__proto__属性,并且指向它的prototype原型对象
  • 每一个构造函数都会有一个prototype原型对象,prototype原型对象里的constructor指向构造函数本身

直接看下面代码:

function Person(name, age, sex) {
    
    
	this.name = name
	this.age = age
	this.sex = sex
}
// 先打印看看Person是什么
// 打印window,从window上找到Person
console.log(window)

以下是Person的打印结果:

请添加图片描述
可以看到Person为一个构造函数,而这个构造函数身上有一个prototype属性,该属性的constructor又指向该构造函数本身。接下来让该构造函数创建一个实例,看卡这个实例身上有哪些内容。

function Person(name, age, sex) {
    
    
	this.name = name
	this.age = age
	this.sex = sex
}

var p1 = new Person('qwe', 12, male)
console.log(p1)
console.log(p1.prototype)
console.log(p1.__proto__)

)
请添加图片描述

__proto__是[[Prototype]]的getter和setter,这里可以简单的将[[prototype]]理解为__proto__。从上图可以看出p1身上有一个__proto__属性,该属性指向自己的原型对象,而p1自身是没有prototype的。总的来说就是,对象自身的__proto__属性会指向自己的构造函数的prototype,即p1.__proto__ === Person.prototype

原型链

原型链简单的理解就是,多个不同的prototype之间通过__proto__链接成一条链(抽象的一种说明方式)。
如何体现出原型链有什么用,直接看下面代码:

function Person(name, age, sex) {
    
    
	this.name = name
	this.age = age
	this.sex = sex
}
// 在构造函数Person的prototype上增加一个sayName方法
Person.prototype.sayName = function() {
    
    
	console.log(this.name)
}

上面说到过p1.__proto__ === Person.prototype,那可以想到p1.__proto__身上也有sayName方法,通过以下几种调用方法来看一下分别是什么结果:
先看一下p1的结构:

在这里插入图片描述

p1.sayName() // abc
p1.__proto__.sayName() // undefined 
// 这里之所以是undefined是因为this指向的是p1.__proto__,p1.__proto__身上并没有name属性

意思就是说我在构造函数的原型上添加一个方法后,在它的实例身上也会存在该方法。那如果将该实例的__proto__指向别的构造函数的原型呢?看以下代码:

function Person(name, age, sex) {
    
    
	this.name = name
	this.age = age
	this.sex = sex
}

function Dog(name, age) {
    
    
	this.name = name
	this.age = age
}

Person.prototype.sayName = function() {
    
    
	console.log(this.name)
}
Dog.prototype.sayName = function() {
    
    
	console.log(this.name + '  dog')
}

var p1 = new Person('abc', 123, 'male')

// 改变p1的__proto__指向
p1.__proto__ = Dog.prototype

// p1的sayName方法为Dog原型上的sayName方法
p1.sayName() // abc  dog

从上面可以看出实例的__proto__是可以更改的,__proto__指向谁的原型,就能够获取该原型上的方法。
再看一个例子:

var arr = [1, 2, 3]
arr.valueOf() // [1, 2, 3]
console.log(arr.__proto__ === Array.prototype) // true
console.log(arr.__proto__.__proto__ === Object.prototype) // true
console.log(Object.prototype.__proto__) // null

这里就体现出原型链的结构,arr身上是没有valueOf方法的,但是它的__proto__指向Array.prototype,所以会去Array.prototype上找valueOf方法,发现Array.prototype身上也没有valueOf方法,但是Array.prototype.__proto__指向Object.prototype,所以又会去Object.prototype上找到valueOf方法。

继承

继承就是说一个对象可以得到另外一个对象身上的属性和方法,怎么得到,直接看下面事例:
假设我想写一个造车的方法,车的大小和模型的制作过程一样,只有车的名字需要定制。

// 制造车的基本信息
function CarBaseInfo(size, model) {
    
    
	this.size = size + 1
	this.model = '超大号:' + model
}

// 制造车的名字,基本信息的制作过程都一致
function CarName(name, size, modle) {
    
    
	// 使得this指向CarName
	// 如果不使用call,this指向window
	CarBaseInfo.call(this, size, modle)
	this.name = name + 'a'
}

var car1 = new CarName('兰博基尼', 123, '1')
console.log(car1)

在这里插入图片描述

如何继承方法,上面原型链中已经提到过,在构造函数的原型上增加方法,让对象的__proto__指向它即可。

function CarBaseInfo(size, model) {
    
    
	this.size = size + 1
	this.model = '超大号:' + model
}

CarBaseInfo.prototype.speed = function() {
    
    
	console.log('加速')
}

function CarName(name, size, modle) {
    
    
	CarBaseInfo.call(this, size, modle)
	this.name = name + 'a'
}

// 按照思路来说,将CarBaseInfo的原型赋给CarName的原型,car1上就会有speed方法
CarName.prototype = CarBaseInfo.prototype

var car1 = new CarName('兰博基尼', 123, '1')
console.log(car1.speed()) // 加速

这里会存在一个问题,对象是引用类型,意思就是说CarName.prototype = CarBaseInfo.prototype这样赋值后,CarName去更改自己原型上的speed方法时,CarBaseInfo原型上的speed方法也会跟着更改,说好只让你使用,可没让你更改,这样肯定是不允许的。所以这里需要完全将CarBaseInfo.prototype拷贝过来,并且将CarBaseInfo.prototype的constructor指向CarName自己。

// 这里使用Object.create方法,该方法为新建一个对象
CarName.prototype = Object.create(CarBaseInfo.prototype)
// 然后再将CarName.prototype的constructor指向构造函数本身
CarName.prototype.constructor = CarName

完整实现:

// 制造车的基本信息
function CarBaseInfo(size, model) {
    
    
	this.size = size + 1
	this.model = '超大号:' + model
}

CarBaseInfo.prototype.speed = function() {
    
    
	console.log('加速')
}

// 制造车的名字,基本信息的制作过程都一致
function CarName(name, size, modle) {
    
    
	CarBaseInfo.call(this, size, modle)
	this.name = name + 'a'
}

CarName.prototype = Object.create(CarBaseInfo.prototype)
CarName.prototype.speed = function() {
    
    
	console.log('减速')
}

var car1 = new CarName('兰博基尼', 123, '1')
var car2 = new CarBaseInfo(12, '222')
console.log(car1.speed()) // 减速
console.log(car2.speed()) // 加速

相关API

1. hasOwnProperty

用来检测该对象身上是否拥有某个属性,只会检测到自身属性,并不会检测继承来的属性,
注意:hasOwnProperty是可以作为属性名在对象身上的,所以在检测的时候可以采用Object.prototype.hasOwnProperty.call()的方式来检测。

2. instanceof

instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。

function Person(name) {
    
    
	this.name = name
}
function Student(name) {
    
    
	Person.call(this, name)
}

var p1 = new Student('123')
console.log(p1 instanceof Person) // false

p1.__proto__ = Person.prototype
console.log(p1 instanceof Person) // true
function C() {
    
    }
function D() {
    
    }
D.prototype = new C() // D.prototype.__proto__ === C.prototype

var o3 = new D() // o3.__proto__ === D.prototype

o3 instanceof D // true
o3 instanceof C // true C.prototype在o3的原型链上
console.log(o3.__proto__ === D.prototype)
console.log(o3.__proto__.__proto__ === D.prototype.__proto__)
console.log(o3.__proto__.__proto__ === C.prototype)

A instanceof B 即判断B的prototype是否在A的原型链上

猜你喜欢

转载自blog.csdn.net/Wind_AN/article/details/124462341