面向对象编程(OOP)
面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构。
OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。
OOP达到了软件工程的三个主要目标:重用性、灵活性和扩展性。为了实现整体运算,每个对象都能够接收信息、处理数据和向其它对象发送信息。
面向对象与面向过程的区分
面向对象是注重于对象自身(谁去做的)
面向过程是注重于函数过程(做了什么)
构造函数方式
所谓”构造函数”,其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。
例如:
var Storages = function () {
this.version = 'Lsy storage-group 1.0.1';
};
var sto = new Storages();
console.log(sto.version); //Lsy storage-group 1.0.1
prototype 原型介绍
简单来说就是:
prototype是javascript中的函数(function)的一个保留属性,并且它的值是一个对象(我们可以称这个对象为”prototype对象”)。
Prototype模式
Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
…… 此处省略理论知识一万字。
来几个案例才是王道:
例一:初识构造函数
//通常把Storage的集合叫做类class
var Storage = function () {
//私有属性
var author = 'loushengyue';
//私有方法(函数表达式)
var getAuthor = function () {
return author;
};
getAuthor(); //调用私有函数表达式
showAuthor(); //调用私有方法
//私有方法
function showAuthor() {
console.log(author);
}
//共有属性
this.version = '1.0.1';
//特权方法
this.create = function () {
console.log('create fn');
this.other(); //可以调用共有方法
showAuthor(); //可以调用私有方法
}
};
//共有方法
Storage.prototype.other = function () {
console.log('other fn');
};
//共有属性
Storage.prototype.firstName = 'lou';
//静态方法
Storage.init = function () {
new this();
};
//静态属性
Storage.createTime = '20180204';
例二:初识构造函数原型
var Storages = function () {
this.version = 'Lsy storage-group 1.0.1';
};
Storages.prototype.set = function () {
console.log('set function');
};
Storages.prototype.get = function () {
console.log('get function');
};
//构造函数的显示原型指向set函数和get函数(记住构造函数的原型属性可以是字符串,数组,对象;不过通常是函数居多)
console.log(Storages.prototype);
//构造函数的原型属性constructor指向构造函数本身
console.log(Storages.prototype.constructor);
//构造函数的隐式原型执行构造函数Function ()
console.log(Storages.__proto__.constructor);
例三:初识实例对象
var Storages = function () {
this.version = 'Lsy storage-group 1.0.1';
};
Storages.prototype.set = function () {
console.log('set function');
};
Storages.prototype.get = function () {
console.log('get function');
};
var sto = new Storages(); //实例化构造函数Storages(),并赋值给变量sto
//通过实例化得到的sto对象具有构造函数一切共有属性及原型属性
console.log(sto.version); //Lsy storage-group 1.0.1
sto.set(); //具有构造函数Storages()的set方法
sto.get(); //具有构造函数Storages()的get方法
例四:初识实例对象原型与构造函数原型之间的关系
var Storages = function () {
this.version = 'Lsy storage-group 1.0.1';
};
Storages.prototype.set = function () {
console.log('set function');
};
var sto = new Storages(); //实例化构造函数Storages(),并赋值给变量sto
//因为prototype不是一个构造函数,所以他的显示原型是undefined
console.log(sto.prototype); //undefined
//实例对象sto的隐式原型是构造函数Storages()的显示原型
console.log(sto.__proto__ === Storages.prototype); // true
//同样的实例对象sto的隐式原型的构造函数===构造函数Storages()本身
console.log(sto.__proto__.constructor === Storages); // true
//另外,由例二可知“构造函数的原型属性constructor指向构造函数本身”
console.log(sto.__proto__.constructor === Storages.prototype.constructor); // true
例五:构造函数多次实例化
var Storages = function () {
this.version = 'version: 1.0.1';
};
Storages.prototype.set = function () {
console.log('set function');
};
var sto1 = new Storages();
var sto2 = new Storages();
var sto3 = new Storages();
//因为实例对象继承了构造函数的所有共有信息,所以以下结果都是相同的
console.log(sto1.version); //version: 1.0.1
console.log(sto2.version); //version: 1.0.1
console.log(sto3.version); //version: 1.0.1
sto1.set();// set function
sto2.set();// set function
sto3.set();// set function
例六:实例对象自定义属性与构造函数属性
var Storages = function () {
this.version = 'version: 1.0.1';
};
Storages.prototype.set = function () {
console.log('set function');
};
var sto = new Storages();
sto.pluginName = 'Lsy storage';
sto.name = 'loushengyue';
//实例对象跟普通对象一样,可以定义自己的属性
console.log(sto.pluginName); //Lsy storage
//给实例对象定义的属性并不会被反馈给构造函数,所以构造函数没有pluginName属性
console.log(Storages.pluginName); //undefined
console.log(sto.name); //loushengyue
//不得不提的是: 构造函数自带一个name属性,并且指向构造函数名称
console.log(Storages.name); //Storages
例七:实例对象读取属性的先后顺序
var Storages = function () {
this.version = 'version: 1.0.1';
this.type = 'javascript';
};
Storages.prototype.version = 'prototype version';
Storages.prototype.type = 'prototype javascript';
Storages.prototype.str = 'prototype string';
var sto = new Storages();
sto.version = 'sto version: 10.0.1';
console.log(sto.version); //sto version: 10.0.1
console.log(sto.type); //javascript
console.log(sto.str); //prototype string
console.log(sto.icon); //undefined
通过上面的例子,很显然得到这样的结论:
实例对象优先读取自身定义的属性,然后读取构造函数共有属性,其次读取构造函数原型属性,……(这里其实还有读取构造函数的隐式原型链上的属性)……,如果都读取不到结果才为“undefined
”
例八:实例对象调用方法的先后顺序
var Storages = function () {
this.test = function () {console.log('constructor testFn');};
this.set = function () {console.log('constructor setFn');}
};
Storages.prototype.test = function () {console.log('prototype testFn');};
Storages.prototype.set = function () {console.log('prototype setFn');};
Storages.prototype.get = function () {console.log('prototype getFn');};
var sto = new Storages();
sto.test = function () {console.log('sto testFn');};
sto.test(); //sto testFn
sto.set(); //constructor setFn
sto.get(); //constructor getFn
sto.toString(); //
sto.hehe(); //error:sto.hehe is not a function
实例对象调用方法跟读取属性一样,都是同样的顺序。
不过,这里与例七不同的是sto.toString()
没有任何结果,也没有报错(原因:toString()
是实例对象sto的构造函数的隐式原型的构造函数的属性方法,即构造函数Function()
的属性方法)。
而hehe()
表明在实例对象的所有被继承对象中都未找到该方法,故而报错。
例九:this属性继承
//父类
var Storage = function () {
var author = 'loushengyue'; //私有属性不支持继承
this.version = '1.0.2';
this.create = function () {
console.log('create fn');
};
function showAuthor() { //私有方法不支持继承
console.log(author);
}
};
//原型上的方法需要采用其他方式继承
Storage.prototype.getAuthor = function () {
console.log('get author fn');
};
//子类
var LsyStorage = function () {
this.son = 'LsyStorage plugin';
//apply与call用法类似,只是有传递参数的区别
Storage.apply(this); //将继承父类Storage所有this属性与方法(不包括原型prototype)
// Storage.call(this);//将继承父类Storage所有this属性与方法(不包括原型prototype)
};
例十:类的完整继承
//父类
var Parent = function () {
//handle
};
Parent.prototype.test = function () {
//handle
};
//子类
var Child = function () {
Parent.call(this); //继承Parent的this属性与方法(不包括原型)
};
Child.prototype = Object.create(Parent.prototype); //继承Parent的显示原型
Child.prototype.constructor = Child; //恢复Child的constructor指向本身