[Effective JavaScript 笔记]第40条:避免继承标准类
ECMAScript标准库里配备了许多重要的类,如Array,function,以及Date等。扩展这些类生成子类可以方便完成很多工作,但它们的定义具有很多特殊的行为,所以很难写出行为正确的类。
Array示例
一个操作文件系统的库可能希望创建一个对象的目录,该目录继承了数组的所有行为。
function Dir(path,entries){
this.path=path;
for(var i=0,n=entries.length;i < n;i++){
this[i]=entries[i];
}
}
Dir.prototype=Object.create(Array.prototype);
运行下面这段代码,并没有得到预期的行为。
var dir=new Dir('/tmp/mysite',['index.html','script.js','style.css']);
dir.length;//0
失败的原因
length属性只对在内部被标记为“真正的”数组的特殊对象起作用。ECMAScript标准规定它是一个不可见的内部属性,称为[[Class]]。它的值只是一个简单的标签,并不是js真正有内部类系统。数组对象的[[Class]]值是"Array",函数对应的[[Class]]是"Function"。下面这个表是ECMAScript中定义的完整的[[Class]]属性值集合。

解密length
那么神奇的[[Class]]属性对length做了什么呢?事实上,length的行为只被定义在内部属性[[Class]]的值为"Array"的特殊对象中。对于这些对象,js保持length属性与该对象的索引属性的数量同步。如果给该对象添加了更多的索引属性,length属性会自动增加。如果减少了length属性,也会自动删除任何索引大于该新值的索引属性。
当我们扩展Array类时,子类的实例并不是通过new Array()或字面量[]语法创建的。所以Dir实例的[[Class]]属性值为"Object"。
使用默认的Object.prototype.toString()方法可以通过查询其接收者的内部[[Class]]属性来创建对象的通用描述。可以传递任何给定的对象来显式地调用它。
var dir=new Dir('/',[]);
Object.prototype.toString.call(dir);//"[Object Object]"
Object.prototype.toString.call([]);//"[Object Array]"
结果Dir的实例未继承数组的length属性所期望的特殊行为。
更好的实现
定义一个entries数组的实例属性。
function Dir(path,entries){
this.path=path;
this.entries=entries;
}
在原型中重新定义Array的方法,将这些相应的方法委托给entries属性来实现。
Dir.prototype.forEach=function(f,thisArg){
if(typeof thisArg==='undefined'){
thisArg=this;
}
this.entries.forEach(f,thisArg);
}
ECMAScript标准库中的大多数构造函数都有类似的问题。某些属性或方法期望具有正确的[[Class]]属性或其他特殊的内部属性,然而子类却无法提供。基于这个原因,最好避免继承以下的标准类:Array,Boolean,Date,Function,RegExp,String。
提示
继承标准类往往会由于一些特殊的内部属性(如[[Class]])而被破坏
使用属性委托优于继承标准类
[Effective JavaScript 笔记]第40条:避免继承标准类的更多相关文章
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染
之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...
- [Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑
构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.t ...
- [Effective JavaScript 笔记]第39条:不要重用父类的属性名
假设想给上节讲的场景图库添加收集诊断信息的功能.这对于调试和性能分析很有用. 38条示例续 给每个Actor实例一个唯一的标识数. 添加标识数 function Actor(scene,x,y){ t ...
- [Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合
对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置 ...
- [Effective JavaScript 笔记]第51条:在类数组对象上复用通用的数组方法
前面有几条都讲过关于Array.prototype的标准方法.这些标准方法被设计成其他对象可复用的方法,即使这些对象并没有继承Array. arguments对象 在22条中提到的函数argument ...
随机推荐
- [Bug]当IDENTITY_INSERT设置为OFF时,不能为表“xx”中的标识列插入显示的值
写在前面 在设计数据库表时,将主键设置为了自增的.在使用linq to sql的时候,添加数据,出现此错误. 解决方案 找到linq to sql生成的**.dbml文件,在对应的表上面右键修改其属性 ...
- jQuery基础之(二)jQuery中的$
在jQuery中,最常用的莫过于使用美元符号$,它提供了各种各样的丰富功能.包括选择页面中一个或者一类元素.作为功能函数的前缀.windows.onload的完善,创建DOM节点等.本文介绍jQuer ...
- c# 关于浅拷贝和深拷贝
class Program { static void Main(string[] args) { //浅拷贝 Person p1 = new Person(); p1.Name = "张三 ...
- 阿里百川IMSDK--自定义群聊界面
// 获取群对象 YWTribe *tribe = [self.tribesArray objectAtIndex:indexPath.row]; // 发起群聊 UIViewController * ...
- 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0 +VS 2013 开发环境配置
图片太多,具体过程参照: [OpenCV入门教程之一] 安装OpenCV:OpenCV 3.0.OpenCV 2.4.8.OpenCV 2.4.9 +VS 开发环境配置 说下我这边的设置: 选择deb ...
- iOS开发获取本机手机号码
最近有个奇葩需求,用户登录返回手机号匹配本机号码相同才可以登录,吓得我虎躯一震,经了解,iOS7后不越狱实现不了 "For security reasons, iPhone OS restr ...
- 【codevs 1200】【NOIP 2012】同余方程 拓展欧几里德求乘法逆元模板题
模板,,, #include<cstdio> using namespace std; void exgcd(long long a,long long b,long long & ...
- 使用Owin中间件搭建OAuth2.0认证授权服务器
前言 这里主要总结下本人最近半个月关于搭建OAuth2.0服务器工作的经验.至于为何需要OAuth2.0.为何是Owin.什么是Owin等问题,不再赘述.我假定读者是使用Asp.Net,并需要搭建OA ...
- Eclipse-插件的安装之link文件方法
1. 我的eclipse路径为eclipse_Home,在eclipse文件夹下建文件夹MyPlugins. 2. 下载插件并解压得到包含features和plugins的文件夹theXXX. 3. ...
- 【POJ 3020】Antenna Placement(二分图匹配)
相当于用1*2的板覆盖给定的h*w的格子里的点,求最少的板.可以把格子相邻的分成两个集合,如下图,0为一个集合,1的为一个,也就是(行数+列数)为奇数的是一个集合,为偶数的为另一个集合.1010101 ...