未加星标

重新认识JavaScript面向对象:从ES5到ES6

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二04 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏

重新认识JavaScript面向对象:从ES5到ES6
一. 重新认识面向对象 1. javascript是一门面向对象的语言

在说明JavaScript是一个面向对象的语言之前, 我们来探讨一下面向对象的三大基本特征: 封装 , 继承 , 多态 。

封装

把抽象出来的属性和对方法组合在一起,且属性值被保护在内部,只有通过特定的方法进行改变和读取称为封装。

我们以代码举例, 首先我们构造一个Person构造函数, 它有name和id两个属性,并有一个sayHi方法用于打招呼:

//定义Person构造函数 functionPerson(name,id){ this.name=name; this.id=id; } //在Person.prototype中加入方法 Person.prototype.sayHi=function(){ console.log('你好,我是'+this.name); }

现在我们生成一个实例对象p1,并调用sayHi()方法

//实例化对象 letp1=newPerson('阿辉',1234); //调用sayHi方法 p1.sayHi();

在上述的代码中, p1这个对象并不知道sayHi()这个方法是如何实现的,但是仍然可以使用这个方法。 这其实就是 封装 。你也可以实现对象属性的私有和公有,我们在构造函数中声明一个salary作为私有属性, 有且只有通过getSalary()方法查询到薪资。

functionPerson(name,id){ this.name=name; this.id=id; letsalary=20000; this.getSalary=function(pwd){ pwd===123456?console.log(salary):console.log('对不起,你没有权限查看密码'); } } 继承

可以让某个类型的对象获得另一个类型的对象的属性和方法称为继承。

以刚才的Person作为父类构造器,我们来新建一个子类构造器Student,这里我们使用call()方法实现继承

