原型
- 在 JavaScript 中,每个对象都有一个原型(prototype)对象,它是一个指向另一个对象的引用。当访问一个对象的属性时,如果该对象本身没有该属性,则 JavaScript 引擎会沿着该对象的原型链向上查找,直到找到该属性或者到达原型链的末尾。
一、原型对象
- JavaScript 规定,每一个构造函数都有一个 prototype 属性(显示原型),指向另一个对象,所以我们也称为原型对象。
- 原型对象可以挂载函数,对象实例化不会多次创建原型对象里面的函数,节约内存
- 实例对象和原型对象关系 (三角)
- 构造函数、原型对象- this指向
- 构造函数和原型对象中的this 都指向 实例化的对象(new出来的那个对象)。
// 构造函数中的this指向 实例对象 new出来的那个对象
function Person(name){
this.name = name
// console.log(this)
}
Person.prototype.sayHi = function(){
console.log(this)
}
const p = new Person('jl')
const mby = new Person('mby')
p.sayHi() //this 指向 Person {name: 'jl'}
mby.sayHi() //this 指向 Person {name: 'mby'}
- 注意:
- 箭头函数不能做构造函数,因为箭头函数里面没有 this
- 原型对象里面的函数如果需要用到this,也不要用箭头函数
- 我们可以往这个原型上添加属性和方法
- 所有通过构造函数创建的实例,都可以共享原型上的属性和方法
- 公共的属性写在构造函数里面:
function Star(name, age){
// c
this.name = name
this.age = age
}
- 公共的方法写到原型上:
function Star(){}
Star.prototype.eat = function(){
console.log('冰淇淋')
}
let p = new Star()
p.eat() // 冰淇淋
P实例对象是可以访问到构造函数的显示原(Star.prototype )
- 但是,原型上也是可以添加属性的:
function Star() {}
Star.prototype.cheer = "Every Step Counts";
let p = new Star()
console.log(p.cheer) // Every Step Counts
- 所有对象的隐式原型指向构造函数的显示原型
- 对象的隐式原型 ==== 实例对象的 proto
- 构造函数的显示原型 ==== 构造函数的 prototype
function Star(){}
const kk = new Star()
console.log(kk.__proto__ === Star.prototype) // true
// kk.__proto__ 就是 对象的隐式原型
// Star.prototype 就是 构造函数的显示原型
找到原型对象示例:
function Star(name){
this.name = name
}
const ldh = new Star('刘德华')
- 通过
ldh.__proto__==> 可以找到原型 - 通过
Star.prototype==> 可以找到原型
直接给原型赋值对象 导致问题 示例
- 当你直接给原型赋值一个对象时,可能会导致一些问题。让我们看一个示例来说明这个问题:
function Star(name){
this.name = name
}
Star.prototype = {
sing:function(){
console.log('唱歌')
},
dance:function(){
console.log('跳舞')
},
rap:function(){
console.log('rap')
}
kk.sing() // 唱歌
kk.rap() // 跳舞
kk.dance() // rap
console.dir(kk) // 原型中的默认的constructor属性丢失了
}
- 如果我们直接给原型赋值一个对象,相当于整个替换了原型对象的所有内容
- 那么原型中的默认的constructor属性就丢失了,我们就不知道这个原型是哪个构造函数的原型了
解决方法 :
- 我们可以手动利用constructor重新添加上去,指回去
Star.prototype = {
constructor:Star,
sing:function(){
console.log('唱歌')
},
dance:function(){
console.log('跳舞')
},
rap:function(){
console.log('rap')
}
}
- 使用原型继承的方式来避免这个问题
Star.prototype.sing = function(){
console.log('唱歌')
}
Star.prototype.dance = function(){
console.log('跳舞')
}
Star.prototype.rap = function(){
console.log('rap')
}
- 使用原型继承的方式添加方法不会丢失constructor属性
二、constructor属性
-
constructor属性是一个用于标识对象的构造函数的特殊属性。它指向创建该对象的构造函数。 -
在 JavaScript 中,当你创建一个对象实例时,该对象会自动包含一个名为
constructor的属性,它引用了用于创建该对象的构造函数。通过这个属性,你可以确定对象的构造函数是哪个
示例:
- 通过constructor找到创建自己的构造函数
function Person(name) {
this.name = name;
}
var person = new Person("Alice");
console.log(person.constructor); // 输出:[Function: Person]
- 实例对象.prototype(原型).constructor 和创建自己的构造函数相等
function Person(){}
console.log(Person.prototype.constructor === Person) //true
注:
- 继承:当你通过原型链创建一个派生对象时,你可以使用
constructor属性来指定该派生对象的构造函数 - 需要注意的是,如果你修改了对象的原型链,可能会影响到
constructor属性的正确性。在这种情况下,你可能需要手动重设constructor属性,以确保它指向正确的构造函数。例如:
function Person(name) {
this.name = name;
}
Person.prototype = {
greet: function() {
console.log("Hello, my name is " + this.name);
}
};
var person = new Person("Alice");
// 重设 constructor 属性
Person.prototype.constructor = Person;
console.log(person.constructor); // 输出:[Function: Person]
- 在这个示例中,我们修改了
Person.prototype的引用,需要手动重设constructor属性来确保它指向正确的构造函数。
三、隐式原型
1.__ proto __
- 隐式原型(Implicit Prototype)是 JavaScript 对象中的一个内部属性,用于实现对象之间的原型继承。每个 JavaScript 对象都有一个隐式原型,它指向了创建该对象的构造函数的原型对象。
- 所有的对象,都有__proto__(隐式原型),属性值是一个普通的对象
- 所有对象的隐式原型(proto)都指向它的构造函数的显示原型(prototype)
注:
- 隐式原型可以通过
__proto__属性来访问,但请注意,__proto__是非标准的属性,在一些环境中可能不被支持。更推荐的方式是使用Object.getPrototypeOf(obj)方法来获取对象的隐式原型。 - 函数也是一个对象,所以函数也有__proto__属性,但是函数还有prototype属性
示例:
function Car(make, model) {
this.make = make;
this.model = model;
}
Car.prototype.start = function() {
console.log(this.make + " " + this.model + " is starting.");
};
var car = new Car("Toyota", "Camry");
console.log(car.__proto__ === Car.prototype); // 输出:true
- 使用
car.__proto__,我们可以访问到car的隐式原型,它指向了Car.prototype。这是因为car是通过Car构造函数创建的实例,JavaScript 在创建实例时会自动将构造函数的prototype属性赋值给实例的__proto__属性。 - 通过对比
car.__proto__ === Car.prototype,我们可以确认car的隐式原型与Car.prototype是相等的
2.Object.getPrototypeOf(obj)
- Object.getPrototypeOf(obj) 和 ## __ proto __ 作用是一样的 只不过 该属性是标准的
示例:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log("Hello, my name is " + this.name);
};
var person = new Person("Alice");
var prototype = Object.getPrototypeOf(person);
console.log(prototype); // 输出:Person.prototype
console.log(prototype === Person.prototype); // 输出:true
- 使用
Object.getPrototypeOf(person),我们可以获取到person的隐式原型。在这个例子中,它返回的是Person.prototype。 - 我们将获取到的隐式原型赋值给变量
prototype,然后通过对比prototype === Person.prototype,我们可以确认prototype是Person.prototype。 - 注: 使用
Object.getPrototypeOf(obj)方法可以获取对象的隐式原型,这是一种推荐的方式,因为它是标准的方法,在不同的 JavaScript 环境中都能够正常工作,具有更好的兼容性和可移植性。
总结
构造函数-实例-原型之间的关系
- 构造函数.prototypr ===> 原型
- 原型.construcyor ===> 构造函数
- 实例.__ proto__ ===> 原型
- new 构造函数 ===> new出实例