前言
今天我们来聊聊JavaScript中的原型和原型链,如果闭包号称JS中的'一座大山',那么原型就是'一座小山'。JavaScript中的原型(prototype)是一个对象,它被用作其他对象的基础。原型是十分重要的,在开始介绍之前,如果大家不太了解构造函数的话,建议大家看看我之前的文章,配合本篇食用效果更佳哦~
文章链接:构造函数
原型
在Javascript中,原型分为函数原型和对象原型,但我们常说的原型一般被默认为函数原型。
函数原型
定义: 原型(prototype)是函数天生就具有的属性,它定义了构造函数制造出的对象的公共祖先。
通过该构造函数产生的对象,可以隐式继承到原型上的属性和方法。
这段话有点难以理解,我们来看一个例子:
// Person.prototype 原型
Person.prototype.say = function () {
return 'hello'
}
function Person() {
this.name = '来颗奇趣蛋'
this.age = 18
}
let p = new Person()
console.log(p);
console.log(p.say());
我们先来看看这里会输出什么:
Person.prototype就是构造函数Person的原型,函数都会有一个原型,函数.prototype
,函数的原型也是一个对象,因为这个它有键值对和方法,就比如这里的Person.prototype.say()
,同时我们也把函数的原型叫做显示原型
。原型是函数天生就有的属性,函数的原型这个对象拥有的属性和方法被此构造函数创造出来的函数公共拥有,通过该构造函数产生的对象,可以隐式继承到原型上的属性和方法。比如:这里在Person.prototype
里添加了一个方法say()
,所以当我们console.log(p.say())
时,会输出hello.我们知道,对象所拥有的属性和方法,在输出这个对象时,都会被输出出来,那这对象里为什么没有p.say()
呢?因为这里是隐式继承: 关于为什么会隐式继承,我们下面会讲解。
那我们怎么理解公共拥有这个概念呢?
function Car(owner, color) {
this.name = 'BMW'
this.lang = 4900
this.height = 1400
this.owner = owner
this.color = color
}
var car = new Car('菌菌', 'red')
var car2 = new Car('来颗奇趣蛋', 'black')
在上述例子中,this.name, this.lang, this.height
这些属性就是公共的,我们每次实例化对象时,都会重复地把这这些属性重复地读取,这样十分地浪费内存。这里就可以使用原型去解决这类问题了,因为原型的属性和方法被构造函数创造出来的实例对象公共拥有。
Car.prototype.name = 'BMW'
Car.prototype.lang = 4900
Car.prototype.height = 1400
// Car.prototype = {
// name: 'BMW',
// lang: 4900,
// height: 1400
// }
function Car(owner, color) {
this.owner = owner
this.color = color
}
var car = new Car('菌菌', 'red')
var car2 = new Car('来颗奇趣蛋', 'black')
原型可以提取公有属性,简化代码执行
但是我们需要注意:
- 实例对象是无法 修改 原型的属性和方法的
- 实例对象是无法 删除 原型的属性和方法的
比如上述例子我们想要修改原型,要从原型上修改,而不是从实例对象上修改,比如我们想修改车标,就可以Car.prototype.name = '玛莎拉蒂'
.
constructor属性
每个函数的原型都有constructor
属性,它记录的对象是由哪个构造函数创建,我们可以拿一个例子来看看
function Car() {
}
var car = new Car()
console.log(car.constructor);
//输出 [Function: Car]
这里书橱[Function: Car]
,因为对象car是由函数Car()创建,所以这里constructor输出Car。
constructor的值为构造函数本身,那么它的值是否能被修改呢?
function Bus() {
}
function Car() {
}
Car.prototype = {
constructor: Bus
}
var car = new Car()
console.log(car.constructor);
这里的实例对象car由构造函数Car()创造,那么car.constructor的值为构造本身Car,因为constructor是原型的属性,所以我们可以在原型上修改它,最后输出
对象原型
对象也具有一个原型,它是因为对象被构造函数实例化之后而产生的,我们称之为隐式原型,而我们为了区分显示原型和隐式原型,将隐式原型用_proto_表示。而隐式原型同样也是一个对象,它能够继承构造函数中显示原型的属性和方法,这里我们来概括一下:
实例对象会通过自己的隐式原型去继承构造函数显示原型上的属性和方法
-
当访问对象属性时,先找对象显示具有的属性,没找到再去找对象的隐式原型
-
实例对象的隐式原型 === 构造函数的显示原型
我们看到第一点所说,当访问对象属性时,先找对象显示具有的属性,如果没找到就去找对象的隐式原型,而对象的隐式原型就等于构造函数的显示原型,所以就跟我们一开始的函数原型串通起来了。为什么实例对象可以隐式地继承到函数原型上的属性和方法?
这其实是new
的功劳:
Person.prototype.name = '来颗奇趣蛋'
function Person(age){
this.age = age
// var this = {
// age: age
// _proto_: Person.prototype
// }
// return this
}
var p = new Person()
console.log(p.name);
在我们上篇文章所讲的new
+ 构造函数为什么会有值,是因为返回了一个this对象,但其实返回的this对象里并不只有传入的属性,还有对象的隐式原型,值为构造函数的显示原型,这里_proto_: Person.prototype
,当构造函数创造一个实例对象时,将return this 为实例对象,所以实例对象可以隐式地继承到函数原型上的属性和方法。
总结
函数原型 (显示原型)
- 定义: 原型(prototype)是函数天生就具有的属性,它定义了构造函数制造出的对象的公共祖先。
通过该构造函数产生的对象,可以隐式继承到原型上的属性和方法
-
意义 可以提取公有属性,简化代码执行
-
原型增删改查
- 实例对象是无法 修改 原型的属性和方法的
- 实例对象是无法 删除 原型的属性和方法的
对象原型 (隐式原型)
实例对象会通过自己的隐式原型去继承构造函数显示原型上的属性和方法
-
当访问对象属性时,先找对象显示具有的属性,没找到再去找对象的隐式原型
-
实例对象的隐式原型 === 构造函数的显示原型
今天的内容就到这啦,如果你觉得小编写的还不错的话,或者对你有所启发,请给小编一个辛苦的赞吧