还搞不会原型?!绝对不允许!

338 阅读4分钟

原型

概念: 原型(prototype)是函数上的一个属性,它定义了构造函数制造的对象的公共祖先,它是一个对象,包含了可以由该构造函数创建的所有实例共享的属性和方法。

Person.prototype.lastName = 'Park'
Person.prototype.say = function () {
  console.log('hello');
}
function Person(a, b) {
  this.name = 'Peter'
}

let p1 = new Person() //隐式具有 lastName say

console.log(p1.lastName);
p1.say()

输出的lastName 是有值得,say也被定义了 image.png

构造函数new出来的对象会 隐式继承到 构造函数原型上的属性

例子: 用车的数据举例:

如果我们每一位用户购买同一型号的车,那除了一些可自定义的变量,其他都是固定的,像车长和车高,那么我们这里就可以将这些固定的数据设置为它们共同“祖先”。

Car.prototype.name = 'su7'
Car.prototype.lang = 5000
Car.prototype.height = 1400
function Car(color, owner) {
    // this.name = 'su7'//免去给每一个不同的对象付相同的值
    // this.lang = 5000
    // this.height = 1400
    this.color = color
    this.owner = owner
}

运行截图:

image.png

就算没有在Car里给name,long,height赋值,但是由于它的prototype中有定义,所以还是能输出值。

1. 实例对象可以修改显示继承的属性,但无法修改隐式继承的属性(原型上的)

还是车的例子,我们加上这行代码:

this.name = 'su9'//把车名改为su9

我们再运行:

image.png

看到结果真的是su9,但是要注意的是,这个并不是不prototype里的name改成了su9, 而是在Car中强加了一个属性name,为su9,prototype里的name我们是访问不到的。

所以

实例对象可以修改显示继承的属性,但无法修改隐式继承的属性(原型上的)

2.实例对象无法给原型新增属性

要给原型加属性的话只能采用这种办法直接往原型上加:

Car.prototype.name2 = 'su77'

image.png

3.实例对象无法删除原型上的属性

跟增加属性一样,这里我们采用 delete直接在原型上进行删除 两种方法

delete tian.long
console.log(tian.long)

delete Car.prototype.long
console.log(tian.long)//undefined

image.png

可以看到和增加的结果一样,采用delete进行删除的话long还是有值的,害得直接在原型上删。

constructor

constructor 属性是 prototype 对象的一部分

  1. constructor 是一个特殊的属性,它存在于每个对象的原型链上,指向创建该对象的函数

看例子:

function Car() {}
let car = new Car()
console.log(car.constructor);//记录该对象是由谁创建的

image.png

  1. 尽管 constructor 属性非常有用,但它不是只读的,可以被修改。也就是说可以手动设置或更改 constructor 属性

image.png

我们加上代码:

function Bus() { }
Car.prototype = {
  constructor: Bus
}

image.png

  1. 实例对象里的constructor是从原型里继承来的
    function Person() {}
    let p = new Person()
    console.log(Person.prototype); //函数原型 显示原型
    console.log(p.constructor);

image.png

对象原型

这个就是谷歌浏览器上的对象原型: image.png 以前的写法是car.__proto__

对象的隐式原型 === 创建它的构造函数的显示原型

    function Person() {

    }
    let p = new Person()

    console.log(Person.prototype); //显示原型(构造函数的原型)

    console.log(p.__proto__); //p.__proto__  隐式原型(实例对象的原型)
    

我们看下面的运行截图就能理解了,这两个运行出来的结果一模一样:

屏幕截图 2024-07-03 184848.png

js引擎在查找属性时,会先查找对象显示具有的属性,找不到,再查找对象的隐式原型(proto)

简要概述:

  1. 查找对象的自有属性:当访问一个对象的属性时,JavaScript引擎首先会检查这个属性是否存在于对象自身的属性列表中。
  2. 原型链上查找:如果对象自身没有这个属性,JavaScript引擎会沿着原型链向上查找,即检查对象的_proto_属性(在某些JavaScript实现中,这可能表现为Object.getPrototypeOf(obj))所指向的原型对象。
  3. 继续向上查找:如果原型对象也没有这个属性,引擎会继续沿着原型链向上查找,直到找到属性或者到达原型链的末端(通常是null)。
  4. 属性未找到:如果在原型链的末端也没有找到属性,那么属性访问将返回undefined

原型链

js引擎在查找属性时,会顺着对象的隐式原型向上查找,找不到,则查找隐式原型的隐式原型,一直向上,直到找到null为止,这种查找关系,称之为原型链

看例子:

GrandFather.prototype.say = function () {
  console.log('hahaha');
}

function GrandFather() {
  this.age = 60
  this.like = 'drink'
}

Father.prototype = new GrandFather()
function Father() {
  this.age = 45
  this.fortune = {
    card: 'visa'
  }
}
Son.prototype = new Father()//{age:40,fortune:{xxx}}
function Son() {
  this.age = 18
}

let son = new Son()

我们运行输出console.log(son.say()),得到

image.png

也就是说son在father中没有找到say方法,去了grandfather里找,也没有,再去grandfather的原型上找,找到了say(),输出hahaha。如果原型链上都没有,那它会一直这么找,直到找到object的隐式原型null。

所有对象都有原型?

直接说结论,不是的, Object.create(null)没有原型

Object.create(XXX)的作用是创建一个新对象,让新对象隐式继承对象的属性

 let a = {
    name: 'Tom'
  }

  let obj = Object.create(a) //创建一个新对象,让新对象隐式继承a对象的属性

  console.log(obj)

image.png

了解了该方法的使用后我们就能知道,用Object.create(null)创建的对象没有原型,因为它们被明确地指定了原型为null