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条:避免继承标准类的更多相关文章

  1. [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  2. [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  3. [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

  4. [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  5. [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染

    之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...

  6. [Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑

    构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.t ...

  7. [Effective JavaScript 笔记]第39条:不要重用父类的属性名

    假设想给上节讲的场景图库添加收集诊断信息的功能.这对于调试和性能分析很有用. 38条示例续 给每个Actor实例一个唯一的标识数. 添加标识数 function Actor(scene,x,y){ t ...

  8. [Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合

    对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置 ...

  9. [Effective JavaScript 笔记]第51条:在类数组对象上复用通用的数组方法

    前面有几条都讲过关于Array.prototype的标准方法.这些标准方法被设计成其他对象可复用的方法,即使这些对象并没有继承Array. arguments对象 在22条中提到的函数argument ...

随机推荐

  1. [Bug]当IDENTITY_INSERT设置为OFF时,不能为表“xx”中的标识列插入显示的值

    写在前面 在设计数据库表时,将主键设置为了自增的.在使用linq to sql的时候,添加数据,出现此错误. 解决方案 找到linq to sql生成的**.dbml文件,在对应的表上面右键修改其属性 ...

  2. jQuery基础之(二)jQuery中的$

    在jQuery中,最常用的莫过于使用美元符号$,它提供了各种各样的丰富功能.包括选择页面中一个或者一类元素.作为功能函数的前缀.windows.onload的完善,创建DOM节点等.本文介绍jQuer ...

  3. c# 关于浅拷贝和深拷贝

    class Program { static void Main(string[] args) { //浅拷贝 Person p1 = new Person(); p1.Name = "张三 ...

  4. 阿里百川IMSDK--自定义群聊界面

    // 获取群对象 YWTribe *tribe = [self.tribesArray objectAtIndex:indexPath.row]; // 发起群聊 UIViewController * ...

  5. 【OpenCV入门教程之一】 安装OpenCV:OpenCV 3.0 +VS 2013 开发环境配置

    图片太多,具体过程参照: [OpenCV入门教程之一] 安装OpenCV:OpenCV 3.0.OpenCV 2.4.8.OpenCV 2.4.9 +VS 开发环境配置 说下我这边的设置: 选择deb ...

  6. iOS开发获取本机手机号码

    最近有个奇葩需求,用户登录返回手机号匹配本机号码相同才可以登录,吓得我虎躯一震,经了解,iOS7后不越狱实现不了 "For security reasons, iPhone OS restr ...

  7. 【codevs 1200】【NOIP 2012】同余方程 拓展欧几里德求乘法逆元模板题

    模板,,, #include<cstdio> using namespace std; void exgcd(long long a,long long b,long long & ...

  8. 使用Owin中间件搭建OAuth2.0认证授权服务器

    前言 这里主要总结下本人最近半个月关于搭建OAuth2.0服务器工作的经验.至于为何需要OAuth2.0.为何是Owin.什么是Owin等问题,不再赘述.我假定读者是使用Asp.Net,并需要搭建OA ...

  9. Eclipse-插件的安装之link文件方法

    1. 我的eclipse路径为eclipse_Home,在eclipse文件夹下建文件夹MyPlugins. 2. 下载插件并解压得到包含features和plugins的文件夹theXXX. 3.  ...

  10. 【POJ 3020】Antenna Placement(二分图匹配)

    相当于用1*2的板覆盖给定的h*w的格子里的点,求最少的板.可以把格子相邻的分成两个集合,如下图,0为一个集合,1的为一个,也就是(行数+列数)为奇数的是一个集合,为偶数的为另一个集合.1010101 ...