js面向对象理解

article/2025/9/18 12:06:52

ECMAScript 有两种开发模式:1.函数式(过程化),2.面向对象(OOP)。面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是,ECMAScript 没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。

js(如果没有作特殊说明,本文中的js仅包含ES5以内的内容)本身是没有class类型的,但是每个函数都有一个prototype属性。prototype指向一个对象,当函数作为构造函数时,prototype则起到类似class的作用。

一.创建对象

创建一个对象,然后给这个对象新建属性和方法。

var box = new Object(); //创建一个Object 对象box.name = 'Lee'; //创建一个name 属性并赋值box.age = 100; //创建一个age 属性并赋值box.run = function () { //创建一个run()方法并返回值return this.name + this.age + '运行中...';};
alert(box.run()); //输出属性和方法的值

上面创建了一个对象,并且创建属性和方法,在run()方法里的this,就是代表box 对象本身。这种是JavaScript 创建对象最基本的方法,但有个缺点,想创建多个类似的对象,就会产生大量的代码。
为了解决多个类似对象声明的问题,我们可以使用一种叫做工厂模式的方法,这种方法就是为了解决实例化对象产生大量重复的问题。

function createObject(name, age) { //集中实例化的函数var obj = new Object();obj.name = name;obj.age = age;obj.run = function () {return this.name + this.age + '运行中...';};return obj;
}
var box1 = createObject('Lee', 100); //第一个实例
var box2 = createObject('Jack', 200); //第二个实例
alert(box1.run());
alert(box2.run()); //保持独立

工厂模式解决了重复实例化的问题,但是它有许多问题,创建不同对象其中属性和方法都会重复建立,消耗内存;还有函数识别问题等等。

二.构造函数的方法

构造函数的方法有一些规范:
1)函数名和实例化构造名相同且大写,(PS:非强制,但这么写有助于区分构造函数和
普通函数);
2)通过构造函数创建对象,必须使用new 运算符。

function Box(name, age) { //构造函数模式this.name = name;this.age = age;this.run = function () {return this.name + this.age + '运行中...';};
}
var box1 = new Box('Lee', 100); //new Box()即可
var box2 = new Box('Jack', 200);
alert(box1.run());
alert(box1 instanceof Box); //很清晰的识别他从属于Box

构造函数可以创建对象执行的过程:
1)当使用了构造函数,并且new 构造函数(),那么就后台执行了new Object();
2)将构造函数的作用域给新对象,(即new Object()创建出的对象),而函数体内的this 就
代表new Object()出来的对象。
3)执行构造函数内的代码;
4)返回新对象(后台直接返回)。

注:

1)构造函数和普通函数的唯一区别,就是他们调用的方式不同。只不过,构造函数也是函数,必须用new 运算符来调用,否则就是普通函数。

2)this就是代表当前作用域对象的引用。如果在全局范围this 就代表window 对象,如果在构造函数体内,就代表当前的构造函数所声明的对象。

这种方法解决了函数识别问题,但消耗内存问题没有解决。同时又带来了一个新的问题,全局中的this 在对象调用的时候是Box 本身,而当作普通函数调用的时候,this 又代表window。即this作用域的问题。

三.原型

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。逻辑上可以这么理解:prototype 通过调用构造函数而创建的那个对象的原型对象。使用原型的好处可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。

function Box() {} //声明一个构造函数
Box.prototype.name = 'Lee'; //在原型里添加属性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型里添加方法
return this.name + this.age + '运行中...';
};

构造函数的声明方式和原型模式的声明方式存储情况如下:

在这里插入图片描述

所以,它解决了消耗内存问题。当然它也可以解决this作用域等问题。

我们经常把属性(一些在实例化对象时属性值改变的),定义在构造函数内;把公用的方法添加在原型上面,也就是混合方式构造对象(构造方法+原型方式):

var person = function(name){this.name = name};person.prototype.getName = function(){return this.name; }var zjh = new person(‘zhangjiahao’);zjh.getName(); //zhangjiahao

下面详细介绍原型:

1.原型对象

每个javascript对象都有一个原型对象,这个对象在不同的解释器下的实现不同。比如在firefox下,每个对象都有一个隐藏的__proto__属性,这个属性就是“原型对象”的引用。

2.原型链

由于原型对象本身也是对象,根据上边的定义,它也有自己的原型,而它自己的原型对象又可以有自己的原型,这样就组成了一条链,这个就是原型链,JavaScritp引擎在访问对象的属性时,如果在对象本身中没有找到,则会去原型链中查找,如果找到,直接返回值,如果整个链都遍历且没有找到属性,则返回undefined.原型链一般实现为一个链表,这样就可以按照一定的顺序来查找。

1)__proto__和prototype
JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype。以上面的例子为例:

console.log(zjh.proto === person.prototype) //true
同样,person.prototype对象也有__proto__属性,它指向创建它的函数对象(Object)的prototype

console.log(person.prototype.proto === Object.prototype) //true
继续,Object.prototype对象也有__proto__属性,但它比较特殊,为null

console.log(Object.prototype.proto) //null
我们把这个有__proto__串起来的直到Object.prototype.__proto__为null的链叫做原型链。如下图:
在这里插入图片描述

