第?章 - ?向对象编程
1.1什么是?向对象编程?
?向对象是?种编程思想,经常被拿来和?向过程?较。其实说的简单点,?向过程关注的重点是动词,是分析出解决问题需要的步骤,然后编写函数实现每个步骤,最后依次调?函数。??向对象关注的重点是主谓,是把构成问题的事物拆解为各个对象,?拆解出对象的?的也不是为了实现某个步骤,?是为了描述这个事物在当前问题中的各种?为。
1.2?向对象的特点是什么?
封装:让使?对象的?不考虑内部实现,只考虑功能使? 把内部的代码保护起来,只留出?些 api 接?供?户使?
继承:就是为了代码的复?,从?类上继承出?些?法和属性,?类也有??的?些属性
多态:是不同对象作?于同?操作产?不同的效果。多态的思想实际上是把“想做什么”和“谁去做“分开
?如下棋的过程,
?向过程是这样的:开局 ??下棋 棋盘展示 检查胜负 ??下棋 棋盘展示 检查
胜负 循环
?代码表示可能是?连串函数的调?
init();
whitePlay(); // ??实现?遍下棋的操作
repaint(); // 棋盘展示
check();
blackPlay(); // 再单独实现?遍下棋的操作
repaint(); // 棋盘展示
check();
?向对象是这样的:棋盘.开局 选?.下棋 棋盘.重新展示 棋盘.检查胜负 选?.下棋
棋盘.重新展示 棋盘.检查胜负
?代码表示可能是这样的
const checkerBoard = new CheckerBoard(); // CheckerBoard 类内部封账了棋盘的操作,?
如初始化棋盘,检查胜负关系等
const whitePlayer = new Player(‘white’); // Player 类内部封装了各种玩家的操作,?如等待,
落棋,悔棋
const blackPlayer = new Player(‘black’);
whitePlayer.start(); // start ?法的结束,内部封装了或者通过事件发布触发
checkerBoard.repaint(), checkerBoard.check()的调?
blackPlayer.start();
你只需要调? new ?个 player, 然后调? start ?法,也就是说我们只需要关注?为,?不需
要知道内部到底做了什么。
?且如果要加?些新功能,?如悔棋,?如再加?个玩家,?向对象都很好扩展。
1.3在上?的例?中,?向对象的特性是怎么表现出来的呢?
封装:Player, CheckerBoard 类,使?的时候并不需要知道内部实现了什么,只需要考虑暴露出的 api 的使?
继承:whitePlayer 和 blackPlayer 都继承? Player,都可以直接使? Player 的各种?法和属性
多态:whitePlayer.start() 和 blackPlayer.start() 下棋的颜?分别是??和??
1.4Js 中的?向对象
1.4.1对象包含什么? 属性、方法
1.4.2js内置对象? Object Array Date Function RegExp Math...
1.4.3创建对象
1> 普通方式
- // 每?个新对象都要重新写?遍 color 和 start 的赋值
- const Player = new Object();
- Player.color = "white";
- Player.start = function () {
- console.log("white下棋");
- };
- // 2.工厂模式
function createObject() { - const Player = new Object();
- Player.color = "white";
- Player.start = function () {
- console.log("white下棋");
- };
- return Player;
- }
以上这两种?式都?法识别对象类型,?如 Player 的类型只是 Object
2> 构造函数/实例
通过 this 添加的属性和?法总是指向当前对象的,所以在实例化的时候,通过 this 添加的属性和?法都会在内存中复制?份,这样就会造成内存的浪费。但是这样创建的好处是即使改变了某?个对象的属性或?法,不会影响其他的对象(因为每?个对象都是复制的?份)
- function Player(color) {
- this.color = color;
- this.start = function () {
- console.log(color + "下棋");
- };
- }
- const whitePlayer = new Player("white");
- const blackPlayer = new Player("black");
// Tips. 怎么看函数是不是在内存中创建了多次呢?
// ?如 2. 构造函数中,我们可以看到 whitePlayer.start === blackPlayer.start // 输出 false
3> 原型
通过原型继承的?法并不是?身的,我们要在原型链上?层?层的查找,这样创建的好处是只在内存中创建?次,实例化的对象都会指向这个 prototype 对象。
- function Player(color) {
- this.color = color;
} - Player.prototype.start = function () {
- console.log(color + "下棋");
- };
- const whitePlayer = new Player("white");
- const blackPlayer = new Player("black");
4. 静态属性
是绑定在构造函数上的属性?法,需要通过构造函数访问?如我们想看?下?共创建了多少个玩家的实例
- function Player(color) {
- this.color = color;
- if (!Player.total) {
- Player.total = 0;// 静态属性
- }
- Player.total++;
} - let p1 = new Player("white");
- console.log(Player.total); // 1
- let p2 = new Player("black");
- console.log(Player.total); // 2
第?章 - 原型及原型链
2.1在原型上添加属性或者?法有什么好处?
刚才已经说过了,如果不通过原型的?式,每?成?个新对象,都会在内存中新开辟?块存储空间,当对象变多之后,性能会变得很差。但是通过
- Player.prototype.xx = function () {};
- Player.prototype.xx = function () {};
- Player.prototype.xx = function () {};
这种?式向原型对象添加属性或者?法的话,?显得?常麻烦。所以我们可以这样写
- Player.prototype = {
- start: function () {
- console.log("下棋");
- },
- revert: function () {
- console.log("悔棋");
- },
- };
- function Player(color) {
- this.color = color;
} - Player.prototype.start = function () {
- console.log(color + "下棋");
- };
- const whitePlayer = new Player("white");
- const blackPlayer = new Player("black");
- console.log(blackPlayer.__proto__); // Player {}
- console.log(Object.getPrototypeOf(blackPlayer)); // Player {},可以通过
- // Object.getPrototypeOf来获取__proto__
- console.log(Player.prototype); // Player {}
- console.log(Player.__proto__); // [Function]
....