javascript在ES6之前是没有办法定义一个类的,使用构造函数模拟类的概念 ,class
ES6已经有类 class
什么是面向对象?
面向对象就是模仿人类的行为去做一些事情
使用js得到一个对象有几种方式
第一种得到对象的方式
var p=new object();
第二种得到对象的方式
json 简单一种javascript对象
使用工厂模式的方案来解决代码的冗余的问题
//第一种方式
var person = new Object();
person.age = 18;
person.name = "小红";
person.say = function() {
//必须加this,指向person对象所定义的属性
alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");
}
person.say();
使用这种定义的方式,虽然可以定义一个对象,但是因为没有类的约束,所以无法实现
对象的重复使用,如存在 10 个人,就要定义 10 个 person,太过于麻烦了,在操作过程中存
在问题
使用 json 得到
var person = {
name : "小雷",
age : 18,
say : function() {
alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");
封装--Javascript 的 原型(prototype )
function Person() {
}
//使用原型来给对象赋值
//这样就讲一个对象的属性和方法放在了该对象的原型中
//外界是无法访问到这样数据的
Person.prototype.name = "刘帅哥";
Person.prototype.age = 18;
Person.prototype.say = function() {
alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");
}
var p1 = new Person();
p1.say();//正常访问了
say();//报错了
原型是 js 中非常特殊一个对象,当一个函数创建之后,会随之就产生一个原型对象,当
通过这个函数的构造函数创建了一个具体的对象之后,在这个具体的对象中就会有一个属性
指向原型。这就是原型的概念
常见的 原型检测方式
可以通过如下的方式检测p1是不是指向Person的原型对象
alert(Person.prototype.isPrototypeOf(p1))
//检测p1的构造器是否指向Person对象
alert(p1.constructor == Person)
//检测某个属性是不是自己内存中的
alert(p1.hasOwnProperty("name"));
alert(p2.hasOwnProperty("name"))
同样我们可以使用 delete 语句来删除我们赋予对象的自己属性(注意:原型中的是无法
删除的),如
//可以使用delete语句删除对象中自己的属性,那么就会找到原型中的值
delete p2.name;
p2.say();
alert(p2.hasOwnProperty("name"));
检测在某个对象自己或者对应的原型中是否存在某个属性。
alert("name" in p1);//true
delete p2.name;//虽然删除了自己的name属性,但是原型中有
alert("name" in p2);//true
//原型和自己中都没有sex属性
alert("sex" in p1);//false
那么问题来了?如果检测只在原型中,不在自己中的属性呢?(提问)
//我们可以自己写代码来测试属性不在自己,在原型中
function hasPrototypeProperty(obj,prop) {
if (!obj.hasOwnProperty(prop)) {
if (prop in obj) {
return true;
}
}
return false;
}
alert(hasPrototypeProperty(p1,"name"));
alert(hasPrototypeProperty(p2,"name"));
原型 重写
在上面的写法中,我们已经解决了大量的问题,使用原型。但是如果我们的对象中存在
大量的属性或者方法的时候,使用上面的方式,感觉要写大量的【对象.prototype.属性名 】
这样的代码,感觉不是很好,那么我们可以使用 json 的方式来写:
function Person() {
}
Person.prototype = {
name : "刘帅哥",
age : 18,
say : function() {
alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");
}
}
var p1 = new Person();
p1.say()
var p2 = new Person();
p2.name = "张三";
p2.age = 20;
p2.say();
但是这种写法,我们是将该对象的原型覆盖(注意:这两种写法不一样的,第一种是扩
充,第二种是覆盖),就会出现如下的问题:
function Person() {
}
Person.prototype = {
constructor:Person,//手动指向Person
name : "刘帅哥",
age : 18,
say : function() {
alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");
}
}
var p1 = new Person();
p1.say()
var p2 = new Person();
p2.name = "张三";
p2.age = 20;
p2.say();
//此时p1的构造器不在指向Person,而是指向了Object
//因为我们覆盖了Person的原型,所以如果constructor比较重要的话,
//我们可以手动指向
alert(p1.constructor == Person)
此时就没有问题了。但是原型重写会给我们带来一些非常有趣的现象。下面我们来研究
研究。
function Person() {
}
var p1 = new Person();
Person.prototype.sayHello = function() {
alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");
}
// p1.sayHello();
Person.prototype = {
constructor:Person,//手动指向Person
name : "刘帅哥",
age : 18,
say : function() {
alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");
}
}
var p2 = new Person();
p2.name = "张三";
p2.age = 20;
p1.sayHello();//此时找不到name和age,但是代码正确
p2.say();//正确
p1.say();//错误,因为原型重写了
p2.sayHello();//错误
继承-- 原型创建对象
在面向对象的语言中,存在了三大特性—封装、继承、多态。我们前面一直说 javascript
是面向对象的语言,那么它应该也有面向对象语言这些特性,上面我们看来封装,那么下面
我们来研究继承。
继承,望名而知意,就是我们现实社会中的子孙后代继承了父辈的财富,我们一直在说,
面向对象的语言就是在模拟现实世界,通过模拟现实世界来编程,那么在 javascript 中,如
何理解继承,如何实现继承呢?
实现继承
//定义一个父类
function Parent() {
this.pv = "parent";
}
Parent.prototype.showParent = function() {
alert(this.pv);
}
//定义一个子类
function Son() {
this.sv = "Son";
}
//使用原型链来实现继承
Son.prototype = new Parent();
Son.prototype.showSon = function() {
alert(this.sv);
}
var s1 = new Son();
s1.showParent();
s1.showSon();
但是使用原型链实现继承要注意以下一些问题:
1、 不要在设定了原型链之后,再原型重写
2、 一定要在原型链赋值之后才能添加或者覆盖方法
注意:javascript 中存在重写,但是没有重载
原型 链继承的缺陷
原型链继承存在的缺陷就是:
1、 无法从子类中调用父类的构造函数,这样就没有办法把子类中属性赋值给父类。
2、 父类中属性是在子类的原型中的,这违背了我们前面所讲的封装的理念(属性在对
象中,方法在原型中),会出现前面值的混淆问题。
所以我们一般都不会使用单纯的原型链来实现继承。
基于 伪装实现继承
在前面我们学习了 call 和 apply 方法,这两个方法我们知道可以使用:函数名.call(上下
文,参数列表),或:者函数名.apply(上下文,参数数组)的方式来调用函数,这样我们可以通过
第一个参数上下文来改变调用函数的对象,那么基于这两个方法,我们可以实现一个基于伪
装的继承。
//定义一个父类
function Parent() {
this.pv = "parent";
}
//定义一个子类
function Son() {
this.sv = "Son";
Parent.call(this);//注意:此时的this指的是Son的对象
//那么就是Son对象调用Parent函数
}
var s1 = new Son();
alert(s1.pv);
在子类中的 this 指的就是子类实例化后的对象本身,当我们在子类中使用 call 方法调用
父类后,就相当于将父类的构造方法绑定到了子类的对象身上,这样就伪装了子类可以使用
父类的构造方法,完成了继承。
伪装 的缺陷
基于伪装的继承解决了基于原型链的问题,但不是说它就十分完美,它也存在了问题,
如下:
由于使用伪造的方式继承,子类的原型不会指向父类,所以父类中写在原型中的方法不
会被子类继承,所以子类调用不到父类的方法。
解决的办法就是将父类的方法放到子类中来,但是这样的又违背了封装的理念。
ECMAScript6— 面向 对象
ECMAScript6 是下一代 Javascript 标准,这个标准将在 2015 年 6 月得到批准。ES6 是
Javascript 的一个重大的更新,并且是自 2009 年发布 ES5 以来的第一次更新。 它将会在主
要的 Javascript 引擎实现以下新的特性
class 关键字
ES6 在面向对象使用了新特性和语法来实现,启用之前的保留字 class 来申明类,因此
在 ES6 之后,JavaScript 就有了类的定义和实现:
虽然 ES6 的 Class 本质上还是语法糖,但这么设计有它的目的,首先这样的书写形式是
的 JavaScript 的面向对象的写法就和 Java(一门后端面向对象的语言)看起来很是类似了。其
次了简化了写法,还让我们之前将代码书写在构造函数外面的那种方法不存在了