在javaScript的学习过程中,原型是我们必须搞懂的一个概念,每一个javaScript对象都有一个内置的属性 [[prototype]]
,他指向的是另一个对象,这个对象就是它的原型.
🐟原型
在javaScript中,使用构造函数来创建对象实例,构造函数所有的原型可以用来定义所有实例共享的属性和方法
javascript
person.prototype.say = function () {
console.log('Hello');
}
Person.prototype.age = 18
function Person() {}
let person1 = new Person()
let person2 = new Person()
console.log(person1===person2) // 输出false
通过构造函数创建出来的都是相互独立的对象,但是他们之间会拥有公共的属性他就是构造函数的原型(原型是所有实例对象的公共祖先),用过构造函数创建的实例对象,可以继承该函数原型的属性
arduino
console.log(person1.say())
console.log(person2.say())
// 都是输出 Hello
console.log(person1.age=person2.age)
//输出true,都是18
原型可以减少内存占用,通过原型共享属性和方法,可以避免在每个对象实例中重复存储相同的内容,从而节省内存空间。
比如说,如果张三需要购买最近最火的一辆车------小米su7,雷总支持你定制你的名字和车身的颜色,我们来定义一个Car函数
ini
function Car(owner,color){
this.name='su7'
this.lang = '5000'
this.height = '1400'
this.owner = owner
this.color = color
}
张三喜欢黑色的车身,我们可以通过实例化创建这样一个对象
csharp
let car1 = new Car('张三','black')
这时候李四看见张三买了,也想买一辆,他喜欢红色
csharp
let car2 = new Car('李四','red')
李四和张三购买小米su7的时候,都将函数Car的代码全部走了一遍,呢就意味着这个车子的车身,结构,每次都要重新打造一遍,生产成本特别高,除了颜色和买家的姓名外,车子的结构和参数都是定死的,这样反复执行就会很浪费空间,这个时候原型就可以派上用场了
ini
Car.prototype.name = 'su7'
Car.prototype.lang = 5000
Car.prototype.height = 1400
function Car(color, owner) {
//提出去降低代码的耦合度
this.color = color
this.owner = owner
}
再去创建car1然后访问他的name依旧可以访问到
arduino
console.log(car1.name);
这样写的好处是在批量化生产的时候,不用反复去执行构造车身的固有属性,减少浪费.
呢下面这种写法可不可以实现上述操作呢
yaml
Car.prototype = {
name: 'su7',
lang: 5000,
height: 1400
}
arduino
console.log(car1.name);
依旧可以输出 'su7',但是这种写法是有问题的,因为Car的原型本身就是一个对象,这个操作相当于你重新对他赋值了一个新对象,其他属性会消失.所以不能使用这个写法。
实例对象隐式具有构造函数原型身上的属性,且是只读属性
arduino
Person.prototype.listen = '周杰伦' //隐式具有
function Person() {
this.name = '彭于晏'
}
let p = new Person()
p.listen = '薛之谦' //显示具有
console.log(p.listen); //薛之谦
console.log(Person.prototype.listen); //周杰伦
delete p.listen
console.log(p.listen); //周杰伦
实例对象有一个属性 constructor 很有趣 ,它可以帮助你找到他的构造函数,它可以记录自己是谁创建的
css
访问p的constructor
console.log(p.constructor);
🐟显示原型和隐式原型
一、显示原型
对于函数对象来说,他们每个人都有一个属性,prototype
,它用来实现属性和方法的继承,用函数作为构造函数创建的实例对象,新创建的实例对象的隐式原型会指向构造函数的显示原型
二、隐式原型
对每一个对象来说,他们都有一个隐式原型 __ proto__
他是指向构造函数的prototype原型,这样对象就可以通过隐式原型去访问他的构造函数的属性和方法
显示原型和隐式原型相互结合构成了我们下面要介绍的原型链机制,它的存在可以让对象能访问他的构造函数的属性和方法.
🐟原型链
如果你能明白这张图,原型这一个知识点你就彻底通关了.
- 对于对象
f1
而言 它们是由Function Foo()
实例化构造的,而Function Foo()
这个函数对象的显示原型prototype
是Foo prototype
,所以 f1 的隐式原型__ proto__
是Foo prototype
,因为对对象来说,隐式原型 __ proto__ 他是指向构造函数的prototype原型 - 对于对象o1来说,
o1
的__proto__
属性指向Object.prototype
。因为o1
是由Object
构造函数创建,而Object
函数的prototype
属性指向Object.prototype
。 function Foo()
他也是一个对象所以他有隐式原型,同时他作为一个函数对象,它也具有显示原型。 函数的隐式原型是创建这个函数构造的显示原型,函数是由大写的function Function()
创建的,所以 他的隐式原型指向Function prototype
function Function()
他也是一个对象,构造函数也是对象,所以它既有隐式原型也有显示原型 ,他的隐式原型直接指向他的显示原型Object.prototype
的__proto__
属性指向null
,这里是原型链的尽头。
最后问大家一个问题? 所有的对象都有原型吗,答案是No,Object.create(null)创建出来的对象就没有原型
Object.create()的作用就是创建一个新的对象,它可以让你指定一个原型对象,新创建的对象将会继承他的属性和方法