扒下JS的“底裤”之 原型和继承

简介: 扒下JS的“底裤”之 原型和继承

原型和继承

  1. 原型和原型链分别是什么?描述 JavaScript 内置的原型链。


原型

红宝书p224

  • 每个函数都会创建一个 prototype 属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。
  • 原型对象上有一个constructor属性指向对应的构造函数。
  • 注意:函数也是一个对象实例,也有一个__proto__属性指向函数的原型对象(指向内置匿名函数 anonymous)。


原型链:

红宝书p238

  • 如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链。
  • 原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链


描述 JavaScript 内置的原型链

29605b80bb664d50a441eb4d90e5bcd1_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


  1. 描述实例,构造函数,原型的联系。
  • 构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。
  1. 默认原型 所有对象的都继承Object.prototype。
  2. 原型与继承关系 用于检测实例对象的原型链上是否出现过该构造函数
  3. 关于方法 子有用子,无则用父。


1. 罗列各种实现继承的方式,并用代码展示。

原型链继承

function SuperType() {
    this.superName = 'parent'
     this.colors = ["red", "blue", "green"];
}
function SubType(name) {
    this.subName = name
}
SubType.prototype = new SuperType()
console.log(new SubType().superName) // parent
console.log(new SubType('child').subName) // child
let instance1 = new SubType(); 
instance1.colors.push("black"); 
console.log(instance1.colors); // "red,blue,green,black" 
let instance2 = new SubType(); 
console.log(instance2.colors); // "red,blue,green,black"
复制代码


原型链继承问题

  1. 属性值是引用值时,不同的实例会共享该属性。
  2. 子类型在实例化时不能给父类型的构造函数传参。


盗用构造函数

function SuperType(name) {
    this.superName = name
}
function SubType(name) {
    // SuperType.call(this,name)
    SuperType.apply(this,[name])
}
let sub = new SubType();
console.log(new SubType('child').superName) // child
复制代码

目的:为了解决原型包含引用值导致的继承问题 实现手段:使用apply()和 call()方法以新创建的对象为上下文执行构造函数 特点:相比第一种原型链继承方式,父类的引用属性不会被共享,优化了第一种继承方式的弊端,但是只能继承父类的实例属性和方法,不能继承原型属性或者方法


组合继承

function SuperType(name) {
    this.superName = name
}
SuperType.prototype.getName = function() {
    return this.superName
}
function SubType(name) {
    // 第二次调用父构造函数
    // SuperType.call(this,name)
    SuperType.apply(this,[name])
}
// 第一次调用父构造函数
SubType.prototype = new SuperType()
// 添加constructor属性
SubType.prototype.constructor = SubType
let sub = new SubType('child')
console.log(sub.superName) // child
console.log(sub.getName());// child
复制代码
  • 是原型链继承和盗用构造函数继承的组合,但是调用了2次父构造函数


原型式继承

本质就是浅拷贝升级,克隆的新对象可以添加一下新的属性

规范化:使用Object.create()

function object(o) {
    function F() {}
    F.prototype = o
    return  new F()
}
let parent = {
    name:'parent',
    color:['red','green'],
}
let child = object(parent)
let child1 = Object.create(parent,{
    name:{
        enumerable:'true',
        value:'child1'
    }
});
child.color.push('yellow')
child1.color.push('black')
console.log(child.name); // parent
console.log(child1.name); // child1
console.log(child.color); // [ 'red', 'green', 'yellow', 'black' ]
console.log(child1.color); // [ 'red', 'green', 'yellow', 'black' ]
console.log(child1,child1.__proto__);//{ name: 'child1' } { name: 'parent', color: [ 'red', 'green', 'yellow', 'black' ] }
for(let i in child1) {
    console.log(i); // name,color
}
复制代码


寄生式继承

let parent = {
    name:'parent',
    color:['red','green'],
    getName: function() {
        return this.name;
    }
}
function clone(object) {
    let child = Object(parent)
    child.getName = function() {
        return this.color
    }
    return child
}
let child = clone(parent)
console.log(child.getName()); // [ 'red', 'green' ]
复制代码

