前两天去图书馆借了一本《JavaScript之美》,在书架上无意中看到的,想着那就看看吧。

第一章

原型

有些JavaScript爱好者宣称JavaScript是一种基于原型而不是面向对象的语言,任何带有“类”字样的方法根本不适用于JavaScript。但“原型”的含义是什么?原型和类有着怎样的区别?

用通用的编程术语来讲,原型是指为其他对象提供基本行为的对象。其他对象也可在此基础上扩展基本行为,加入个性化行为。该过程也称为有差异的继承,有别于类继承的是它不需要明确指定类型(静态或动态),从形式上而言,它也不是在一种类型的基础上定义其他类型。类继承的目的是复用,而原型继承则不一定。

JavaScript的每个对象均指向一个原型对象并继承其属性。JavaScript的原型是实现复用的好工具。原型还可以继承自其他原型,从而形成原型链。

JavaScript将prototype属性绑定到构造器,其结果是多个层级的对象继承通常需要链接构造器和原型来实现。构造器-原型链句法,不但不优雅,缺点还体现在需要预先规划。ES6的class关键字只是将现有实现方式形式化。

ES5标准引入了Object.create,以增加原型继承的灵活度,扩展应用场景。该方法允许将原型直接赋给对象,JavaScript原型不再受限于构造器和类型的限制。

var circle = Object.create({
area: function() {
return Math.PI * this.radius * this.radius;
},
grow: function() {
this.radius++;
},
shrink: function() {
this.radius--;
}
});

Object.create方法的第2个参数可选,表示继承自哪个对象。不幸的是第2个参数不是对象自身,而是一个完整的 meta 属性定义:

var circle = Object.create({
area: function() {
return Math.PI * this.radius * this.radius;
},
grow: function() {
this.radius++;
},
shrink: function() {
this.radius--;
}
}, {
radius: {
writable: true, configurable: true, value: 7
}
});

或者可以手动将属性赋给它。

即便如此,Object.create方法也只是允许对象继承某个原型的属性,但真实应用场景,往往需要从多个原型对象获得行为。

mixin方法

函数复用的最基本方法是手动委托,任何公共函数都可以直接用call或apply方法调用。

mixin基础

从传统意义上讲,mixin是一个类,它定义了一组原本要用实体定义的函数。然而,mixin类被视作是抽象的,因为它不是由自己来完成实例化。相反,具体的类通过复制(或借)mixin类的函数,继承mixin的行为,而不必跟行为的提供者产生正式的关系。mixin类可以是常规对象,原型或函数等。

应用场景

1:类形式的mixin

先定义一个圆形mixin

var circleFns = {
area: function() {
return Math.PI * this.radius * this.radius;
},
grow: function() {
this.radius++;
},
shrink: function() {
this.radius--;
}
};

再看一个定义按钮行为的mixin

var clickableFns = {
hover: function() {
console.log('hovering');
},
press: function() {
console.log('button pressed');
},
fire: function() {
return this.action();
}
};

如何将mixin对象整合到你的对象之中?你需要借助extend函数。

function extend(destination, source) {
for(var key in source) {
if(source.hasOwnProperty(key)) {
destination[key] = source[key];
}
}
return destination;
}

用刚才创建的两个mixin对象,扩展新对象RoundButton的基础原型RoundButton.prototype:

var RoundButton = function(radius, label, action) {
this.radius = radius;
this.label = label;
this.action = action;
}; extend(RoundButton.prototype, circleFns);
extend(RoundButton.prototype, clickableFns); var roundButton = new RoundButton(3, 'send', function(){return 'send';}); roundButton.area();
roundButton.fire();

函数形式的mixin

下面我们将圆形和按钮mixin改写为函数。

var circleFns = function() {
this.area = function() {
return Math.PI * this.radius * this.radius;
};
this.grow = function() {
this.radius++;
};
this.shrink = function() {
this.radius--;
};
}; var clickableFns = function() {
this.hover = function() {
console.log('hovering');
};
this.press = function() {
console.log('button pressed');
};
this.fire = function() {
return this.action();
};
};

现在原型对象通过Function.prototype.call就能将自己注入到目标对象中去

circleFns.call(RoundButton.prototype);
clickableFns.call(RoundButton.prototype);

这种方法给人的感觉是很贴切,编码风格自然简洁,this总是指向接收者而不是我们不需要的抽象对象,并且,我们不必提防无意中复制了被继承的属性。

带options参数

函数形式的mixin方法还支持通过options参数将行为参数化,以掺杂使用各种行为。看下述示例:

var withOval = function(options) {
this.area = function() {
return Math.PI * this.longRadius * this.shortRadius;
};
this.ratio = function() {
return this.longRadius / this.shortRadius;
};
this.grow = function() {
this.shortRadius += (options.growBy / this.ratio());
this.longRadius += (options.growBy);
};
this.shrink = function() {
this.shortRadius -= (options.shinkBy / this.ratio());
this.longRadius -= (options.shinkBy);
};
}; var OvalButton = function(longRadius, shortRadius, label, action) {
this.longRadius = longRadius;
this.shortRadius = shortRadius;
this.label = label;
this.action = action;
}; withOval.call(OvalButton.prototype, {growBy: 2, shinkBy: 2}); button.area();
button.grow();
button.area();

添加缓存

函数形式的mixin还能做进一步优化,对mixin构造闭包,我们能缓存第一次定义时的结果,由此所带来的性能上的提升非常显著。下面是增加缓存机制的withRectangle mixin:

var withRectangle = (function() {
function area() {
return this.length * this.width;
};
function grow() {
this.length++, this.width++;
};
function shrink() {
this.length--, this.width--;
};
return function() {
this.area = area;
this.grow = grow;
this.shrink = shrink;
return this;
};
})(); var RectangleButton = function(length, width) {
this.length = length;
this.width = width;
};
withRectangle.call(RectangleButton.prototype); var button = new RectangleButton(4, 2);
button.area();

小结

类继承重复用一个对象定义另一个对象,由此形成了一系列紧密的耦合关系,将不同的层级粘结在一起,对象之间的依赖关系非常复杂。相反,mixin极其敏捷,你几乎不需要调整代码库,只要发现了一组通用的,可共享的行为,就可以根据需要创建mixin,而其他所有对象不管在整个模型中扮演什么角色,都可以访问mixin的功能。mixin和对象之间的关系非常自由:mixin的任意组合可应用于任意对象,对象对应用于它的mixin数量也没有限制。这正是原型继承赋予我们的根据机会复用代码的能力。

JavaScript之美读书笔记一的更多相关文章

  1. JavaScript 函数式编程读书笔记2

    概述 这是我读<javascript函数式编程>的读书笔记,供以后开发时参考,相信对其他人也有用. 说明:虽然本书是基于underscore.js库写的,但是其中的理念和思考方式都讲的很好 ...

  2. JavaScript 函数式编程读书笔记1

    概述 这是我读<javascript函数式编程>的读书笔记,供以后开发时参考,相信对其他人也有用. 说明:虽然本书是基于underscore.js库写的,但是其中的理念和思考方式都讲的很好 ...

  3. javascript高级程序设计读书笔记-事件(一)

    读书笔记,写的很乱   事件处理程序   事件处理程序分为三种: 1.html事件2. DOM0级,3,DOM2级别  没有DOM1 同样的事件 DOM0会顶掉html事件   因为他们都是属性  而 ...

  4. javascript框架设计(读书笔记)

    我觉得多看几本进阶的书 与其十本书读一遍,不如一本书读十遍 读书的启示: 读好书(看推荐) 精读(重复看) 能读厚书(javascript权威指南) Object.keys Object.keys=O ...

  5. <JavaScript语言精粹>--<读书笔记三>之replace()与正则

    今天有人问我repalce(),他那个题目很有意思.我也不会做,于是我就去查,结果发现就是最基础的知识的延伸. 所以啊最基础的知识才是很重要的,千万不能忽略,抓起JS就写代码完全不知到所以然,只知道写 ...

  6. <JavaScript语言精粹>-读书笔记(一)

    用object.hasOwnProperty(variable)来确定这个属性名是否为该对象成员,还是来自于原型链. for(my in obj){ if(obj.hasOwnProperty(my) ...

  7. JavaScript语言精粹读书笔记 - JavaScript函数

    JavaScript是披着C族语言外衣的LISP,除了词法上与C族语言相似以外,其他几乎没有相似之处. JavaScript 函数: 函数包含一组语句,他们是JavaScript的基础模块单元,用于代 ...

  8. 深入理解javascript系列,读书笔记

    深入理解JavaScript系列(2):揭秘命名函数表达式 1.讲了函数声明和函数表达式的区别,包括一些在函数提升上的区别 2.如果给函数表达式的函数也取名,会在调试的时候受益 3.不要在block( ...

  9. JavaScript高级程序设计 读书笔记

    第一章 JavaScript 简介 第二章 Html中使用JavaScript 第三章 基本概念 第四章 变量,作用域,内存 第五章 引用类型 第六章 面向对象 第七章 函数表达式 第八章 BOM 第 ...

随机推荐

  1. python爬虫之短信报警

    1 import smtplib import email.mime.multipart import email.mime.text def send_email(content=''): &quo ...

  2. Django的一些操作与视图函数

    一 . Django的安装 pip install django==1.14.6 # 后面的数字是django的版本 二 .  通过命令行(cmd)来创建Django项目 1. 切换到保存项目的文件夹 ...

  3. admin快速搭建后台管理系统

    一.基于admin后台管理系统的特点: 权限管理:权限管理是后台管理系统必不可少的部分,拥有权限管理,可以赋予用户增删改查表权限(可以分别赋予用户对不同的表有不同的操作权限): 前端样式少:后台管理主 ...

  4. varnish4 配置文件整理

    vim default.vcl # 使用varnish版本4的格式. vcl 4.0; # 加载后端轮询模块 import directors; #######################健康检查 ...

  5. orecal基本连接数据库简介

    整理自互联网 一. jdbc:oracle:thin:@192.168.3.98:1521:orcljdbc:表示采用jdbc方式连接数据库oracle:表示连接的是oracle数据库thin:表示连 ...

  6. Nginx 模块分类

    L:34

  7. BZOJ3275Number——二分图最大权独立集

    题目描述 有N个正整数,需要从中选出一些数,使这些数的和最大.若两个数a,b同时满足以下条件,则a,b不能同时被选1:存在正整数C,使a*a+b*b=c*c2:gcd(a,b)=1 输入 第一行一个正 ...

  8. Navicat MySQL 自动备份

    1 新建批处理作业 2 设定(多个库,就轮流选择不同库,然后步骤2) 最后记得保存 3 计划 4 设计批处理作业->设置计划任务 5 输入账号和密码 服务器登录名 服务器密码 6 你可以修改备份 ...

  9. 基准对象object中的基础类型----列表 (四)

    object有如下子类: CLASSES object basestring str unicode buffer bytearray classmethod complex dict enumera ...

  10. 洛谷P2085最小函数值题解

    题目 首先我们先分析一下题目范围,\(a,b,c\) 都是整数,因此我们可以得出它的函数值在\((0,+\infty )\)上是单调递增的,,然后我们可以根据函数的性质,将每个函数设置一个当前指向位置 ...