JS面向对象编程,原型与继承全面解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: JS面向对象编程,原型与继承

面向对象编程的特点

  1. 封装:使用对象的人无需考虑内部实现,只考虑功能的使用。
  2. 继承:为了代码的可复用
  3. 多态:不同对象 作用于同一操作产生不同结果。

JS如何创建对象

普通方式

const A = new Object()
A.attribute = '' // 定义属性
A.fn = function() { } // 定义方法

工厂模式

function Creat(attr) {
    // .... 同上面普通方式 
    return A
}
const aa = Creat('...')
const bb = Creat('...')

存在问题:往实例化对象上层找不到父类,只能知道是一个Object

构造函数/实例

function Player(name) {
    this.name = name
    this.run = function() {
        console.log('...');
    }
}

const x = new Player('a')
const y = new Player('b')
console.log(x.run === y.run) // false 独立内存

缺点:通过this添加的属性和方法,总是指向当前对象(改变当前对象不会影响构造函数),实例化时通过this添加的属性和方法都会在内存当中复制一份。

原型对象

function Player(name) {
    this.name = name
}
Player.prototype.run = function() {
    console.log('...');
}

const x = new Player('a')
const y = new Player('b')
console.log(x.run === y.run) // true

静态属性

function Player(name) {
    Player.count = 1
}

const p = new Player('a')
console.log(p.count) // undefined
console.log(Player.count) // 1

原型及原型链

查找原型对象的方法

xxx.__proto__
Object.getPrototypeOf(xxx)

new关键字做了什么

  1. 创建了新对象并将.__proto__指向构造函数的.prototype
  2. 将this指向新创建的对象
  3. 返回新对象(1. 有显式的返回值且是对象则返回这个对象 2. 其他情况返回this)
function newSimulator() {
    // 1. 创建新对象
    const obj = new Object()
    // 2. 设置__proto__为构造函数prototype
    const constructor = [].shift.call(arguments) // 取出参数第一项,并删除掉,剩余参数在下一步会用到
    obj.__proto__ = constructor.prototype
    // 3. this指向新对象,也就是改变this的指向:例如apply,call
    const ret = constructor.apply(obj, arguments)
    // 4. 返回新对象或this
    return typeof ret === 'object' ? ret : obj
}
function Player(name, type) {
    this.name = name
    this.say = () => { console.log(`i am ${name},i create by ${type}`) }
}

const a = new Player('a', 'new')
a.say() // i am a,i create by new
const b = newSimulator(Player, 'b', 'newSimulator')
b.say() // i am b,i create by newSimulator

理解原型链

当读取实例对象的属性时,如果找不到,会查找对象原型中的属性,直到最上层为止。

Object.prototype.name = 'Object';
function Player() {}
Player.prototype.name = 'Player';

const p1 = new Player();
p1.name = 'p1';
console.log(p1.name); // p1

delete p1.name;
console.log(p1.name); // Player

delete Player.prototype.name;
console.log(p1.name); // Object

JS实现继承

原型链继承

function Parent() {
    this.name = 'ParentName';
    this.actions = ['sing', 'jump', 'rap'];
}

function Child() {}

Child.prototype = new Parent(); // 通过实例化对象来拿到全部属性方法
Child.prototype.constructor = Child; // 但是直接赋值会覆盖掉Child,这一步是其将修改回来

存在问题:引用类型被改变,所有实例共享,无法传参

const c1 = new Child();
c1.actions.push('basketball');
console.log(c1.actions); //[ 'sing', 'jump', 'rap', 'basketball' ]
const c2 = new Child();
console.log(c2.actions); // [ 'sing', 'jump', 'rap', 'basketball' ]

构造函数继承

function Parent(name) {
    this.name = name;
    this.actions = ['sing', 'jump', 'rap'];
    this.say = function () {}
}

function Child() {
    Parent.apply(this, arguments); // 把Parent执行了一遍,解决了传参问题
}
const c1 = new Child('c1');
const c2 = new Child('c2');
c1.actions.pop() // 引用类型的问题被解决
console.log(c1.name, c1.actions) // c1 [ 'sing', 'jump' ]
console.log(c2.name, c2.actions) // c2 [ 'sing', 'jump', 'rap' ]

console.log(c1.say === c2.say); // false 独立内存,构造函数的问题,消耗大

