面向对象编程和面向过程编程
共同点:都是解决问题的一种思路;
区别:面向过程关注的是解决问题需要的一个个的步骤(细节).
面向对象关注是解决问题所需要的对象
什么是对象?
万物皆对象,一般对象是指具体的事物。面向对象和面向过程一样,都是解决问题的思路,无优劣之分,但是面向对象更加方便,代码的结构性更好,更方便维护,代码的复用性更好。
- 现实中的对象:
1.对象的静态的描述信息:
张三(姓名,性别,身高,体重,头发的颜色..)
张三家的狗(姓名,性别,品种...)
- 对象的动态的行为:
张三(吃饭,睡觉,打游戏,谈恋爱,打呼噜..)
张三家的狗(吃饭,睡,咬人,流口水...)
- 现实中的对象和JS中的对象的关系
对象的静态的描述信息--->对象的属性(定义在对象中的变量)
对象的动态的行为------->对象的方法(定义在对象中的函数)
面向对象的三大特性
- 封装:JS中使用对象封装一些变量和函数;
优点:信息隐蔽,代码复用性好,更方便维护。
var name = '战狼2';
var actors = ['吴京',‘张翰’,‘达康书记’,‘弗兰克’]
var type = '动作片'
var showTime = '2017-7-28'
var play = function (){
console.log('play');
}
var film = {
name : '战狼2',
actors :['吴京',‘张翰’,‘达康书记’,‘弗兰克’],
type :'动作片',
showTime : '2017-7-28',
play = function (){
console.log('play');
}
}
- 继承:
生活中的继承:一个人获取另一个人的财富或者资源的一种方法。
程序中的继承:一个类获取另一个类属性或者方法的一种方式。
面向对象的特点:有没有类(Java c++ oc)
Js 没有类,支持面向对象的语言,基于对象的语言。 - 多态:polymorphism = poly(复数) + morph(形态) + ism
多用于强类型语言中,JavaScript具备与生俱来的多态特性。
表现:1. 对同一个操作,不同对象有不同的反应;
2. 隐蔽不同
对象的创建:
- 字面量的方式创建对象(key-value):
场合:只需要创建少量的对象。
问题:1. 创建的对象无法复用;
2. 有大量的重复代码(代码的冗余度太高)。
var stu1 = {
name : '张三',
num : 1001,
className : '如何成为科学家',
study : function () {
console.log('我的名字是' + this.name);
}
};
var stu2 = {
name : '宝宝',
num : 1190,
className : '葵花宝典班',
study : function () {
console.log('我的名字是' + this.name);
}
};
var stu3 = {
name : '瓜皮',
num : '2345',
className : '三分钟精通JS',
study : function () {
console.log('我的名字是' + this.name);
}
};
- 通过内置的构造函数:
问题:内置的构造函数和字面量的方式存在一样的问题
1.创建的对象无法复用
2.创建多个同类型的对象时,有大量重复的代码,代码的冗余度太高
Object | Array | Function | Date
String | Number | Boolean
//1.创建对象
var obj = new Object();// var obj = {};
//2. 利用对象的动态特性设置属性和方法
obj.name = 'zs';
obj.num = 1001;
obj.className = '暴雨梨花针班';
obj.study = function () {
console.log(this.name + this.className);
}
var obj1 = new Object();
obj.name = 'ls';
obj.num = 2001;
obj.className = '葵花宝典班';
obj.study = function () {
console.log(this.name + this.className);
}
var obj2 = new Object();
obj.name = '老王';
obj.num = 3001;
obj.className = '三分钟精通C++';
obj.study = function () {
console.log(this.name + this.className);
}
- 简单的工厂函数:
// 1.提供一个函数(工厂函数)封装对象的创建过程
function createStudent(name,age) {
// 2.原料:创建对象
var stu = new Object();
// 3.加工:设置属性和方法
stu.name = name;
stu.age = age;
stu.logDes = function () {
console.log(this.name);
};
//4. 出厂:返回这个对象
return stu;
}
var stu1 = creatStudent('zs',20);
var stu2 = creatStudent('ls',18);
console.log(stu1, stu2);
-
自定义构造函数:
自定义构造函数的返回值:
1.如果构造函数内部没有return,返回构造函数内部新创建的对象;- 如果是构造函数内部有return,要看具体情况:
- 如果返回的是值类型的数据,直接忽略,返回构造函数内部新建的对象;
- 如果返回是引用类型的数据,返回该引用类型的数据。
- 提供一个构造函数;
1.做一些初始化的设置;
2. 首字母大写 ;使用new 关键字调用构造函数- 通过this设置属性和方法;
- 使用new 创建对象。
// 1.提供一个构造函数
function Person(name,age) {
// 默认会创建一个新的对象
// var obj = new Object();
// 默认会把这个新的对象赋值给this
// this = obj;
// 2.通过this设置属性和方法
this.name = name; // this->构造函数内部新创建的对象;
this.age = age;
// 默认会返回新创建的对象
// return obj;
}
// 3.使用new 创建对象
var p1 = new Person('zs',20);
var p2 = new Person('ls',18);
console.log(p1, p2);
自定义构造函数和工厂函数的区别
- 构造函数首字母大写;
- 使用new 调用构造函数;
- 构造函数默认会返回新创建的对象,工厂函数需要手动返回.
instanceof:判断对象是否是由制定的构造函数创建的。
function Person() {
}
function Dog() {
}
var p = new Person();
var d = new Dog();
console.log(p instanceof Person); // true
console.log(d instanceof Dog); // true
console.log(p instanceof Dog); // false
获取对象的类型(获取对象是由哪一个构造函数创建出来的)-->构造器属性(constructor)
function Person() {
}
var p = new Person();
// 每一个对象都有构造器属性
console.log(p.constructor);//function Person() {}
原型对象:在构造函数创建出来的时候,系统会默认创建一个对象与这个构造函数相关联,这个对象就叫该构造函数的原型对象。
- 作用:通过构造函数创建出来的对象,默认就可以使用该构造函数原型对象的属性和方法。
- 如何访问:构造函数.prototype
- 如何设置:1. 原型对象本质是一个对象,利用对象的动态特性设置原型对象的属性和方法;2. 使用字面量的方式(替换原型);
实例和实例化:
- 实例化:通过构造函数创建对象的过程称为实例化;
- 实例:通过构造函数创建出来的对象就是一个实例.一般情况我们在说实例需要说明是哪一个构造函数的实例. 某个对象是XXX构造函数的实例。
原型的使用方法:
利用对象的动态特性设置原型对象。
- 实例成员:实例属性和实例方法。
- 原型成员:原型的属性和方法。
function Person(name) {
this.name =name
}
//增加成员
Person.prototype.des ='des';
Person.prototype.showName =function () {
console.log(this.name);
}
//修改成员
Person.prototype.des = 'log';
Person.prototype.log = 'des';
var p1 =new Person('zs');
console.log(p1.des);//log;
p1.showName();//zs
//删除成员
delete p1.des;//只能删除实例上的属性而不能删
除原型对象的属性。
console.log(p1.des);//log
delete Person.prototype.des;
console.log(p1.des);//undefined
替换原型对象的注意点:
- 替换原型对象之前和替换原型对象之后创建的对象,他们的原型对象不是同一个对象;
建议: 替换原型对象完毕后 在创建对象。 - 需要修正构造器属性:替换之后创建的对象的 constructor指向Object,并不是Person。
//构造函数
function Person(name) {
this.name = name;
}
var p2 = new Person('ls');
//设置原型对象
Person.prototype = {
constructor :Person,//给替换的原型对象设置constructor属性
des:'des',
showName:function () {
console.log(this.name);
}
}
var p1 = new Person('zs');
console.log(p1.constructor ==p2.constructor);
属性的访问原则
通过对象.属性访问属性的时候,首先会查找自己有没有这个属性,如果有就直接使用;如果没有会查找原型对象上有没有这个属性,如果有就直接使用,如果没有就返回undefined 或者报错;
设置原型属性的注意点:
1.通过对象.属性设置属性的时候,如果没有这个属性就是添加一个新的属性,如果有就是修改;但是不会修改原型对象的属性
2.怎么修改原型对象的属性
1).通过构造函数.prototype.属性修改属性或者替换原型;
2).如果有个属性是引用类型的属性,可以通过对象.属性.属性的方式修改;
function Person(name) {
this.name = name;
}
Person.prototype.des = 'des';
Person.prototype.dog = {
name:'huahua',
age:3
};
var p1 = new Person('zs');
p1.des = '111';
console.log(p1.des);
//有两个操作,先访问p1的dog属性,由于构造函数没有这个属性,所以访问其原型对象的dog属性,再操作dog.name = 'baobao',修改了原型对象的属性。
p1.dog.name = 'baobao';
console.log(Person.prototype.dog.name);
__ proto __属性:对象中指向对象的原型对象。
访问原型对象:
1.构造函数.prototype;
2.对象. __ proto __ 。
- Object.getPrototypeOf();
注意点: - __ proto __在正式开发中不要使用;
- __ proto __ 是非标准的,ECMA不支持.部分浏览器厂商为了方便程序员调试程序提供的。
function Person() {
}
var p1 = new Person();
console.log(Person.prototype == p1.__proto__);//true
hasOwnProperty:用于判断对象中是否存在指定的属性(实例属性);
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.des ='des';
Person.prototype.age = 10;
var p1 = new Person('zs',20)
console.log(p1.hasOwnProperty('des'))//false des为原型对象的属性,不属于p1自己的属性。
构造一个函数:判断属性是不是原型属性,并且只能是原型属性。
function isPrototypeProperty(objName,obj) {
return (objName in obj) && !obj.hasOwnProperty(objName);
}
isPrototypeOf:判断一个对象是否是指定对象的原型对象。
function Person() {
}
var obj = {};
Person.prototype = obj;
var p1 =new Person();
console.log(obj.isPrototypeOf(p1));//true
console.log(p1 instanceof Person);//true