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. 清除TFS版本控制信息

    http://blog.csdn.net/feihu_guest/article/details/8442434 How to permanently remove TFS Source Contro ...

  2. js中初学函数的使用

    <script> function SetColor(name,value) { var oDiv=document.getElementById('div3'); oDiv.style[ ...

  3. The C Programming Language (second edition) 实践代码(置于此以作备份)

    1. #include <stdio.h> #include <stdlib.h> #include <math.h> #include<time.h> ...

  4. mysql gb2312与lanti1

    1.如果数据库编码为lanti1,页面编码utf-8和gb2312均可,并且不用set names,设置就会乱码: 2.如果数据库编码为utf8,页面编码utf-8和gb2312均可,一定要设置好se ...

  5. ajax 如何做到 SEO 友好

    我猜你是在网络上搜索“ajax如何被搜索引擎收录”.“ajax SEO”.“ajax SEO友好”等关键词来到这里的.你可能已经很疲惫了,因为前段时间我也这样搜索,但是我发现搜索到的内容质量不高,有的 ...

  6. Swig 使用指南

    如何使用 API swig.init({ allowErrors: false, autoescape: true, cache: true, encoding: 'utf8', filters: { ...

  7. CSS - 如何实现强制不换行、自动换行、强制换行 以及 chrom默认焦点 IE下 Input 默认出现叉

    *:focus {outline: none;} input::-ms-clear {display:none;} 一般的文字截断(适用于内联与块): .text-overflow { display ...

  8. Bookshelf 2

    Bookshelf 2 Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit  ...

  9. Maven 安装以及一些开发技巧

    解压 apache-maven-3.2.5 在conf ->sites中配置repository 的路径. Eclipse 配置 maven 2. 3. 一些小BUG 或开发技巧 eclipse ...

  10. Sql存储过程分页--临时表存储

    set ANSI_NULLS ON set QUOTED_IDENTIFIER ON go -- ============================================= -- Au ...