面向对象编程 原型链 继承
第?章 - ?向对象编程
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> 普通方式
封装:让使?对象的?不考虑内部实现,只考虑功能使? 把内部的代码保护起来,只留出?些 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.工厂模式以上这两种?式都?法识别对象类型,?如 Player 的类型只是 Object 2> 构造函数/实例 通过 this 添加的属性和?法总是指向当前对象的,所以在实例化的时候,通过 this 添加的属性和?法都会在内存中复制?份,这样就会造成内存的浪费。但是这样创建的好处是即使改变了某?个对象的属性或?法,不会影响其他的对象(因为每?个对象都是复制的?份)
function createObject() { const Player = new Object(); Player.color = "white"; Player.start = function () { console.log("white下棋"); }; return Player; }
function Player(color) { this.color = color; this.start = function () { console.log(color + "下棋"); }; } const whitePlayer = new Player("white"); const blackPlayer = new Player("black");3> 原型 通过原型继承的?法并不是?身的,我们要在原型链上?层?层的查找,这样创建的好处是只在内存中创建?次,实例化的对象都会指向这个 prototype 对象。
// Tips. 怎么看函数是不是在内存中创建了多次呢?
// ?如 2. 构造函数中,我们可以看到 whitePlayer.start === blackPlayer.start // 输出 false
function Player(color) { this.color = color;4. 静态属性 是绑定在构造函数上的属性?法,需要通过构造函数访问?如我们想看?下?共创建了多少个玩家的实例
} Player.prototype.start = function () { console.log(color + "下棋"); }; const whitePlayer = new Player("white"); const blackPlayer = new Player("black");
function Player(color) { this.color = color; if (!Player.total) { Player.total = 0;// 静态属性 } Player.total++;第?章 - 原型及原型链 2.1在原型上添加属性或者?法有什么好处? 刚才已经说过了,如果不通过原型的?式,每?成?个新对象,都会在内存中新开辟?块存储空间,当对象变多之后,性能会变得很差。但是通过
} let p1 = new Player("white"); console.log(Player.total); // 1 let p2 = new Player("black"); console.log(Player.total); // 2
Player.prototype.xx = function () {}; Player.prototype.xx = function () {}; Player.prototype.xx = function () {};这种?式向原型对象添加属性或者?法的话,?显得?常麻烦。所以我们可以这样写
Player.prototype = { start: function () { console.log("下棋"); }, revert: function () { console.log("悔棋"); }, };2.2怎么找到 Player 的原型对象?
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]