js最重要也是最核心的东西就是对象了,入行这么长时间,一直对面向对象一知半解。网上有很多介绍对象对象的内容,这里也做了很多借鉴,

尤其是阮一峰老师的文章。我这里写的大多例子都是阮一峰老师文章的例子,但是加上了我自己的见解。JavaScript面向对象编程

js最核心的东西就是对象,万物皆对象。对象分为普通对象函数对象。分区是看是否有function关键字。

为什么会出现面向对象编程?

场景:如果我们把属性和方法封装成一个对象,或者从原型对象上生成一个实例对象,我们如何做?

技术大佬们为了让技术界更核心,开始了他们的研究:

第一:原始模式(最古老的模式)

这里我们创建一个对象Cat,把它当做其他对象的原型。其他对象都按照这个模式来创建。

然后我们在下方创建了cat1和cat2。

但是问题是我们看不到cat1和cat2之间的关系,最重要的是如果有n的cat,我们岂不是要写到天荒地老?

 let Cat = {
name:'',
color:''
}
// 然后根据这个规格来实现两个实例对象。
  let cat1 = {};
cat1.name = '大毛';
cat1.color = '黄色';
  let cat2 = {};
cat2.name = '二毛';
cat2.color = '黑色';

所以,进化到了封装成一个函数

第二:原始模式改进

我们把Cat封装为一个函数,我们每次只需要调用该函数就行。

问题是,我们依然看不出cat1和cat2之间的关系?不能一眼看出他们之间的关联。

function Cat(name,color){
return {
name:name,
color:color
}
}
let cat1 = Cat('大毛','黄色');
let cat2 = Cat('二毛','黑色');
console.log(cat1);//{ name: '大毛', color: '黄色' }
console.log(cat2);//{ name: '二毛', color: '黑色' }

第三:构造函数(重点!!!)

为了解决从原型对象上生成实例的问题,js开发人员给我们提供了一个构造函数模式。

那么什么是构造函数呢?

先了解几个概念:

构造函数(constructor):其实就是一个普通的函数,只是内部使用了this关键字。(这里我们记住构造函数的英文--Constructor)

实例对象:通过new操作符生成的对象就是实例对象。并且this变量会绑定在实例对象上。(记住:生成的是实例对象!!)

constructor:构造函数生成的实例对象,会有一个constructor属性,指向该构造函数。(我们可以理解为,实例对象是从构造函数上衍生出来的,当然要有它的烙印了!)

// 我们先创建一个构造函数Cat
function Cat(name,color){
this.name = name;
this.color = color;
}
// 用new生成Cat的实例。cat1和cat2
let cat1 = new Cat('大毛','黄色');
let cat2 = new Cat('二毛','黑色');
// 看一下输出结果
console.log(cat1);//Cat { name: '大毛', color: '黄色' }
console.log(cat2);//Cat { name: '二毛', color: '黑色' }

我们看一下通过构造函数Cat创建出来的实例对象cat1和cat2身上的constructor属性:

//  我使用的是构造函数生成的实例(Constructor),所以实例上得有我的烙印。属性名称就是Constructor
// 指向的就是他们的构造函数。需要让别人知道,是谁创建了你。
console.log(cat1.constructor === Cat);//true
console.log(cat2.constructor === Cat);//true

看到这里,我们已经明白了什么是构造函数,什么是原型对象,什么是实例对象,以及实例对象的constructor属性。

奖励一下自己!

看到这里,说明我们已经成功了一半了!

我们知道了构造函数和实例对象的关系,那我们如何验证呢?

js提供了一个instanceof运算符。instance(例子的意思)。从字面量就能看出某某是某某的实例。

看一下下面的代码就会很清晰了。

console.log(cat1 instanceof Cat);//true

我们了解了instanceof,那大家脑海里还知不知道js提供的其他两种验证的方法呢?建议大家看一下js秘密花园,里面讲解的很多东西很清晰。

除了instanceof,我们还了解到typeof,以及Object.prototype.toString方法。

typeof其实没什么作用,不能验证类型(验证类型不够彻底),只能验证一个对象或者一个变量是否存在。是一个垃圾产品。

instanceof的作用就是我们上面提到的验证构造函数之间的关系。

Object.prototype.toString则是我们经常使用的方法,来验证类型。先看一下下面的例子:

console.log(Object.prototype.toString.call([]));//[object Array]
console.log(Object.prototype.toString.call({}));//[object Object]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(function f(){}));//[object Function]
// 然后我们可以根据slice方法来截取字符串
// slice(8,-1)表示从第八位开始,截取到倒数第一位(负数从后往前截取。)。
console.log(Object.prototype.toString.call([]).slice(8,-1));//Array

通过熟悉,我们又知道了js存在的三种验证类型。

继续接着我们构造函数开始:

构造函数的方法很好用,但是存在一个浪费内存的问题,这句话怎么理解呢?先看个例子。

我们给构造函数Cat内部多增加一个type属性,以及eat方法(运行在对象上的函数成为方法)。

然后我们根据Cat生成两个实例对象,cat1和cat2。根据输出结果我们发现,都会存在type属性和eat方法。

如果说我生成的实例上面不需要这个属性或这个eat方法,这就存在了浪费内存的问题了。

 function Cat(name,color){
this.name = name;
this.color = color;
this.type = '猫科动物';
this.eat = function(){
console.log('吃老鼠');
};
} let cat1 = new Cat('大毛','黄色');
let cat2 = new Cat('二毛','黑色');
console.log(cat1);//Cat { name: '大毛', color: '黄色', type: '猫科动物', eat: [Function] }
console.log(cat2);//Cat { name: '二毛', color: '黑色', type: '猫科动物', eat: [Function] }
// 为什么是false?因为cat1和cat2是两个不同的实例对象,而对象的比较是内存的比较,不是值的比较。
console.log(cat1.eat === cat2.eat);//false

那我们如何解决呢?

第五:prototype

js规定,每个构造函数都有一个prototype属性,指向另外一个对象。(我们这里可以把它当做一个虚无的对象A)。A对象上面的所有属性和方法,都会被构造函数

的实例继承。所以这就意味着,我们可以把这些不变的属性和方法放到这个对象A上面。

我们继续使用构造函数Cat的例子:

 function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.type = '猫科动物';
Cat.prototype.eat = function(){
console.log('吃老鼠');
}
// 继续生成实例
let cat1 = new Cat('大毛','黄色');
let cat2 = new Cat('二毛','黑色');
console.log(cat1.type); //猫科动物
// 实例对象cat1和实例对象cat2上面的eat方法,都是继承过来的,都指向我们所说的对象A上面。
// 所以它们的指针是一样的。
console.log(cat1.eat === cat2.eat);//true
console.log(cat1.type === cat2.type);//true

到了这里,我们知道了每个构造函数上面都会有一个prototype属性。

就以我们创建的构造函数Cat来说,就相当于 let A = Cat.prototype;

这个A就是一个对象,我们Cat.prototype.type = "猫科动物",其实就是给该对象新增属性罢了。

isPrototype 

那我们如何判断呢?还记得我们上面说过的instanceof,这里有一个新的方法,isPrototype,来判断某个prototype对象(A)和某个实例(cat)之间的关系。

console.log(Cat.prototype.isPrototypeOf(cat1));

hasOwnProperty() 

经常使用for...in循环的应该知道该属性,用来过滤继承的属性。

function Cat(name, color) {
this.name = name;
this.color = color;
}
// 我们在其原型上增加两个属性
Cat.prototype.type = "猫科动物";
Cat.prototype.address = "杭州";
let cat1 = new Cat("大毛", "黑色");
cat1.lover = '二毛';
for (let i in cat1) {
console.log(cat1[i]);
// 输出结果是如下,也就是说把继承的属性和自身的属性全部遍历出来了(这就是我们很少使用for in 循环的原因。)
// 大毛
// 黑色
// 二毛
// 猫科动物
// 杭州
}
// 然后我们使用hasOwnPrototype()来过滤
for (let j in cat1) {
if (cat1.hasOwnProperty(j)) {
console.log(cat1[j])
// 大毛
// 黑色
// 二毛
}
}

学到这里,让我们回想一下以下几个概念:

1、什么是构造函数?

2、什么是实例对象?它是如何创造出来的?

3、实例对象的constructor指向什么?

4、构造函数的prototype属性是什么?

答案:

构造函数是一个普通函数,函数内通过this关键字来声明变量。