2)constructor
原型对象prototype中都有个预定义的constructor属性,用来引用它的函数对象。这是一种循环引用


person.prototype.constructor === person //true
Function.prototype.constructor === Function //true
Object.prototype.constructor === Object //true

3)为加深对理解,我们再举一个例子:

function Task(id){  this.id = id;  
}  Task.prototype.status = "STOPPED";  
Task.prototype.execute = function(args){  return "execute task_"+this.id+"["+this.status+"]:"+args;  
}  var task1 = new Task(1);  
var task2 = new Task(2);  task1.status = "ACTIVE";  
task2.status = "STARTING";  print(task1.execute("task1"));  
print(task2.execute("task2"));

结果:
execute task_1[ACTIVE]:task1
execute task_2[STARTING]:task2
构造器会自动为task1,task2两个对象设置原型对象Task.prototype,这个对象被Task(在此最为构造器)的prototype属性引用,参看下图中的箭头指向。

在这里插入图片描述
由于Task本身仍旧是函数,因此其”proto”属性为Function.prototype, 而内建的函数原型对象的”proto”属性则为Object.prototype对象。最后Obejct.prototype的”proto”值为null。

总结:

实例对象的__proto__指向,其构造函数的原型;构造函数原型的constructor指向对应的构造函数。构造函数的prototype获得构造函数的原型。

有时某种原因constructor指向有问题,可以通过

constructor:构造函数名;//constructor : Task
重新指向。

四.继承

继承是面向对象中一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而ECMAScript 只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。

在JavaScript 里,被继承的函数称为超类型(父类,基类也行,其他语言叫法),继承的函数称为子类型(子类,派生类)

1.call+遍历

属性使用对象冒充(call)(实质上是改变了this指针的指向)继承基类,方法用遍历基类原型。

function A()
{this.abc=12;
}A.prototype.show=function ()
{alert(this.abc);
};//继承A
function B()
{//继承属性;this->new B()A.call(this);   //有参数可以传参数A.call(this,name,age)
}//继承方法;B.prototype=A.prototype;
for(var i in A.prototype)
{B.prototype[i]=A.prototype[i];
}
//添加自己的方法
B.prototype.fn=function ()
{alert('abc');
};var objB=new B();
var objA=new A();objB.show();

可以实现多继承。

2.寄生组合继承

主要是Desk.prototype = new Box(); Desk 继承了Box,通过原型,形成链条。主要通过临时中转函数和寄生函数实现。

临时中转函数:基于已有的对象创建新对象,同时还不必因此创建自定义类型
寄生函数:目的是为了封装创建对象的过程

//临时中转函数
function obj(o) { //o表示将要传递进入的一个对象
function F() {}   //F构造是一个临时新建的对象,用来存储传递过来的对象
F.prototype = o;  //将o对象实例赋值给F构造的原型对象
return new F();   //最后返回这个得到传递过来对象的对象实例
}
//寄生函数
function create(box, desk) {var f = obj(box.prototype);f.constructor = desk;  //调整原型构造指针desk.prototype = f;
}
function Box(name) {this.name = name;this.arr = ['apple','pear','orange'];
}
Box.prototype.run = function () {return this.name;
};
function Desk(name, age) {Box.call(this, name);this.age = age;
}
//通过寄生组合继承实现继承
create(Box, Desk); //这句话用来替代Desk.prototype = new Box();
var desk = new Desk('Lee',100);
desk.arr.push('peach');
alert(desk.arr);
alert(desk.run());

临时中转函数和寄生函数主要做的工作流程:

临时中转函数:返回的是基类的实例对象函数
寄生函数:将返回的基类的实例对象函数的constructor指向派生类,派生类的prototype指向基类的实例对象函数(是一个函数原型),从而实现继承。



转载:https://www.cnblogs.com/jingwhale/p/4678656.html


http://chatgpt.dhexx.cn/article/ED4jblmJ.shtml

相关文章

Python面向对象理解

一,初始面向对象. 面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。 优点是:极大的降低了写程序的复杂度…

面向对象理解——多态

何为多态 定义: 多态是指不同的子类在继承父类后分别都重写覆盖了父类的方法,即父类同一个方法,在继承的子类中表现出不同的形式。系统在运行时(而非编译时),能够根据其类型确定调用哪个重载的成员函数的能…

面向对象的理解及相关概念(封装,继承,多态)

前言 面向对象程序设计(OOP)是当今主流的程序设计范性,它取代了结构化或过程式编程技术。 一、面向对象是什么? 1、定义: 面向对象就是:把数据及对数据的操作方法放在一起,作为一个相互依存…

面向对象的理解

一、什么是面向对象? 对 Java 语言来说,一切皆是对象。把现实世界中的对象抽象地体现在编程世界中,一个对象代表了某个具体的操作。一个个对象最终组成了完整的程序设计,这些对象可以是独立存在的,也可以是从别的对象继…

面向对象理解

1.早期发展 面向对象(Object Oriented,OO)是软件开发方法。面向对象的概念和应用已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。面向对象是一种对现实世界理解和抽象的方法…