functionStudent(name,id,subject){ //使用call实现父类继承 Person.call(this,name,id); //添加子类的属性 this.subject=subject; } lets1=newStudent('阿辉',1234,'前端开发'); 多态

同一操作作用于不同的对象产生不同的执行结果,这称为多态

JavaScript中函数没有重载, 所以JavaScript中的多态是靠函数覆盖实现的。

同样以刚才的Person构造函数为例,我们为Person构造函数添加一个study方法

functionPerson(name,id){ this.name=name; this.id=id; this.study=function(){ console.log(name+'在学习'); } }

同样, 我们新建一个Student和Teacher构造函数,该构造函数继承Person,并也添加study方法

functionStudent(subject){ this.subject=subject; this.study=function(){ console.log(this.name+'在学习'+this.subject); } } Student.prototype=newPerson('阿辉',1234); Student.prototype.constructor=Student; functionTeacher(subject){ this.subject=subject; this.study=function(){ console.log(this.name+'为了教学而学习'+this.subject); } } Teacher.prototype=newPerson("老夫子",4567); Teacher.prototype.constructor=Teacher;

测试我们新建一个函数doStudy

functiondoStudy(role){ if(roleinstanceofPerson){ role.study(); } }

此时我们分别实例化Student和Teacher,并调用doStudy方法

letstudent=newStudent('前端开发'); letteacher=newTeacher('前端开发'); doStudy(student);//阿辉在学习前端开发 doStudy(teacher);//老夫子为了教学在学习前端开发

对于同一函数doStudy, 由于参数的不同, 导致不同的调用结果,这就实现了多态。

JavaScript的面向对象

从上面的分析可以论证出, JavaScript是一门面向对象的语言, 因为它实现了面向对象的所有特性。 其实, 面向对象仅仅是一个概念或者一个编程思想而已, 它不应该依赖于某个语言存在,比如Java采用面向对象思想构造其语言,它实现了类,继承,派生,多态,接口等机制。 但是这些机制,只是实现面向对象的一种手段, 而非必须。换言之, 一门语言可以根据自身特性选择合适的方式来实现面向对象。 由于大多数程序员首先学习的是Java, C++等高级编程语言, 因而先入为主的接受了“类”这个面向对象实际方式,所以习惯性的用类式面向对象语言中的概念来判断该语言是否是面向对象的语言。这也是很多有其他编程语言经验的人在学习JavaScript对象时,感觉到很困难的地方。

实际上, JavaScript是通过一种叫 原型(prototype) 的方式来实现面向对象编程的。下面我们就来讨论一下 基于类(class-basesd)的面向对象 和 基于原型(protoype-based)的面向对象 这两者的差别。

2. 基于类的面向对象和基于原型的面向对象的比较 基于类的面向对象

在基于 类 的面向对象语言中(比如Java和C++), 是构建在 类(class) 和 实例(instance) 上的。其中 类 定义了所有用于具有某一特征对象的属性。 类 是抽象的事物, 而不是其所描述的全部对象中的任何特定的个体。另一方面, 一个 实例 是一个 类 的实例化,是其中的一个成员。

基于原型的面向对象

在基于 原型 的语言中(如JavaScript)并不存在这种区别: 它只有对象! 不论是构造函数(constructor),实例(instance),原型(prototype)本身都是对象。基于原型的语言具有所谓的原型对象的概念,新对象可以从中获得原始的属性。

所以,在JavaScript中有一个很有意思的__proto__属性(ES6以下是非标准属性)用于访问其原型对象, 你会发现,上面提到的构造函数,实例,原型本身都有__proto__指向原型对象。其最后顺着原型链都会指向Object这个构造函数,然而Object的原型对象的原型是null,不信, 你可以尝试一下Object.prototype.__proto__ === null为true。然而typeof null === 'object'为true。到这里, 我相信你应该就能明白为什么JavaScript这类基于原型的语言中没有类和实例的区别, 而是 万物皆对象!

差异总结

基于类的(Java)基于原型的(JavaScript)类和实例是不同的事物。所有对象均为实例。通过类定义来定义类;通过构造器方法来实例化类。通过构造器函数来定义和创建一组对象。通过 new 操作符创建单个对象。相同通过类定义来定义现存类的子类, 从而构建对象的层级结构指定一个对象作为原型并且与构造函数一起构建对象的层级结构遵循类链接继承属性遵循原型链继承属性类定义指定类的所有实例的所有属性。无法在运行时动态添加属性构造器函数或原型指定初始的属性集。允许动态地向单个的对象或者整个对象集中添加或移除属性。

二. ES5中的面向对象

*这里的ES5并不特指ECMAScript 5, 而是代表ECMAScript 6 之前的ECMAScript!

(一) ES5中对象的创建

在ES5中创建对象有两种方式, 第一种是使用对象字面量的方式, 第二种是使用构造函数的方式。该两种方法在特定的使用场景分别有其优点和缺点, 下面我们来分别介绍这两种创建对象的方式。

1. 使用对象字面量的方式

我们通过对象字面量的方式创建两个student对象,分别是student1和student2。

varstudent1={ name:'阿辉', age:22, subject:'前端开发' }; varstudent2={ name:'阿傻', age:22, subject:'大数据开发' };

上面的代码就是使用对象字面量的方式创建实例对象, 使用对象字面量的方式在创建单一简单对象的时候是非常方便的。但是,它也有其缺点:

在生成多个实例对象时, 我们需要每次重复写name,age,subject属性,写起来特别的麻烦 虽然都是学生的对象, 但是看不出student1和student2之间有什么联系。

为了解决以上两个问题, JavaScript提供了构造函数创建对象的方式。

2. 使用构造函数的方式

构造函数就其实就是一个普通的函数,当对构造函数使用new进行实例化时,会将其内部this的指向绑定实例对象上,下面我们来创建一个Student构造函数(构造函数约定使用大写开头,和普通函数做区分)。

functionStudent(name,age,subject){ this.name=name; this.age=age; this.subject=subject; console.log(this); }

我特意在构造函数中打印出this的指向。上面我们提到,构造函数其实就是一个普通的函数, 那么我们使用普通函数的调用方式尝试调用Student。

Student('阿辉',22,'前端开发');//window{}

采用普通方式调用Student时, this的指向是window。下面使用new来实例化该构造函数, 生成一个实例对象student1。

letstudent1=newStudent('阿辉',22,'前端开发');//Student{name:"阿辉",age:22,subject:"前端开发"}

当我们采用new生成实例化对象student1时, this不再指向window, 而是指向的实例对象本身。这些, 都是new帮我们做的。上面的就是采用构造函数的方式生成实例对象的方式, 并且当我们生成其他实例对象时,由于都是采用Student这个构造函数实例化而来的, 我们能够清楚的知道各实例对象之间的联系。

letstudent1=newStudent('阿辉',22,'前端开发'); letstudent2=newStudent('阿傻',22,'大数据开发'); letstudent3=newStudent('阿呆',22,'python'); letstudent4=newStudent('阿笨',22,'Java'); (二) ES5中对象的继承 1. prototype的原型继承

prototype是JavaScript这类基于原型继承的核心, 只要弄明白了原型和原型链, 就基本上完全理解了JavaScript中对象的继承。下面我将着重的讲解为什么要使用prototype和使用prototype实现继承的方式。

为什么要使用prototype?

我们给之前的Student构造函数新增一个study方法

functionStudent(name,age,subject){ this.name=name; this.age=age; this.subject=subject; this.study=function(){ console.log('我在学习'+this.subject); } }

现在我们来实例化Student构造函数, 生成student1和`student2, 并分别调用其study方法。

letstudent1=newStudent('阿辉',22,'前端开发'); letstudent2=newStudent('阿傻',22,'大数据开发'); student1.study();//我在学习前端开发 student2.study();//我在学习大数据开发

这样生成的实例对象表面上看没有任何问题, 但是其实是有很大的 性能问题 !我们来看下面一段代码:

console.log(student1.study===student2.study);//false

其实对于每一个实例对象studentx,其study方法的函数体是一模一样的,方法的执行结果只根据其实例对象决定,然而生成的每个实例都需要生成一个study方法去占用一份内存。这样是非常不经济的做法。新手可能会认为, 上面的代码中也就多生成了一个study方法, 对于内存的占用可以忽略不计。

那么我们在MDN中看一下在JavaScript中我们使用的String实例对象有多少方法?


重新认识JavaScript面向对象:从ES5到ES6

上面的方法只是String实例对象中的一部分方法(我一个屏幕截取不完!), 这也就是为什么我们的字符串能够使用如此多便利的原生方法的原因。设想一下, 如果这些方法不是挂载在String.prototype上, 而是像上面Student一样写在String构造函数上呢?那么我们项目中的每一个字符串,都会去生成这几十种方法去占用内存,这还没考虑Math,Array,Number,Object等对象!

现在我们应该知道应该将study方法挂载到Student.prototype原型对象上才是正确的写法,所有的studentx实例都能继承该方法。

functionStudent(name,age,subject){ this.name=name; this.age=age; this.subject=subject; } Student.prototype.study=function(){ console.log('我在学习'+this.subject); }

现在我们实例化student1和student2

letstudent1=newStudent('阿辉',22,'前端开发'); letstudent2=newStudent('阿傻',22,'大数据开发'); student1.study();//我在学习前端

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

tags: subject,name,对象,实例,study,函数,面向对象,Student,prototype,JavaScript,构造,原型,age
分页:12
转载请注明
本文标题:重新认识JavaScript面向对象:从ES5到ES6
本站链接:https://www.codesec.net/view/578581.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(282)