面向对象认识
面向对象(OOP,即 Object Oriented),是相对于面向过程来讲的,简单说就是把数据和方法组合封装起来成为一个整体,提高软件的重用性、灵活性和扩展性。
面向对象特点:
-
继承,即子类自动分享父类数据结构和方法
-
封装,捆绑数据和方法,但是对于某些私有的数据和方法,不可被外界访问,就封装起来区分访问权限
-
多态,即对于同一函数,因对象不同,结果不同
let ww = { name : "魏无羡", age : 18, sex : "男", say : function () { console.log(`我的名字是${ww.name},已经${ww.age}岁了,我是${ww.sex}生`); } } ww.say()
有需要要写多个类似的对象时,可用下面这种方法来写
function Person(name,age,sex){ let obj = {} obj.name = name obj.age = age obj.sex = sex obj.say = function (){ console.log(`我的名字是${obj.name},已经${obj.age}岁了,我是${obj.sex}生`); } return obj } let ww = Person("魏无羡",18,"男") // 这种叫做实例,是对象Person的实例 ww.say() let ll = Person("蓝忘机",20,"男") ll.say()
new 关键词
用的比较多的:
new Date()
new Object()
new Array()
new RegExp()
new XMLHTTPRequest()
new Promise()
new Image()
函数执行前加new的影响
- 加new后,函数内部会自动创建一个全新的对象,所以函数内部的this指向就会转变,指向这个对象。
- 不加new时,函数默认返回undefined;加new后,函数默认返回自动创建的对象。
一、prototype
当创建的对象较多时,会有可共用的属性,此时就可用prototype。
比如我们用Array来创建一个实例时,不用我们定义,它本身就具有Pop、push、join等方法,且这些方法存在于构造函数的_proto_中。这样我们就可以理解为前辈将可能使用到的方法都写到函数的proto中,当我们创建实例,想要去使用某一方法时,其实就是使用了共有属性。例: (此处的let a = [] 是 let a = new Array() 的简写)
function Students(name, id) {
// 每个实例私有属性
this.name = name
this.id = id
}
// 共有属性
Students.prototype.campus = "云深不知处"
Students.prototype.say = function (){
console.log(`我的名字是${this.name},是${this.campus}的学生,学号是${this.id}`);
}
let ww = new Students("魏无羡",1213)
ww.say()
let ll = new Students("蓝忘机",1314)
ll.say()
//我的名字是魏无羡,是云深不知处的学生,学号是1213
//我的名字是蓝忘机,是云深不知处的学生,学号是1314
二、原型链
如上面那个代码,函数Students的prototype指向了一个对象,这个对象就是在调用构造函数时创建的实例的原型,也就是ww和ll的原型。
function fn(){
this.x = 10
}
fn.prototype.x = 20
let ss = new fn()
console.log(ss.x); // 10 , 其优先访问私有属性
console.log(ss.__proto__); // {x:20, constructor: f}
console.log(fn.prototype); // {x:20, constructor: f}
console.log(ss.__proto__ === fn.prototype); // true
console.log(fn.prototype); // 打印如下,可以看出所有原型链的顶端都是object
三、继承(ES5写法)
注意:默认构造函数的首字母大写
function Person(name, age){
this.name = name
this.age = age
}
Person.prototype.say = function (){
console.log(this.name + ":" + this.age);
}
// 子可继承父的东西,父不能使用子的东西,且子可新增
function Students(name, age, id){
// 私有属性继承
Person.call(this,name,age)
// 私有属性新增
this.id = id
}
let aa = new Person("ajs",15)
console.log(aa.name); // ajs
console.log(aa.age); // 15
console.log(aa.id); // undefined
aa.say() // ajs:15
let ss = new Students("魏无羡",18, "0012")
console.log(ss.name); // 魏无羡
console.log(ss.age); // 18
console.log(ss.id); // 0012
// 原型的继承。还可用方法二、三
Students.prototype = Person.prototype
ss.say() // 魏无羡:18
// 原型的继承
// 方法一:两方为引用关系,一方改变,另一方也会变。Students新增属性时会改变原型Person
Students.prototype = Person.prototype
// 方法二: 创建实例的方法,也为引用关系,但当Students改变时,原型Person不会改变。且Students下的属性有参数,但其prototype中的属性无参数
Students.prototype = new Person()
// 方法三:
function Fn(){}
Fn.prototype = Person.prototype
Students.prototype = new Fn() // 此时Students的原型为Fn(),
//修复 每个函数默认 Person.prototype.constructor = Person
Students.prototype.constructor = Students
// 原型的新增
Students.prototype.campus = "内容"
四、ES6构造函数
class Person{
// 私有属性
constructor(name, age){
this.name = name
this.age = age
}
// 公有属性,只能用函数形式
say (){
console.log(this.name + ":" + this.age);
}
}
// 继承
class Students extends Person{
constructor(name, age, id){
super(name, age) // 执行后,才能继承Person的私有属性
this.id = id
}
campus(){ // Students的共有属性
return "云深"
}
}
let ww = new Students("魏无羡",18,"0012")
console.log(ww.name); // 魏无羡
console.log(ww.age); // 18
console.log(ww.id); // 0012
ww.say() // 魏无羡:18
console.log(ww.campus()); // 云深
let ll = new Person("蓝忘机",20,"1314")
console.log(ll.name); // 蓝忘机
console.log(ll.age); // 20
console.log(ll.id); // 1314
ll.say() // 蓝忘机:20
// console.log(ll.campus()); // 报错