【读书笔记】读《编写高质量代码—Web前端开发修炼之道》 - JavaScript原型继承与面向对象
JavaScript是基于原型的语言,通过new实例化出来的对象,其属性和行为来自于两部分,一部分来自于构造函数,另一部分是来自于原型。构造函数中定义的属性和行为的优先级比原型中定义的属性和优先级高,如果构造函数和原型定义了同名的属性和行为,构造函数中的属性和行为会覆盖原型中的同名的属性和行为。如下图——

当我们声明一个类时,其实同时生成了一个对应的原型,例如我们定义Animal这个类时,会生成一个与Animal类对应的原型,通过Animal.prototype可以指向这个原型,原型可以通过constructor指向Animal类,更确切的说,是指向Animal类的构造函数。
function Animal() {}
var a = Animal.prototype;
b = a.constructor;
alert(b === Animal); //true
//**************************** 1.开始应用原型 ****************************
(function() { function Animal() {} // 旧的方式
// function Animal(name) {
// this.name = name;
// this.type = 'animal';
// this.say = function () {
// alert('Hello');
// }
// } Animal.prototype = {
name : 'king',
say : function() {
//this.name和this.type均能访问到,与它们属性所在的先后顺序是无关的
console.log('I am a ' + this.type + ', my name is ' + this.name);
},
type : 'animal'
} //-----test-----
var dog = new Animal();
dog.say(); //I am a animal, my name is king })();
//**************************** 2.属性与方法的分离 ****************************
(function() { //我们习惯把属性放在构造函数里面
function Animal(name) {
this.name = name || 'king';
this.type = 'animal';
} //将方法放在类的原型里
Animal.prototype = {
say : function() {
console.log('I am a ' + this.type + ', my name is ' + this.name);
}
} //-----test-----
var dog = new Animal();
dog.say(); //I am a animal, my name is king })();
//**************************** 3.公有和私有 ****************************
//用this.xxx定义的属性是公有的,用var xxx定义的属性是私有的
(function() { function Animal(name) {
this.name = name || 'king';
this.type = 'animal';
//私有变量
var age = 20;
//私有方法
var move = function() {
alert('I am a moving dog');
}
} Animal.prototype = {
say : function() {
console.log('I am a ' + this.type + ', my name is ' + this.name);
}
} //-----test-----
var dog = new Animal('wangcai');
console.log(dog.age); //undefined
dog.say(); //I am a animal, my name is wangcai
dog.move(); //报错,has no method 'move' })();
//**************************** 4.如何访问到私有变量(方法) ****************************
(function() { function Animal(name) {
this.name = name || 'king';
this.type = 'animal';
//私有变量
var age = 20;
//私有方法
var move = function() {
alert('I am a moving dog');
}
//在构造器作用域(私有变量的作用域)中,构造一个能够访问私有变量的公有方法
this.say = function() {
console.log('I am a ' + this.type + ', my name is ' + this.name + ', my age is ' + age);
}
this.act = function() {
move();
}
} Animal.prototype = {}; //-----test-----
var dog = new Animal('wangcai');
dog.say(); //I am a animal, my name is wangcai, my age is 20
dog.act(); //I am a moving dog /**
* 将所有属性和行为,无论公有还是私有的属性和行为全部写在构造函数里,的确是最方便的方式,但并不推荐这么做。
* 因为在内存中一个类的原型只有一个,写在原型中的行为,可以被所有实例所共享,实例化的时候,
* 并不会在实例的内存中再复制一份,而写在类里的行为,实例化的时候会在每个实例里复制一份。
* 把行为写在原型里可以减少内存消耗,没有特殊原因,推荐尽量把行为写在原型里。
*/ })();
//**************************** 5.如何访问到私有变量(方法) —— 性能改造****************************
/**
* 写在原型里的行为一定是公有的,而且无法访问私有属性,所以如何处理私有行为和私有属性是个难题。
* 1>如果对属性和行为的私有性有非常高的强制性,我们不得不牺牲内存,将私有行为放在构造函数里,实现真正的私有。
* 2>如果对属性和行为的私有性要求不高,更常见的做法是约定私有行为,
* 通过给属性和行为的名称前面加上“_”来约定它是私有的,这是一种命名约定。
*/
(function() { function Animal(name) {
this.name = name || 'king';
this.type = 'animal';
//私有变量
this._age = 20;
} Animal.prototype = {
_move : function() {
alert('I am a moving dog');
},
say : function() {
//访问this._age来标识age属性是私有的
console.log('I am a ' + this.type + ', my name is ' + this.name + ', my age is ' + this._age);
},
act : function() {
//访问this._age来标识move方法是私有的
this._move();
}
}; //-----test-----
var dog = new Animal('wangcai');
dog.say(); //I am a animal, my name is wangcai, my age is 20
dog.act(); //I am a moving dog
console.log(dog._age); //不推荐实例直接调用_age,违反命名约定
dog._move(); //I am a moving dog 不推荐实例直接调用_move,违反命名约定 })();
//**************************** 6.继承:继承构造函数 ****************************
(function () { function Animal(name) {
//应用函数调用模式,此时的this指向window
this.name = name || 'king';
this.type = 'animal';
} Animal.prototype = {
say: function () {
console.log('I am a ' + this.type + ', my name is ' + this.name);
}
} function Bird(name) {
//此时的this是Bird
Animal(name); //这个方法调用过后this就变成了window(函数调用模式)
} //-----test-----
var bird = new Bird('xiaocui');
console.log(bird.type); //undefined,此时的bird对象没有任何自身属性 })();
//**************************** 7.继承:继承构造函数 —— 改造 ****************************
(function () { function Animal(name) {
//此时的this指向Bird
this.name = name || 'king';
this.type = 'animal';
} Animal.prototype = {
say: function () {
console.log('I am a ' + this.type + ', my name is ' + this.name);
}
} //这里我们只对构造器进行了继承操作,但对于Animal类里面的方法却无法继承过来
function Bird(name) {
//此时的this是Bird
Animal.call(this, name); //利用call方法改变作用域
} //-----test-----
var bird = new Bird('xiaocui');
console.log(bird.type); //animal
bird.say(); //报错,has no method 'say' })();
//**************************** 8.继承:继承原型 —— 1 ****************************
//方式1:Bird.prototype = Animal.prototype;
(function () { function Animal(name) { //初始化完这个函数后Animal.prototype.contructor === Animal; //true
//此时的this指向Bird
this.name = name || 'king';
this.type = 'animal';
} //对Animal.prototype重新赋值,那么此时的Animal.prototype.contructor === Animal; //false
Animal.prototype = {
say: function () {
console.log('I am a ' + this.type + ', my name is ' + this.name);
}
} function Bird(name) {
Animal.call(this, name);
} //原型继承
Bird.prototype = Animal.prototype; //-----test-----
var bird = new Bird('xiaocui');
console.log(bird.type); //animal
bird.say(); //I am a animal, my name is xiaocui })();
//**************************** 9.继承:继承原型 —— 2 ****************************
//方式1:Bird.prototype = Animal.prototype;
//给子类增加独有方法,如给Bird类增加fly方法
(function () { /**
* Animal.prototype = { //Object
* constructor: funciton Animal(name) {...},
* __proto__: Object
* }
*/
function Animal(name) {
this.name = name || 'king';
this.type = 'animal';
} /**
* Animal.prototype = { //Object
* say: funciton () {...},
* __proto__: Object
* }
*/
Animal.prototype = {
say: function () {
console.log('I am a ' + this.type + ', my name is ' + this.name);
}
} /**
* Bird.prototype = { //Object
* constructor: funciton Bird(name) {...},
* __proto__: Object
* }
*/
function Bird(name) {
Animal.call(this, name);
} /**
* 执行完这一句,Bird.prototype与Animal.prototype指向同一个对象,即
* Bird.prototype = Animal.prototype = { //Object
* say: funciton () {...},
* __proto__: Object
* }
*/
Bird.prototype = Animal.prototype; /**
* 执行完这一句,增加Bird.prototype的属性(fly方法),即
* Bird.prototype = Animal.prototype = {
* fly: function () {...},
* say: function () {...},
* __proto__: Object
* }
*/
Bird.prototype.fly = function () {
console.log('I am flying');
} //-----test-----
var bird = new Bird('xiaocui');
bird.fly(); //I am flying var dog = new Animal('king');
dog.fly(); //I am flying
/**
* 由于Bird.prototype和Animal.prototype指向同一个地址,对Bird.prototype新增的方法,
* 通过对Animal.prototype的属性访问也能够访问到Bird.prototype新增的方法。
* 显然,这并不是我们想要的。
*/ })();
//**************************** 10.继承:继承原型 —— 3 ****************************
//方式二:Bird.prototype = new Animal();
//给子类增加独有方法,如给Bird类增加fly方法
(function () { function Animal(name) {
this.name = name || 'king';
this.type = 'animal';
} Animal.prototype = {
say: function () {
console.log('I am a ' + this.type + ', my name is ' + this.name);
}
} function Bird(name) {
Animal.call(this, name);
} /**
* 执行完这一句,把Animal原型中的say方法继承过来,即
* Bird.prototype = {
* name: 'king',
* type: 'animal',
* __proto__: {
* say: function () {...}
* }
* }
*/
Bird.prototype = new Animal(); /**
* 执行完这一句,重新赋值Bird.prototype的构造器属性,即
* Bird.prototype = {
* constructor: function Bird(name) {...},
* name: 'king',
* type: 'animal',
* __proto__: {
* say: function () {...}
* }
* }
*/
Bird.prototype.constructor = Bird; /**
* 执行完这一句,增加Bird.prototype的属性(fly方法),即
* Bird.prototype = {
* constructor: function Bird(name) {...},
* fly: function () {...},
* name: 'king',
* type: 'animal',
* __proto__: {
* say: function () {...}
* }
* }
*/
Bird.prototype.fly = function () {
console.log('I am flying');
} //-----test-----
var bird = new Bird('xiaocui');
bird.say(); //I am a animal, my name is xiaocui
bird.fly(); //I am flying var dog = new Animal('king');
dog.fly(); //报错,has no method 'fly' })();
//**************************** 11.继承:封装继承函数 ****************************
(function () { function extend(subClass, superClass) { var F = function () {}; //构建中间量
F.prototype = superClass.prototype; //中间量原型指向父类原型 subClass.prototype = new F(); //将父类原型赋值给子类原型,能够继承所有父类原型的方法 subClass.prototype.constructor = subClass; //子类原型构造器属性指回子类构造器,使得子类的方法不会新增或覆盖父类的方法 subClass.superclass = superClass.prototype; //给子类添加superclass属性,并将其赋值父类原型
//使得在子类构造器中成功利用superclass属性来调用父类构造器
if (superClass.prototype.constructor === Object.prototype.constructor) {
superClass.prototype.constructor = superClass; //重新赋值父类原型构造器为父类
} } function Animal(name) {
this.name = name || 'king';
this.type = 'animal';
} Animal.prototype = {
say: function () {
console.log('I am a ' + this.type + ', my name is ' + this.name);
}
} function Bird(name) {
//具备通用性
this.constructor.superclass.constructor.apply(this, arguments);
} extend(Bird, Animal); Bird.prototype.fly = function () {
alert('I am flying');
} //-----test-----
var bird = new Bird('xiaocui');
bird.say(); //I am a animal, my name is xiaocui
bird.fly(); //I am flying })();
//**************************** 12.面向过程与面向对象 ****************************
/**
* 面向过程编程
* 1.这种编程方式将程序分成了“数据”和“处理函数”两个部分,程序以“处理函数”为核心,
* 如果要执行什么操作,就将“数据”传给相应的“处理函数”,返回我们需要的结果。这种编程方式就是面向过程编程。
* 2.这种编程方式存在的问题如下:
* 1> 数据和处理函数之间没有直接的关联,在执行操作的时候,我们不但要选择相应的处理函数,
* 还要自己准备处理函数需要的数据,也就是说,在执行操作的时候,我们需要同时关注处理函数和数据。
* 2> 数据和处理函数都暴露在同一个作用域内,没有私有和公有的概念,整个程序中所有的数据和处理函数都可以相互访问,
* 在开发阶段初期也许开发速度很快,但到了开发后期和维护阶段,由于整个程序耦合得非常紧,
* 任何一个处理函数和数据都有可能关联到其他地方,容易牵一发而动全身,从而加大了修改难度。
* 3> 面向过程的思维方式是典型的计算机思维方式——输入数据给处理器,处理器内部执行运算,处理器返回结果。
* 也就是说面向过程的思维方式是在描述一个个“动作”。
* 而现实生活中的一个个“物件”(如人{姓名,状态})很难用面向过程的思维方式进行描述。
*
* 面向对象编程
* 这种编程就是抛开计算机思维,使用生活中的思维进行编程的编程方式。面向过程的思维就是描述一个个“动作”,
* 而面向对象的思维就是描述一个个“物件”,客观生活中的物件,在程序中我们管“物件”叫做“对象”,
* 对象由两部分组成:“属性”和“行为”,对应客观世界中物件的“状态”和“动作”。
*/ //面向过程
(function () { //定义电话本
var phonebook = [
{name: 'adang', tel: '1111'},
{name: 'king', tel: '2222'}
]; //查询电话
function getTel(oPhoneBook, oName) {
var tel = '';
for (var i = 0; i < oPhoneBook.length; i++) {
if (oPhoneBook[i].name === oName) {
tel = oPhoneBook[i].tel;
break;
}
}
return tel;
} //添加记录
function addItem(oPhonebook, oName, oTel) {
oPhonebook.push({name: oName, tel: oTel});
} //删除记录
function removeItem(oPhonebook, oName) {
var index;
for (var i = 0; i < oPhonebook.length; i++) {
if (oPhonebook[i].name === oName) {
index = i;
break;
}
}
if (index !== undefined) {
oPhonebook.splice(index, 1);
}
} //-----test-----
//调用函数-增
addItem(phonebook, 'xiaoxiao', '3333');
//调用函数-删
removeItem(phonebook, 'xiaoxiao');
//调用函数-查
getTel(phonebook, 'king'); })(); //面向对象
(function () { //构造函数专注的是对象所具有的属性
function PhonebookManager(oPhonebook) {
this._phonebook = oPhonebook;
} //所有方法均在构造函数的原型中添加
PhonebookManager.prototype = {
addItem: function (oName, oTel) {
this._phonebook.push({name: oName, tel: oTel});
},
removeItem: function (oName) {
var index,
phonebook = this._phonebook;
for (var i = 0, len = phonebook.length; i < len; i++) {
if (phonebook[i].name === oName) {
index = i;
break;
}
}
if (index !== undefined) {
phonebook.splice(index, 1);
}
},
getTel: function (oName) {
var tel = '',
phonebook = this._phonebook;
for (var i = 0; i < this._phonebook.length; i++) {
if (phonebook.name === oName) {
tel = phonebook.tel;
break;
}
}
return tel;
}
} //------test-----
var myPhoneManager = new PhonebookManager([
{name: 'adang', tel: '1111'},
{name: 'king', tel: '2222'}
]); //调用函数-增
myPhoneManager.addItem('xiaoxiao', '3333');
//调用函数-删
myPhoneManager.removeItem('xiaoxiao');
//调用函数-查
myPhoneManager.getTel('king'); })();
【读书笔记】读《编写高质量代码—Web前端开发修炼之道》 - JavaScript原型继承与面向对象的更多相关文章
- [已读]编写高质量代码--Web前端开发修炼之道
我觉得还蛮实用的一本,推荐看看,主要涉及到这些: 标签语义化.css模块化. css的一些东西,比如haslayout 文档流,还有如何实现水平.垂直居中. js代码组织与js分层.js压缩 编码规范 ...
- 编写高质量代码:Web前端开发修炼之道(一)
最近老大给我们买来一些技术方面的书籍,其实很少搬着一本书好好的完整的看完过,每每看电子档的,也是打游击式的看看这章,瞅瞅那章,在那5本书中挑了一本比较单薄的<编写高质量代码web前端开发修炼之道 ...
- 编写高质量代码:Web前端开发修炼之道(四)
这一节是继上一节高质量的Javascript 7)编程实用技巧 1:弹性 从 一个标签区和内容区的实例(就是点击不同的标签菜单显示不同的内容块)来说明不需要每个tabmenu都设置onclick事件, ...
- 编写高质量代码:Web前端开发修炼之道(三)
第五章:高质量的Javascript 这章的内容我看的最久,这是跟我js基础没打好有着莫大的关系,但是还是耐着性子看完了, 不懂的东西都是百度上搜索,理解后再继续.下面是记录下来的笔记. 1)如何避免 ...
- 编写高质量代码:Web前端开发修炼之道(二)
第四章:高质量的css 1)怪异模式和标准模式 在标准模式中,浏览器根据规范表现页面:而怪异模式通常模拟老式浏览器的行为以防止老站点无法工作. 他们两者之间的差异比较典型的表现在IE对盒模型的解析:在 ...
- 读《编写高质量代码-Web前端开发修炼之道》笔记
第一章 1.Web标准由一系列标准组合而成,核心理念是将网页的结构,样式和行为分离,所以分为三大部分:结构标准,样式标准和行为标准.结构标准包括XML标准,XHTML标准,HTML标准:样式标准指CS ...
- 《编写高质量代码——Web前端开发修炼之道》读后随笔
结构样式行为的分离 结构标准包括XML标准.XHTML标准.HTML标准:样式标准有CSS标准:行为标准主要包括DOM标准和ECMAScript标准. 通常的项目会按照如上的方式进行分离,但自己曾今做 ...
- 通用base.css —— 《编写高质量代码 web前端开发修炼之道》
@charset "utf-8"; /*CSS reset*/ html{color:#000;background:#FFF;} body,div,dl,dt,dd,ul,ol, ...
- 『编写高质量代码Web前端开发修炼手册』读书笔记--高质量的CSS
1.怪异模式和DTD 标准模式:浏览器根据规范表现页面 怪异模式:模拟老浏览器行为防止老站点无法工作(为了兼容老式浏览器的代码),如果漏写DTD(Document Type Definition文档定 ...
随机推荐
- groovy-集合
Lists 你能使用下面的方法创建一个lists,注意[]是一个空list. 1 def list = [5, 6, 7, 8] 2 assert list.get(2) == 7 3 assert ...
- STL Iterators
Summary of Chapter 33 STL Iterators from The C++ Programming Language 4th. Ed., Bjarne Stroustrup. - ...
- 轻量级应用开发之(07) UIPickerView使用
#import "ViewController.h" @interface ViewController ()<UIPickerViewDataSource,UIPicker ...
- centos6.3配置MFS服务器
一.简介 MooseFS(Moose File System,mfs)是一种分布式文件系统,它将数据分布在网络中的不同服务器上,支持FUSE(用户空间文件系统Filesystem in Userspa ...
- [Angularjs]视图和路由(一)
写在前面 对单页应用来讲,视图和路由的作用可以从一个视图跳转到另外一个视图,可以合理管理用户在使用过程中看到的界面. 将视图分解成布局和模版视图,并且根据用户当前访问的URL来展示对应的视图,将会是一 ...
- XUnit学习
1.建立测试单元项目 2.引用XUnit.dll或者在Nuget里安装XUnit 3.安装Nuget->xUnit.net[Runner: Visual Studio] 4.打开 测试-> ...
- Boost的状态机库教程(1)
介绍 Boost状态机库一个应用程序框架,你可以用它将UML状态图快速的转换为可执行的c++代码,而不需要任何的代码生成器.它支持几乎所有的UML特征,可以直接了当的转换,并且转换后的c++代码就像对 ...
- Python socket编程之六:多窗口的应用
import struct import sqlalchemy import pandas import matplotlib.pyplot as Plot from matplotlib.finan ...
- get_magic_quotes_gpc函数
magic_quotes_gpc函数在php中的作用是判断解析用户提示的数据,如包括有:post.get.cookie过来的数据增加转义字符“\”,以确保这些数据不会引起程序,特别是数据库语句因为特殊 ...
- GDB中应该知道的几个调试方法 来自陈皓
GDB中应该知道的几个调试方法 2011年2月10日陈皓发表评论阅读评论62,325 人阅读 七.八年前写过一篇<用GDB调试程序>,于是,从那以后,很多朋友在MSN上以及给我发邮件询 ...