error: unknown file type '.pyx' (from 'pycocotools/_mask.pyx')

在 YOLO v3模型中运用 cocoapi 进行评估模型,安装时报错显示: building pycocotools._mask extension error: unknown file type .pyx (from pycocotools/_mask.pyx) make: *** [all] Error 1可能的解决方法:   运行的 Python 版本不对。 …

sklearn/tree/_criterion.pyx

注解: criterions分为几类,其中有classification criterions与regression criterions。classification criterions是针对离散的,regression criterions是针对连续分布的 本文主要讲解一下RegressionCriterion。 RegressionCriterion用于回归树(Regression Tree 回归树)中…

python中导入.pyx文件的问题解决

已经几次遇到这种问题了, 里面的region指的是一个.pyx文件。 ImportError: cannot import name region from toolkit.utils (D:\******\RPN\pysot-master\toolkit\utils\__init__.py) 解决方案: 创建一个setup.py文件,一般如果是搬运的话里面会有&…

将pyx文件编译成pyd文件(很多坑,已解决)

项目场景: Faster R-CNN项目,将pyx文件编译成pyd文件(很多坑,请注意) 项目环境 python 3.6 – conda的虚拟环境 TensorFlow 1.15.0 win 10 问题描述 使用网上的教程进行编译 有一个需要被编译的bbox.pyx文件,创建…

python文件中import pyx文件问题

1、首先,pyx文件需要安装Cython,这里先进入自己的虚拟环境,用conda安装Cython conda install Cython 2、需要用到pyx的地方是mattnet中的mask.py 文件,里面 import _mask.pyx 文件,需要先在mask的目录下建立setup文…

成功解决windows下将.pyx文件编译成.pyd文件

在linux上正常跑通的算法,搬到windows下使用就报错了 原来是其中涉及到.pyx文件的编译 在linux下.pyx会被编译成.so的文件,但是在windows下是无法使用.so文件的 需要重新编译成windows下的.pyd格式的才可以直接将python模块成功导入到算法中 所以需要解…

.pyx文件编译出错

文章目录 .pyx文件的编译导入 参考博客(写的超级好,完美解决了问题) 错误 ImportError: Building module dtw failed: ["distutils.errors.CompileError: command D:\\\\Program Files (x86)\\\\Microsoft Visual Studio\\\\2019\\\\Pr…

python导入pyx文件失败

报错如下: 出现无法导入的情况时,在import之前加上一下两条语句即可解决问题: import pyximport pyximport.install()

pyx 编译成 pyd

1、编写好py文件,然后将后缀改为pyx 2、编写好setup.py文件 3、在setup.py 文件同目录下打开命令行执行: python setup.py build_ext --inplace之后就会生成pyd文件 和C文件 示例: 需要编译成pyd的文件 fast_fib.pyx import functoolsdef …

如何在pycharm上编译使用pyx文件

前言 cython是专门用来写在Python里面import用的扩展库。实际上Cython的语法基本上跟Python一致,而Cython有专门的“编译器”先将 Cython代码转变成C,然后使用C编译器编译出最终的Python可调用的模块。 本人因在github中找到了一份用cython写的自定义库…

虚拟机的安装与ubuntu的创建,,Ubuntu快捷键

文章目录 vmware安装包与Ubuntu镜像文件:VMware的安装Ubuntu的创建Ubuntu快捷键 在学习与工作中,我们往往可能需要用到windows以外的操作系统,比如Linux系统就适合开发者的使用,往往用到虚拟机,这篇博客来讲讲VMware的…

Ubuntu操作-09 快捷键

文章目录 1 系统快捷键2 终端快捷键3 Gedit编辑器快捷键 Ubuntu快捷键介绍,已在Ubuntu21.10测试 1 系统快捷键 命令说明Ctrl Alt Fn1进入图形化用户登录界面Ctrl Alt Fn2进入当前图形化界面Ctrl Alt Fn3-Fn6进入命令行虚拟终端Ctrl Alt Fn7-Fn12进入另外的…

不可不知的ubuntu系统快捷键

软硬件环境 ubuntu 19.10 64bit Desktop 前言 ubuntu是当前最流行的linux发行版之一,使用ubuntu除了经常使用的Ctrlc (复制) 和Ctrlv (粘帖),其实还有一些不常用但却很有用的快捷键,熟练掌握常用快捷键绝对可以大大提升你的工作学习效率。文中…

ubuntu系统的快捷键,软件等配置

最近要完全使用ubuntu桌面系统了,很多在win 上的快捷键和软件都无法使用了,这里打算总结一下。 文章目录 1 常用快捷键2 常用配置调整左侧状态栏的顺序截图火狐禁止通知键盘快捷键设置DNS修改默认打开程序vs code 终端间距太大 3 常用软件安装meshlabana…

ubuntu的快捷键

1.一起按AltTab,在应用间顺向一览窗口,AltTabShift是逆向预览窗口。先打开终端,后打开文本编辑器,是下面这个样子。 一起按AltTab之后就会变成下面这样子 2.AltF2是打开命令窗口,可输入命令启动应用程序。一起按AltF2之后变成下面这样子,还可…