实例对象同构new+构造函数生成的。构造函数的this指向生成的实例对象。

实例对象的constructor属性指向构造函数。

构造函数的prototype属性指向的是一个对象,该对象上面的所有属性和方法,都会被构造函数创建的实例对象所继承。

下面开始我们的最重要部分,了解后面的知识,我们差不多了解了原型,了解了构造函数,了解了面向对象

先看下面的例子:

给构造函数Cat的原型对象上新增三个属性。

function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.address = '杭州';
Cat.prototype.lover = '小翠';
Cat.prototype.type = '猫科动物';

看到这里,会有小伙伴问,为什么不写在一起?那我们改变一下:

function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype = {
address:'杭州',
lover:'小翠',
type:'猫科动物'
}
// 等价于
// Cat.prototype.address = '杭州';
// Cat.prototype.lover = '小翠';
// Cat.prototype.type = '猫科动物';

看到这里是不是会明白点什么?Cat.prototype就是我们创建的原型对象,也就是我们上面提到的A。

其实它还有一个默认的属性,constructor属性。

这种情况下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性指向prototype所造的函数。

翻译过来就是,A有一个默认的属性,constructor。这个属性是一个指针,指向Cat。

Cat.prototype.constructor = Cat;

看到constructor是不是感觉很熟悉?

我们之前也提到过,每个构造函数生成的实例上面都有一个constructor属性,指向该构造函数。

对比一下下面的代码,看看有什么发现?

cat1.constructor = Cat;
Cat.prototype.constructor = Cat;

cat1有constructor我们知道,因为它是Cat的实例对象。

那么Cat.prototype是怎么回事呢?

其实Cat.prototype也是一个实例对象。我们看一下下面的代码

function Cat(){

};
console.log(Cat.prototype);//Cat {}
let cat1 = new Cat();
console.log(cat1);//Cat {}

发现什么没?Cat.prototype其实就是Cat的一个实例对象!!!所以它也有constructor属性!!!!!

_proto_

js创建对象的时候,会有一个_proto_的内置属性,用于指向创建它的构造函数的原型对象。

function Cat(){};
let cat1 = new Cat();
// 实例对象上面的__proto__指向的是构造函数的原型
console.log(cat1.__proto__ == Cat.prototype);

总结一下吧:

构造函数Cat

实例对象cat1

1、cat1通过new Cat()创建。

2、cat1.constructor = Cat;

3、Cat.prototype.constructor = Cat;

4、cat1.__proto__ = Cat.prototype;

再次声明:

这里我主要借鉴了阮一峰老师的JavaScript面向对象编程,加上我我自己的简介。对理解面向对象感觉熟悉很多。希望能帮助大家。

