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原型继承与面向对象的更多相关文章

  1. [已读]编写高质量代码--Web前端开发修炼之道

    我觉得还蛮实用的一本,推荐看看,主要涉及到这些: 标签语义化.css模块化. css的一些东西,比如haslayout 文档流,还有如何实现水平.垂直居中. js代码组织与js分层.js压缩 编码规范 ...

  2. 编写高质量代码:Web前端开发修炼之道(一)

    最近老大给我们买来一些技术方面的书籍,其实很少搬着一本书好好的完整的看完过,每每看电子档的,也是打游击式的看看这章,瞅瞅那章,在那5本书中挑了一本比较单薄的<编写高质量代码web前端开发修炼之道 ...

  3. 编写高质量代码:Web前端开发修炼之道(四)

    这一节是继上一节高质量的Javascript 7)编程实用技巧 1:弹性 从 一个标签区和内容区的实例(就是点击不同的标签菜单显示不同的内容块)来说明不需要每个tabmenu都设置onclick事件, ...

  4. 编写高质量代码:Web前端开发修炼之道(三)

    第五章:高质量的Javascript 这章的内容我看的最久,这是跟我js基础没打好有着莫大的关系,但是还是耐着性子看完了, 不懂的东西都是百度上搜索,理解后再继续.下面是记录下来的笔记. 1)如何避免 ...

  5. 编写高质量代码:Web前端开发修炼之道(二)

    第四章:高质量的css 1)怪异模式和标准模式 在标准模式中,浏览器根据规范表现页面:而怪异模式通常模拟老式浏览器的行为以防止老站点无法工作. 他们两者之间的差异比较典型的表现在IE对盒模型的解析:在 ...

  6. 读《编写高质量代码-Web前端开发修炼之道》笔记

    第一章 1.Web标准由一系列标准组合而成,核心理念是将网页的结构,样式和行为分离,所以分为三大部分:结构标准,样式标准和行为标准.结构标准包括XML标准,XHTML标准,HTML标准:样式标准指CS ...

  7. 《编写高质量代码——Web前端开发修炼之道》读后随笔

    结构样式行为的分离 结构标准包括XML标准.XHTML标准.HTML标准:样式标准有CSS标准:行为标准主要包括DOM标准和ECMAScript标准. 通常的项目会按照如上的方式进行分离,但自己曾今做 ...

  8. 通用base.css —— 《编写高质量代码 web前端开发修炼之道》

    @charset "utf-8"; /*CSS reset*/ html{color:#000;background:#FFF;} body,div,dl,dt,dd,ul,ol, ...

  9. 『编写高质量代码Web前端开发修炼手册』读书笔记--高质量的CSS

    1.怪异模式和DTD 标准模式:浏览器根据规范表现页面 怪异模式:模拟老浏览器行为防止老站点无法工作(为了兼容老式浏览器的代码),如果漏写DTD(Document Type Definition文档定 ...

随机推荐

  1. MySql批处理的小窍门:排行榜类数据生成

    MySql批处理的小窍门:排行榜类数据生成 最近在做新版本的开发,其中涉及到排行榜的批量预生成,在此分享给大家. 关键点 名次的计算(不考虑用游标) 单榜单查询 对于排行榜这种类型的数据,当只查一个排 ...

  2. Spring学习1-初识Spring

    一.简介   1.Spring是一个开源的控制反转(Inversion of Control ,IoC)和面向切面(AOP)的容器框架.它的主要目得是简化企业开发.  2.为何要使用Spring?   ...

  3. VirtualBox安装debian的详细方法步骤

    下面是用VirtualBox安装Debian6的方法和步骤 l 新建一个文件夹,用于存放虚拟硬盘,如Debian l 打开VirtualBox,点击新建 l 输入虚拟机名称,Debian_6 l 给虚 ...

  4. Java及Android开发环境搭建

    前言 自从接触java以来,配置环境变量折腾了好几次,也几次被搞得晕头转向,后来常常是上网查阅相关资料才解决.但是过一段时间后一些细节就会记不清了,当要在其他机子上配置时又得上网查或者查阅相关书籍,如 ...

  5. (转)CSS3 @font-face

    @font-face是CSS3中的一个模块,他主要是把自己定义的Web字体嵌入到你的网页中,随着@font-face模块的出现,我们在Web的开发中使用字体不怕只能使用Web安全字体,你们当中或许有许 ...

  6. ios中的几种多线程实现

    iOS 支持多个层次的多线程编程,层次越高的抽象程度越高,使用起来也越方便,也是苹果最推荐使用的方法.下面根据抽象层次从低到高依次列出iOS所支持的多线程编程范式:1, Thread;2, Cocoa ...

  7. 如何有效的保护 JAVA 程序

    从头到尾保护 JAVA 目前关于 JAVA 程序的加密方式不外乎 JAVA 模糊处理(Obfuscator)和运用 ClassLoader 方法进行加密处理这两种方式(其他的方式亦有,但大多是这两种的 ...

  8. eclipse-统计代码行数

    使用Eclipse可以方便的统计工程或文件的代码行数,方法如下: 1.点击要统计的项目或许文件夹,在菜单栏点击Search,然后点击File...  2.选中正则表达式(Regular express ...

  9. Lamp学习笔记

    1,php.ini 文件在哪里  /opt/app/php-5.3/etc/php.ini  --------------------------------------------   2014-0 ...

  10. 老项目的#iPhone6与iPhone6Plus适配#Icon适配

        本文永久地址为http://www.cnblogs.com/ChenYilong/p/4020373.html ,转载请注明出处.  这是Evernote印象笔记的链接:https://www ...