首先,我们要明确,面向对象不是语法,是一个思想,是一种编程模式
面向对象介绍
-
OAA 面向对象分析
-
OOD 面向对象设计
-
OOP 面向对象编程
-
JavaScript 支持两种编程
- 面向过程 早期
- 面向对象 适合大的项目(多人团队)
OOP面向对象编程的三个基本特征是:封装、继承、多态 1.封装 将相同的属性和方法,提取成为一个类,类是抽象 对象是类的实例(具体) 类是对象的模板(抽象) 2.继承 子类拥有父类的属性和方法 3.多态 1.重写 override 子类重写父类的属性和方法 2.重载 overload 在同一个类中,同名不同参 js是不支持重载,相同的方法名会被覆盖
构造函数
创建及实例化
- 通过
new
关键字来调用
js
// 创建
function Person(name,age){
this.name = name
this.age = age
}
// 实例化
var p1=new Person("杨超越",25)
function Dog(name,age){
this.name = name
this.age = age
}
var d2 = new Dog("旺财",2)
console.log(p1) //Person {name: '杨超越', age: 25}
console.log(d2) //Dog {name: '旺财', age: 2}
注意点
-
首字母大写(约定俗成)
-
通过构造函数实例化对象,必须使用 new
-
构造函数不写return
-
构造函数能当成普通用?
- 返回undefined
-
this指向问题
- 构造函数中的this指向实例化对象
原型
-
原型:prototype 是函数中一个自带的属性, 我们创建的每个函数都有一个prototype(原型)属性, 这个属性是一个对象
-
作用:可以让同一个构造函数创建的所有对象共享属性和方法
- 也就是说, 可以不在构造函数中定义对象的属性和方法, 而是直接将这些信息添加到原型对象中
-
优点 : 可以减少内存的使用
-
原型模式的执行流程:
1.先查找实例对象里的属性或方法,如果有,立刻返回;
2.如果实例对象里没有,则去它的原型对象里找,如果有就返回;
写法
写法1(不推荐)
- 属性和方法都写在原型上(不推荐)
js
function Person() { } //声明一个构造函数
Person.prototype.name = "zhang" //在原型里添加属性
Person.prototype.age = 100;
Person.prototype.show = function () { //在原型里添加方法
return this.name + this.age;
};
var person = new Person();
console.log(person)
// 比较一下原型内的方法地址是否一致
var person1 = new Person();
var person2 = new Person();
console.log(person1.show == person2.show); //true,方法的引用地址一致
写法2(字面量)
- 原型的字面量写法(推荐)
js
function Person(name, age, sex) {
this.name = name
this.age = age
this.sex = sex
}
// 批量添加方法
// 原理就是对象覆盖
Person.prototype = {
constructor: Person, //强制指回构造函数
eat() { },
run() { },
sleep() { }
}
// 单个添加方法
// Person.prototype.eat = function(){}
// Person.prototype.run = function(){}
// Person.prototype.sleep = function(){}
console.log(Person.prototype)
var p1 = new Person()
var p2 = new Person()
console.log(p1==p2) //false 地址不同
console.log(p1.prototype==p2.prototype) // true 原型相同
属性
-
constructor: 是原型的属性, 指向原型对象所属的构造函数;
-
__proto__
: 是对象的属性,指向构造函数的原型 -
isPrototypeOf() : 判断一个对象是否指向了该构造函数的原型对象
-
hasOwnProperty() : 判断实例对象中是否存在该属性
- 实例对象里有(私有)则为true,否则为false
-
in操作符: 判断属性是否存在于该实例对象或者该对象的原型中
- 存在实例中或原型中(公有或私有都是true)
js
function Person() {
// this.name = "杨超越"
}
Person.prototype.name = "虞书欣"
var p = new Person()
console.log(p.name)
// constructor: 是原型的属性, 指向原型对象所属的构造函数
console.log(Person.prototype.constructor)
// __proto__: 是对象的属性,指向构造函数的原型
console.log(p.__proto__)
console.log(p.__proto__ === Person.prototype)
// isPrototypeOf(): 判断一个对象是否指向了该构造函数的原型对象
console.log(Person.prototype.isPrototypeOf(p)); //true, 实例对象都会指向
// hasOwnProperty():判断构造函数内部是否存在该属性
// 实例对象里有(私有)则为true,否则为false
console.log(p.hasOwnProperty("name"))
// in操作符:判断构造函数有没有该属性,内部和原型链都查找
// 存在实例中或原型中(公有或私有都是true)
console.log("name" in p)
// 封装一个方法判断是否为原型链上的属性和方法
function isPrototype(o, attr) {
return attr in o && !o.hasOwnProperty(attr)
}
console.log(isPrototype(p, "name"))
内置对象的原型
js
console.log(Array.prototype.sort); //sort 就是 Array 类型的原型方法
console.log(String.prototype.substring); //substring 就是 String 类型的原型方法
// 使用原型可以给已有构造函数添加方法:
// 例如: 给String类型添加一个方法
String.prototype.addstring = function () {
return this + ',被添加了!'; //this 代表调用的字符串
};
console.log('张三'.addstring()); //使用这个方法 --> 张三,被添加了!
原型+构造函数
-
单独使用原型来给对象添加属性和方法, 是有缺点的, 具体有以下两点 :(仅使用原型的缺点)
- 它省略了构造函数传参初始化这一过程, 带来的缺点就是初始化的值都是一致的
- 原型对象共享的属性或者方法是公用的, 在一个对象(引用类型)修改后,会影响其他对象(引用类型)对该属性或方法的使用
-
构造函数+原型模式
-
使用构造函数添加私有属性和方法,
-
使用原型添加共享的属性和方法
-
优点:
- 实例对象都有自己的独有属性
- 同时共享了原型中的方法,最大限度的节省了内存
- 支持向构造函数传递参数 (初始值)
-