组合继承

该继承同时解决以上两种继承存在的问题,副作用是会重复执行构造函数

// 即原型链继承 + 构造函数继承

function Parent(name, actions) {
    this.name = name;
    this.actions = actions;
}

Parent.prototype.say = function () {
    console.log(this.name + ' say');
}

function Child() {
    Parent.apply(this, arguments); // 第一次调用构造函数
}

Child.prototype = new Parent(); // 第二次调用构造函数
Child.prototype.constructor = Child;
// 组合继承中到这一步,是使用到开头原型链继承的方式,可以看做它将Child中的原型链方法变成了引用类型
const c1 = new Child('c1', ['eat']);
const c2 = new Child('c2', ['run']);
c1.actions.pop() // 引用类型的问题被解决
console.log(c1.name, c1.actions) // c1 []
console.log(c2.name, c2.actions) // c2 [ 'run' ]

console.log(c1.say === c2.say); // true 内存消耗问题被解决

寄生组合式继承

// 还是刚才的组合继承,改变的地方会被注释
function Parent(name, actions) {
    this.name = name;
    this.actions = actions;
}

Parent.prototype.say = function () {
    console.log(this.say + ' say');
}

function Child() {
    Parent.apply(this, arguments);
}

// Child.prototype = new Parent();
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

解析其中 Object.create 等于以下写法(es5):

let TempFunction = function () {}; // 一个空的临时变量
TempFunction.prototype = Parent.prototype; // 将临时函数的原型指向Parent的原型
Child.prototype = new TempFunction(); // 这样就同样实现了原型链继承的优点,并且开销极低

这也就是“寄生”的含义:用一个纯粹的中间函数去执行了new。

es6继承

class Parent {
    constructor() {
        this.name = 'aaa';
    }
    say() {
        console.log(this.name, 'say');
    }
}

class Child extends Parent {
    constructor() {
        super();
    }
}

JS:面向未来编程😋

相关文章
|
2月前
|
JavaScript 前端开发 Go
CSS 与 JS 对 DOM 解析和渲染的影响
【10月更文挑战第16天】CSS 和 JS 会在一定程度上影响 DOM 解析和渲染,了解它们之间的相互作用以及采取适当的优化措施是非常重要的。通过合理的布局和加载策略,可以提高网页的性能和用户体验,确保页面能够快速、流畅地呈现给用户。在实际开发中,要根据具体情况进行权衡和调整,以达到最佳的效果。
|
1月前
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
2月前
|
存储 前端开发 JavaScript
JavaScript垃圾回收机制深度解析
【10月更文挑战第21】JavaScript垃圾回收机制深度解析
122 59
|
1月前
|
JavaScript 前端开发
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
26 1
JavaScript中的原型 保姆级文章一文搞懂
|
1月前
|
JavaScript 前端开发
Javascript如何实现继承?
【10月更文挑战第24天】JavaScript 中实现继承的方式有很多种,每种方式都有其优缺点和适用场景。在实际开发中,我们需要根据具体的需求和情况选择合适的继承方式,以实现代码的复用和扩展。
|
1月前
|
JavaScript 前端开发
如何使用原型链继承实现 JavaScript 继承?
【10月更文挑战第22天】使用原型链继承可以实现JavaScript中的继承关系,但需要注意其共享性、查找效率以及参数传递等问题,根据具体的应用场景合理地选择和使用继承方式,以满足代码的复用性和可维护性要求。
|
1月前
|
JavaScript 前端开发 开发者
js实现继承怎么实现
【10月更文挑战第26天】每种方式都有其优缺点和适用场景,开发者可以根据具体的需求和项目情况选择合适的继承方式来实现代码的复用和扩展。
31 1
|
1月前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
57 0
|
1月前
|
前端开发 JavaScript
JavaScript新纪元:ES6+特性深度解析与实战应用
【10月更文挑战第29天】本文深入解析ES6+的核心特性,包括箭头函数、模板字符串、解构赋值、Promise、模块化和类等,结合实战应用,展示如何利用这些新特性编写更加高效和优雅的代码。
48 0
|
2月前
|
JavaScript 前端开发 开发者
原型链深入解析:JavaScript中的核心机制
【10月更文挑战第13天】原型链深入解析:JavaScript中的核心机制
41 0

推荐镜像

更多