prototype | 青训营笔记

43 阅读3分钟

一直以为自己对原型链的理解已经很牛逼了,但是发现还是有一些不会的地方,记录一下📝

ES5实现继承

原型链实现

实现继承可以利用原型链来实现,把用父级构造函数创建的对象放到子级构造函数的prototype中就可以了

function Person() {
  this.name = "Allen"
  this.friends = []
}

Person.prototype.eating = function() {
  console.log("eating")
}

function Student() {
  this.sno = 123
}

const p = new Person()
Student.prototype = p

Student.prototype.studying = function() {
  console.log("Studying")
}

const stu = new Student()
console.log(stu)

const stu1 = new Student()
const stu2 = new Student()

// 如果获取引用并修改引用的值会改变原型中的值
stu1.friends.push("Tony")
console.log(stu2.friends)

// stu1.friends = ["Rose"]
// console.log(stu2.friends)   //[]

通过这种原型链实现的继承虽然确实是能够继承,但是有很明显的弊端:

  1. 打印stu对象,继承的属性看不到,只能看到sno

image.png

  1. 如果创建两个对象,之间可能会有影响
  2. 子级构造函数不能传参数,比如name和age都不能传,不然就是只在子级构造函数中使用而不是公用的

stealing改进

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

Person.prototype.eating = function() {
  console.log("eating")
}

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

const p = new Person()
Student.prototype = p

Student.prototype.studying = function() {
  console.log("Studying")
}

const stu = new Student("Allen", 18, ["Tony"], 35)
console.log(stu)

const stu1 = new Student("Rose", 15, ["Tim"], 39)
const stu2 = new Student("John", 15, ["Bob"], 28)

stu1.friends.push("Jack")
console.log(stu1.friends)
console.log(stu2.friends)

image.png

像这样在子级构造函数中调用一次父级构造函数,并使用call进行绑定,子级构造函数在被new调用的时候,this会指向其创建的新对象,这样就解决了传参数的问题,并且这样创建的对象在后续获取属性引用的时候并不是去原型中去寻找,而是其自身的属性,所以他们之间相互影响的问题也解决了,并且继承来的属性由于在其自身所以也可以打印出来 当然这样也还是有一些弊端,比如这个Person父级构造函数调用的次数就不太对劲,在传给子级构造函数的原型时就调了一次,而在后续使用子级构造函数时又不停的调用

寄生组合式继承

既然Student.prototype = new Person()这种操作会造成一个没用的对象浪费内存,那如果能让Student.prototype直接等于Person.prototype不就解决问题了,但是这样如果我们后续往Student.prototype上添加属性那就是在Person.prototype上添加了,如果我后续还有Teacher类什么的需要继承自Person类,就会出问题。所以最好的办法就是创建一个对象,让Student.prototype指向它,而它可以指向Person.prototype 直接使用

Student.prototype = Object.create(Person.prototype)

这样就完成了

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

Person.prototype.eating = function() {
  console.log("eating")
}

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

Student.prototype = Object.create(Person.prototype)

Student.prototype.studying = function() {
  console.log("Studying")
}

const stu = new Student("Allen", 18, ["Tony"], 35)
console.log(stu)
stu.studying()
stu.eating()

image.png

方法补充

hasOwnProperty

const obj = {
  name: "Allen",
  age: 18
}


const per = Object.create(obj, {
  address: {
    value: "ShenZhen",
    enumerable: true
  }
})


console.log(per)
console.log(per.hasOwnProperty("name"))
console.log(per.hasOwnProperty("age"))
console.log(per.hasOwnProperty("address"))

image.png 这个方法可以用来判断是否是这个对象自身的属性,原型的不算

in

把上面的hasOwnProperty换成in

console.log("name" in per)
console.log("age" in per)
console.log("address" in per)

image.png这个就和for...in是一样的,可以判断某个属性是否出现在对象或对象的原型上