面向对象编程 —— Object Oriented Programming,简称 OOP ,是一种编程开发思想
创建对象的方式
工厂模式
创建对象交给一个工厂方法来实现,可以传递参数,但主要缺点是无法识别对象类型和来源,因为创建对象都是使用Object的原生构造函数来完成的
function createPerson(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.getName = function() {
return this.name;
}
return o; //使用 return 返回生成的对象实例
}
var person1 = createPerson('Jack', 19, 'SoftWare Engineer')
var person2 = createPerson('Tom', 25, 'Mechanical Engineer')
构造函数模式
解决了获取对象类型和来源的问题,但是重复创建方法,浪费内存
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.getName = function() {
return this.name;
}
}
var person1 = new Person('Jack', 19, 'SoftWare Engineer')
var person2 = new Person('Tom', 23, 'Mechanical Engineer')
①通过constructor获取对象的来源
console.log(person1.constructor)
②通过instanceof判断对象的来源
console.log(person1 instanceof Person)
console.log(person1 instanceof Object)
* new方法实例对象(补充)
要创建Person的实例,必须使用new关键字,以Person函数为构造函数,传递参数完成对象创建
实际创建经过以下四个过程:
a) 创建一个空对象(底层实现)
b) 将函数的作用域赋给新对象(即this指向新对象)
c) 执行构造函数的代码
d) 返回该对象(底层实现)
function Person(name, age, job) { // 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象 // let instance = {} // 然后让内部的 this 指向 instance 对象 // this = instance // 接下来所有针对 this 的操作实际上操作的就是 instance this.name = name; this.age = age; this.job = job; this.getName = function() { return this.name; } // 在函数的结尾处会将 this 返回,也就是 instance // return this }
原型模式
能够节省内存,但是无法为构造函数传递初始化参数,最主要是当对象的属性是引用数据类型时,它的值(指针)是不变的,总是指向同一个外部对象,所有实例对该对象的操作都会影响其它实例
原型对象:JS每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个空对象,它是所有通过new操作符使用函数创建的实例的原型对象
①所有在原型中创建的属性和方法都直接被所有实例共享
function Person() {}
Person.prototype.name = 'Jack' //使用原型来添加属性
Person.prototype.age = 19
Person.prototype.job = 'Mechanical Engineer'
Person.prototype.getName = function() {
return this.name
}
var person1 = new Person()
console.log(person1)
var person2 = new Person()
console.log(person2)
②实例中属性会覆盖原型对象中的属性:原型模式创建的对象实例,其属性是共享原型对象的;但也可以自己实例中重新进行定义,在查找时,就不从原型对象获取,而是根据搜索原则,得到本实例的返回
function Person() {}
Person.prototype.name = 'Jack' //使用原型来添加属性
Person.prototype.age = 19
Person.prototype.job = 'Mechanical Engineer'
Person.prototype.getName = function() {
return this.name
}
var person1 = new Person()
person1.name = '黄小米'
console.log(person1.getName())
var person2 = new Person()
console.log(person2.getName())
③每当调用构造函数创建一个新实例后,该实例的内部包含一个指针,指向构造函数的原型属性。(一般名为_ _proto_ _,chrome浏览器现更改为[[prototype]])
④函数及函数原型之间形成循环指向:只要创建一个新函数,就会为该函数创建一个prototype属性。在默认情况下,所有prototype属性都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针
组合构造函数及原型模式
目前最为常用的定义类型方式,构造函数模式用于定义实例的属性(支持向构造函数传递参数),原型模式用于定义方法和共享的属性。因此每个实例都会有自己的一份实例属性的副本,但同时又共享着对方方法的引用,最大限度的节约内存
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.lessons = ['Math', 'Physics']
}
Person.prototype.getName = function() {
return this.name;
}
var person1 = new Person('Jack', 19, 'SoftWare Engneer')
person1.lessons.push('Biology')
console.log(person1.lessons)
var person2 = new Person('Tom', 39, 'Mechanical Engneer')
console.log(person2.lessons)
console.log(person1.getName === person2.getName) //共享原型中定义方法
定义原型方法的方法:
// 方法一:constructor指向存在问题
Person.prototype = {
constructor: Person, //原型方式会将对象的constructor变为Object,此处强制指回Person
getName: function() {
return this.name;
}
}
// 方法二:constructor指向没有问题,逐项赋值
Person.prototype.getName = function() {
return this.name;
}
修改this指向
call:立即运行n个参数
function Point(x,y){
this.x = x
this.y = y
}
let person = {
name:'黄小米'
}
Point.call(person,1,1)
console.log(person)
apply:立即运行,数组传参,两个参数
function Point(x,y){
this.x = x
this.y = y
}
let person = {
name:'黄小米'
}
Point.apply(person,[1,1])
console.log(person)
bind:绑定this,生成一个新的函数
function Point(x,y){
this.x = x
this.y = y
}
let person = {
name:'黄小米'
}
let creat = Point.bind(person)
creat(1,1)
console.log(person)