13-2对象与原型对象
13-2-1对象的分类
在ES6之前,对象可以分为2大类,分别是原生对象和宿主对象。
●原生对象
原生对象又可以分为2类:内置对象和自定义对象。例如前面我们学习过的Date、Math、 正则、数组等,这些就是典型的内置对象。它们是JavaScript这门语 言本身所内置的,我们直接使用即可。
而自定义对象则是我们开发人员自己定义的对象,例如在JS基础中介绍过的使用{}快速生成对象。这样的对象就可以被称之为自定义对象。
。宿主对象
顾名思义,就是依存于某一个特定的环境才会有的对象。一旦离开了特定的环境,则这些对象将不存在。例如前面我们在讲解DOM编程时介绍过
的widnow、navigator 、history 等对象,这些都是依赖于浏览器环境的。一旦脱离了浏览器环境,这些对象也就不存在了。
在ES6中,对象的类别得到了扩充,分为了4个类别,分别是普通对象,外来对象,标准对象和内置对象。
13-2-2原型对象
迷途 2019/7/23 星期二 19:14:34
迷途 2019/07/23 19:14:33
let Computer = function(name,price)f
this. name = name;this.price = price;
112CiE Tobject#t U35Rreturn (
name : xiejie"t
showSth : functíon()
console, log("another object");
console. log(this); 11 FJEDthis1
Computer, protatype. showSth = function()f
console. log(this); 11 JEDWthis1311
let apple = new Computer("S R" , 12000);console. log(apple.name); 11 xiejieapple. showSth();11 another object
11 [ name: 'xiejie', showSth: [Function: showSthJ ]
imit E 7ĩ83513. EXíEJLZE. kẞs pQiã gg üïxT e tiRO1EfẞxtE. this QUsft5 bs f5IJ4B
19:15:01
迷途 2019/7/23 星期二 19:15:01
迷途 2019/07/23 19:15:01
用new运算符调用函数时,该函数总会返回- -个对象,通常情况下,构造函数里面的this就指向返回的这个对象。示例如下:
Let Computer = function(name, price){
this.name = name;this,price F price;}
Computer . prototype. showSth = function(){
console. log(this); //打印出this所指向的对象console. log('这是一台${this . name}电脑);}
let apple = new Computer("苹果",12000);console. log( apple. name);//苹果console. log(apple. price);//12000
apple, showSth();//Computer { name; ' 苹果’,price: 12000 }这是: -台苹果电脑Let asus = new Computer("华硕" , 5000);console. log(asus. name);//华硕console. log (asus. price);//5000
asus. showSth();//Computer { name; ' 华硕’,price: 5000 }这是一 台华硕电脑
这里,我们分别实例化出来了两个对象apple和asus。那么,这两个对象就有各自的属性值。
迷途 2019/7/23 星期二 19:15:11
13-3-2 ES6类的声明
在ES6中,已经开始越来越贴近其他的高级语言了。在ES6中有了类这个概念,使用class来创建类,然后从类里面实例化对象。
但是,需要说明的是,虽然有了class这种关键字,但是这只是一种语法糖,背后对象的创建,还
是使用的是原型的方式。
具体示例如下:
迷途 2019/7/23 星期二 19:15:23
迷途 2019/07/23 19:15:22
13-3类与对象的创建
在前面的第一-小节,我们讲述了什么是面向对象,以及什么是类,还有类和对象之间的关系。紧接着在第二小节我们话锋-转,阐述了JavaScript里面不存在类的概念,而是基于一门基于原型的语言。
既然JavaScript里面不存在类,那么为什么我们还要在第- -小节花大量的篇幅来介绍面向对象里面的类呢?实际上,在JavaScript中 虽然没有类,但是可以通过构造函数来模拟其他编程语言里面的类。从而从构造函数里面实例化对象出来。所以在这- -小节, 我们就来看-下JavaScript中如何书写构造函数来模拟其他语言中的类的。
13-3-1构造函数
JavaScript是- -个]很特殊的语言,在ES6之 前都没有类的概念(注: ES6新增了class关键字),而是通过构造函数来模拟其他编程语言里面的类的。构造函数实际上也是函数,不过这种函数是专门用于生产对象的,所以被称之为构造函数。它的外表和普通函数- -模-样,区别只是在于被调用的方式.上面。
构造函数的函数名有一个不成文的规定,就是首字母要大写,以便和普通函数进行区分。下面的例子就演示了在JavaScript中如何书写一个构造函数:
迷途 2019/7/23 星期二 19:15:33
迷途 2019/07/23 19:15:33
13-3-1构造函数
JavaScript是一门很特殊的语言,在ES6之前都没有类的概念(注: ES6新增了class关键字),而是通过构造函数来模拟其他编程语言里面的类的。构造函数实际上也是函数,不过这种函数是专]用于生产对象的,所以被称之为构造函数。它的外表和普通函数一模-样,区别只是在于被调用的方式上面。
构造函数的函数名有一个不成文的规定,就是首字母要大写,以便和普通函数进行区分。下面的例子就演示了在JavaScript中如何书写一个构造函数:
Let Computer = function(name, price){
this.name = name;this.price = price;
Computer. prototype. showSth = function(){
console. log(这是-台${this. name}电脑);}
这里,我们创建了一-个Computer类,这个类有两个成员属性,分别是name和price,有一-个成员方法,是showSth()。但是可以看到,这里的成员方法showSth()是书写在Computer类的prototype,上面的。
迷途 2019/7/23 星期二 19:15:46
具体示例如下:
class Computer{
//构造器
constructor(name, price){
this. name = name;this.price = price;
}
//原型方法showSth(){
console.log('这是一台${this .name}电脑^ );
}
}
Let apple = new Computer("苹果" , 12000); console. log(apple.name);//苹果console. log( apple. price);//12000apple. showSth();//这是一台苹果电脑
迷途 2019/7/23 星期二 19:15:57
迷途 2019/07/23 19:15:57
一PToto_
{})
prototype
constructor
[Function Object]
. proto_
Dror
[Function J
o
constructor
原型对象) Dron
C
_Proto__
Otyoefor
.proto_
"Clor
构造函数
o
厂( [Function Function]
)
constructor
、对象
COS
Arravll. Nuncel].Booleant. Singll.Functionll. ablectl自定义购值品报导
(对象与原型对象关系图)
通过上面的图我们可以得出以下的结论:
●每一个对象都有一个原型对象。我们可以通过_ proto__ 来访问到某个对象的原型对象●通过_ _proto__ 一直向上寻找原型对象的话,最终会找到null
●一个构造函数的prototype属性指向-一个对象,而这个对象是通过该构造函数实例化出来
的对象的原型对象
●JavaScript中的根对象是0bject.prototype对象。object . prototype对象是一一个空对
象。我们在JavaScript中遇到的每个对象,实际上都是从0bject . prototype对象克隆而来的。0bject. prototype对象就是它们的原型。而object. prototype对象的原型为null。
迷途 2019/7/23 星期二 19:16:08
13-2-2原型对象
在JavaScript里面,没有类这个概念,只有原型的概念。在JavaScript里 面的每一个对象, 都有一个原型对象,而原型对象上面也有一个自己的原型对象,一层一-层向上找,最终到达null。这听起来感觉有点复杂,这里我们将通过- -张图来进行解释,如下图:
迷途 2019/7/23 星期二 19:16:20
迷途 2019/07/23 19:16:19
这里,我们分别实例化出来了两个对象apple和asus。那么,这两个对象就有各自的属性值。
但用new调用构造函数时,还要注意一个问题, 如果构造函数显式地返回了一个object类型象,那么此次运算结果最终会返回这个对象,而不是我们之前所期待的this,这里我们通过的两段代码来搞清楚构造函数是否显式返回object类型对象之间的区别,如下:
正常情况,构造函数没有返回object类型对象,this指向实例化出来对 象
Let Computer = function (name ,price){
this. name = name;this.price = price;
Computer . prototype. showSth = function(){
console. log(this); //打印出this所指向的对象
}
Let apple = new Computer("苹果",12000);console. log(apple.name); //苹果
apple. showSth(); // Computer { name: ' 苹果’,price: 12000 }
构造函数显式的返回一个object类型的对象,那么最终使用的就是手动返回的这个对象。
Let Computer = function (name, price){
this. name = name;this.price = price;
迷途 2019/7/23 星期二 19:16:29
3. constructorl
i@i5 constructor Æ', F*í]ẞJIê#EJ- ↑Xj ŸẞÆft4. tGiEiš, ñtãiZ↑XfXRXf#жR, 5Jk0F:
let Computer = function(name,price)[
this. name = name;this.price = price;]
Computer, prototype. showSth = function()f
console. log(' i&Æ- a$íthis . name]@i );]
let apple = new Computer("#Q" , 12000);
console. log(apple.const ructor);// [Function: Computer]
I
迷途 2019/7/23 星期二 19:16:39
迷途 2019/07/23 19:16:38
关键字来提供不同的访问权限。但是在JavaScript中并没有提供对这些关键字的支持。
不过在JavaScript中要实现封装的方法也非常简单,可以利用函数的作用域来进行模拟。在声明属性的时候,添加关键字(et、const、 var)即可。原因也非常简单,因为构造函数也是函数,既然是函数就会有函数作用域,所以在属性的前面添加了关键字之后使其成为一个局部变量,外部自然而然也就访问不到了。示例如下:
Let Computer = function name, price){
this.name = name ;Let price = price;
Computer. prototype. showSth = function(){
console. log(这是一台${this. name}电脑~ );}
Let apple = new Computer("苹果”,12000);
console. log(apple. name);//苹果
console. log(apple. price);//undefinedconsole. log(apple._ price);//undefined
-般来讲,对于私有属性,有一个不成文的规定,那就是习惯使用_开头来命名私有属性。这里我们可以看到,由于我们对price属性进行了封装,所以外部无法访问到。
迷途 2019/7/23 星期二 19:16:50
迷途 2019/07/23 19:16:49
13-3-3静态方法
所谓静态方法,又被称之为类方法。顾名思义,就是通过类来调用的方法。静态方法的好处在于不需要实例化对象,直接通过类就能够进行方法的调用。
在ES6创建类的方法的时候,可以给方法前面添加一个关键字static,来创建一个静态方法。
class Computer{
//构造器
const ructor(name, price){
this.name = name;this.price = price;
}
//原型方法
showSth(){ T
console. log( .这是一台${this . name}电脑);
//静态方法
static comStruct(){
console. log("电脑由显示器,主机,键鼠组成");
}
Computer. comStruct();
//电脑由显示器,主机,键鼠组成
迷途 2019/7/23 星期二 19:16:58
4. instanceofjklF7
l
#JF- -↑XJXE2€- -↑133315J. s↓# iẞtrue, EJtiEfalse
let Computer = function(name, price)f
this.name = name;this.price = price;
Computer. prototype. showSth = function()(
console. log(' i&- a$[this, name]@Æ" );]
let apple = new Computer("E R", 12000); .
console. log(apple instanceof Computer);//trueconsole. log(apple instanceof Array);//false
19:17:09
迷途 2019/7/23 星期二 19:17:09
迷途 2019/07/23 19:17:08
6. hasOwnProperty(
#Jí-↑4ÆÆXÆX$*# LñiZMM#xæ L õẞmTЖÉJ. $ẞ#4#a, J!i@true, SOR€ÉmmxTжÉ], J!JjEfalse
let Computer = function (name, price)[
this.name = name;this.price = price;
Computer. prototype . showSth = function()f
console. log(' iXk B$[this. name]Rü );]
Computer. prototype.test = "this is a test";let apple = new Computer("E R" , 12000);console. log(apple.test);//this is a test
console. log( apple. has0wnP roperty("test"));//falseconsole. log( apple: has0wnProperty("name"));//true
迷途 2019/7/23 星期二 19:17:31
迷途 2019/07/23 19:17:30
起
x- 1. prototype和_ proto_ ^-2. Object.getPrototypeC. 3. constructor属性. 4. instanceof操作符- 5. isPrototypeOf0. 6. hasOwnProperty03 13-5封装
. 13-5-1 封装基本介绍13-5-2存取器白13-6继承
白13-6-1继承基本介绍
继承的好处. 继承的缺点白13-6-2对象冒充
L方法借用模式.13-6-3原型继承. 13-6-4 ES6继承方式白13-7多态(选修)
-13-7-1 多态简介. 13-7-2实现多态13-7-3多态的意义田13-8 this的指向
-13-9内置对象添加方法13-10属性特性以及描述符13-11基于对象创建对象a 13-12混入技术
-13-12-1浅复制与深复制13-12-2用mixin函数添加总结
13-6-1继承基本介绍
在程序语言中,面向对象里面的继承是指一个子类去继承-个父类。子类继承了父类之后,父类所有的属性和方法都自动都拥有了。
继承的好处
继承最大的好处,就在于代码复用。例如有两个类,这两个类的属性和方法基本相同,那么我们就可以考虑给这两个类写-一个父类
继承的缺点
首先如果继承设计得非常复杂,那么整个程序的设计也会变得庞大和臃肿。甚至经常会出现"大猩猩与香蕉"的问题。"大猩猩与香蕉"这个名字来自于Erlang编程语言的创始人Joe Armstrong的文章:你想要一个香蕉,但是得到的是拿着香蕉和整个丛林的大猩猩。
还有一个问题就是,如果是像C+ +那样的多继承语言,那么还可能会遇到菱形继承的问题。这里我们首先来看一下什么叫做多继承。既然有多继承,自然就有单继承。单继承:所谓单继承,就是指只有一个父类多继承:所谓多继承,就是指有多个父类
菱形继承问题:首先需要说明,菱形继承问题只会在多继承里面才会出现。例如: A和B都继承MindonBase类,假设Base类 里面有一个成员方法, 那么A和B里面都会有这个成员方法,这个时候A和B
两个类又都对这个成员方法进行了修改。接下来让一个C来同时继承A和B,那么 这个时候就会产 Q
迷途 2019/7/23 星期二 19:18:00
迷途 2019/07/23 19:17:59
13-4与原型相关的方法
前面已经介绍了JS里面比较有特色的原型对象,也介绍了在JS里面如何创建构造函数或者类,然后通过构造函数和类来实例化出对象。接下来,我们就来看一下JS中和原型对象相关的一- 些方法。
1. prototype和_ proto_
prototype是构造函数上面的一个属性,指向一个对象,这个对象是构造函数实例化出来的对象的原型对象。实例化出来的对象可以通过proto_ 来找到自 己的原型对象。
Let Computer = function(name, price){
this.name = name ;this.price = price;}
Computer. prototype. showSth = function(){
console. log(这是一台${this . name}电脑);
Let apple = new Computer("苹果",12000);
console. log(Computer . prototype);//Computer { showSth: [Function }console. log(apple._ proto__ );//Computer { showSth; [Function] }console. log(Computer. prototype === apple._ proto__ );// true
迷途 2019/7/23 星期二 19:18:17
迷途 2019/07/23 19:18:17
2. Object.getPrototypeOf)
Bâ7. Lïẞîi_ proto_ жäE!JXJ EXRIX9N, tEFJlXi Object. getPrototypeOfl) жE# - -↑XJ SgR@XJR
let Computer = function (name,price)f
this. name = name;this.price = price;
Computer . prototype. showSth = function()f
console.log(' i&- B$íthis. name]Aü );3
let apple = new Computer("4#", 12000);
console. log(Object. getPrototypeOf (apple));//Computer ( showSth: [Function] 1console. log(apple._ _proto__ );//Computer f showSth: [Function] ]
console. log(0bject . getPrototypeOf(apple) === apple._ proto__ );//true
迷途 2019/7/23 星期二 19:18:28
迷途 2019/07/23 19:18:27
-般来讲,对于私有属性,有一个不成文的规定,那就是习惯使用_开头来命名私有属性。这里我们可以看到,由于我们对price属性进行了封装,所以外部无法访问到。
封装后的属性,我们可以将其称之为私有属性。对于外部来讲,私有属性是不可见的,这个我们已经知道了,但是对于内部来讲,私有属性是可见的。这就好比电视机里面的零部件封装后外部无法直接访问到,但是电视机内部是可以使用这个零部件来完成相应的功能的。示例如下:
Let Computer = function (name,price){
this. name = name;Let_ price = price;
this.sayPrice = function(){
console. log(价格为${_ .price} );
}
}
Computer . prototype. showSth = function(){
console. log(这是一台${this . name}电脑~ );
}
Let apple = new Computer("苹果" , 12000);apple. sayPrice();//价格为12000
迷途 2019/7/23 星期二 19:18:38
迷途 2019/07/23 19:18:37
callf9l]:
let Person = function(name, age)[
this. name = name;this.age = age;
Person. prototype.test = function()[
console. log("this is a test");
let Student = function( name, age , gender, score)f l
11 XPersoni Fi/this EäF11this6EE#E
Person. call(this , name , age);this . gender = gender;this.score. = score;
let xiejie = new Student("i92", 18,"B" ,100);console. log(xiejie.name);// i$%2N3console. log(xiejie.age);//18 Iconsole. log(xiejie.gender);11#console. log(xiejie. score);//100
xiejie. test();//TypeError: xiejie,test is not a function