javaScript构造函数、原型、面向对象编程的更多相关文章

  1. JavaScript中的面向对象编程,详解原型对象及prototype,constructor,proto,内含面向对象编程详细案例(烟花案例)

    面向对象编程:   面向:以什么为主,基于什么模式 对象:由键值对组成,可以用来描述事物,存储数据的一种数据格式 编程:使用代码解决需求   面向过程编程:         按照我们分析好的步骤,按步 ...

  2. Javascript 构造函数原型继承机制

    我们先聊聊Js的历史,1994年Netscape公司发布了Navigator浏览器0.9班.这是历史上第一个比较成熟的网络浏览器.轰动一时.但是,这个版本的浏览器只能用来浏览,不具备交互功能,最主要的 ...

  3. javaScript设计模式之面向对象编程(object-oriented programming,OOP)(二)

    接上一篇 面向对象编程的理解? 答:面向对象编程,就是将你的需求抽象成一个对象,然后针对这个对象分析其特征(属性)与动作(方法).这个对象我们称之为类.面向对象编程思想其中一个特点就是封装,就是把你需 ...

  4. 深入理解javascript中实现面向对象编程方法

    介绍Javascript中面向对象编程思想之前,需要对以下几个概念有了解: 1. 浅拷贝和深拷贝:程序在运行过程中使用的变量有在栈上的变量和在堆上的变量,在对象或者变量的赋值操作过程中,大多数情况先是 ...

  5. javaScript设计模式之面向对象编程(object-oriented programming,OOP)(一)

    面试的时候,总会被问到,你对javascript面向对象的理解? 面向对象编程(object-oriented programming,OOP)是一种程序设计范型.它讲对象作为程序的设计基本单元,讲程 ...

  6. JavaScript构造函数+原型创建对象,原型链+借用构造函数模式继承父类练习

    虽然经常说是做前端开发的,但常常使用的技术反而是JQuery比较多一点.在JavaScript的使用上相对而言少些.尤其是在创建对象使用原型链继承上面,在项目开发中很少用到.所以今天做个demo练习一 ...

  7. javascript总结22: javascript的对象--面向对象编程

    1 对象:JavaScript 中的所有事物都是对象:字符串.数值.数组.函数. 对象与程序的关系: 程序 = 基于对象操作的算法 + 以对象为最小单位的数据结构 此外: 面向对象的本质就是让对象有多 ...

  8. javaScript设计模式之面向对象编程(object-oriented programming,OOP)--寄生组合式继承

    组合式继承:将类式继承同构造函数继承组合使用,但是存在一个问题,子类不是父类的实例,而子类的原型式父类的实例,所以才有了寄生组合式继承. 意思就是说,寄生就是寄生式继承,寄生式继承就是依托于原型继承, ...

  9. JavaScript 中的面向对象编程

    使用JSON 来定义一个对象: <script type="text/javascript">var xiaoming = { name : 'xiaoming', a ...

  10. 探讨javascript面向对象编程

    (个人blog迁移文章.) 前言: 下面将探讨javascript面向对象编程的知识. 请不要刻意把javascript想成面向对象编程是理所当然的. javascript里面,对象思想不可少,但是不 ...

随机推荐

  1. RedHat6.4安装图形行化界面

    1.1    打开电源进入RedHat shell命令行界面 1.2    查看系统镜像包括的所有软件包组信息 [root@zhongyi-test ~]# yum grouplist Loaded ...

  2. 【vlan-端口配置】

    搭建好拓扑图如下: 分别配置两台终端的ip地址 创建vlan把e0/4/0接口加入到新的vlan中 连通性失败 . 同理在把e0/4/1加入到vlan视图中 连通性成功 : 搭建好拓扑图如下 进入e0 ...

  3. 微信小程序插件内页面跳转和参数传递

    在此以插件开发中文章列表跳传文章详情为例. 1.首先在插件中的文章列表页面wxml中绑定跳转事件. bindtap='url' data-id="{{item.article_id}}&qu ...

  4. 图解HTTP总结(2)——简单的HTTP协议

    HTTP协议是一种不保存状态,即无状态(stateless)协议.HTTP协议自身不对请求和响应之间的通信状态进行保存.也就是说在HTTP这个级别,协议对于发送过的请求或响应都不做持久化处理. 使用H ...

  5. 文件/etc/passwd,/etc/shadow,/etc/group

    文件/etc/passwd /etc/shadow /etc/group 计算资源的使用(并不是所有的人都可以用这台计算机的) 权限:访问资源的的能力. 用户:获取资源或者权限的凭证. 用户的容器:关 ...

  6. python-9-IO编程

    1-文件读写 f = open('d:/file.txt','r') #如果文件不存在会报异常 print(f.read()) #一次性读取所有内容 f.close() 1.2 由于文件操作会用异常, ...

  7. 代码review的流程

    以前我们一直都是如果要进行代码review的时候,要不我们就直接用idea来进行查看,根据不同的来查看 但是我们都是看代码的不同来进行来实现的,其实我们不需要这样,我们可以使用工具Phabricato ...

  8. CentOS 更换yum源为aliyun yum源

    CentOS自带的yum源为官方的源,由于墙的原因,经常无法访问,国内也有不错的源,这里讲一下使用aliyun的源替换原yam源. 第一种方法 1.安装base reop源 cd /etc/yum.r ...

  9. Pascal 基础教程

    Pascal现在还有人想学习吗?先给出一本不错的Pascal教程,Object Pascal的教程我日后给出. Pascal基础教程       第一课 初识PASCAL语言           …… ...

  10. Python全栈工程师(多继承、函数重写)

    ParisGabriel                每天坚持手写  一天一篇  决定坚持几年 为了梦想为了信仰    开局一张图 Python人工智能从入门到精通 补充: 对象 --------- ...