特点和原型式继承一样,浅拷贝。可以添加为克隆对象方法


寄生式组合继承

核心:就是为了解决调用父构造函数2次的效率问题,还有就是避免子类原型上有不必要的属性。

function Parent(name,color) {
    this.name = name
    this.color = color
}
Parent.prototype.getName = function() {
    return this.name;
}
function Child(name,color) {
    this.name = name;
    this.color = color;
}
function inheritPrototype(child,parent) {
    // 创建副本做原型
    child.prototype = Object.create(parent.prototype)
    // 修改constructor指向
    child.prototype.constructor = child
}
inheritPrototype(Child,Parent)
Child.prototype.getColor = function() {
    return this.color
}
let sub = new Child('child','red')
console.log(sub); // Child { name: 'child', color: 'red' }
console.log(sub.getColor()); // red
console.log(sub.getName()); // child
复制代码

书上是这样的


先原型链继承,但是直接改写子类构造函数的原型,用父类实例赋值。 因为存在共享引用值,构造实例时不能传参,

然后就诞生了盗用父类构造函数。


使用apply,call改变作用域对象。实现了,创建子类实例时可以传参的目的,也不会共享属性是引用值的情况,但是没有办法继承父类原型对象的属性方法。这样方法都得写到子类构造函数中,没有办法复用。


然后又抛出来了一个原型式继承。核心就是浅拷贝,将拷贝的对象进行加工,添加属性,方法。实现手段是Object.create。创建的副本对象的__proto__属性值就是原本的对象。


然后寄生式继承和原型式继承类型都是写了一个构造函数创建副本对象,在副本对象上添加属性,方法。


然而寄生式组合继承就是为了优化组合继承而诞生的。


与组合继承比较核心改变的地方就是子类构造函数的prototype属性的值不是通过父类构造函数new出来的,而是直接把父类构造函数的原型对象创建一个副本直接赋值。并且这个也不用在去修改constructor指向。封装的函数就已经改写的constructor属性值了。也避免了子类的原型因为new带有一些用不上的属性,且这些属性值的undefined。



目录
相关文章
|
11天前
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
3月前
|
JavaScript 前端开发
如何在JavaScript中实现基于原型的继承机制
【8月更文挑战第14天】如何在JavaScript中实现基于原型的继承机制
30 0
|
20天前
|
JavaScript 前端开发
Javascript如何实现继承?
【10月更文挑战第24天】JavaScript 中实现继承的方式有很多种,每种方式都有其优缺点和适用场景。在实际开发中,我们需要根据具体的需求和情况选择合适的继承方式,以实现代码的复用和扩展。
|
14天前
|
JavaScript 前端开发
如何使用原型链继承实现 JavaScript 继承?
【10月更文挑战第22天】使用原型链继承可以实现JavaScript中的继承关系,但需要注意其共享性、查找效率以及参数传递等问题,根据具体的应用场景合理地选择和使用继承方式,以满足代码的复用性和可维护性要求。
|
14天前
|
JavaScript 前端开发 开发者
js实现继承怎么实现
【10月更文挑战第26天】每种方式都有其优缺点和适用场景,开发者可以根据具体的需求和项目情况选择合适的继承方式来实现代码的复用和扩展。
30 1
|
2月前
|
自然语言处理 JavaScript 前端开发
一文梳理JavaScript中常见的七大继承方案
该文章系统地概述了JavaScript中七种常见的继承模式,包括原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承等,并探讨了每种模式的实现方式及其优缺点。
一文梳理JavaScript中常见的七大继承方案
|
2月前
|
JavaScript 前端开发
js之class继承|27
js之class继承|27
|
2月前
|
JSON JavaScript 前端开发
js原型继承|26
js原型继承|26
|
2月前
|
JavaScript 前端开发 开发者
JavaScript 类继承
JavaScript 类继承
19 1
|
2月前
|
JavaScript 前端开发
JavaScript prototype(原型对象)
JavaScript prototype(原型对象)
31 0