[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 ...
随机推荐
- CSS3——3D效果
1.效果1 <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" c ...
- go语言的模板,text/template包
go语言的模板,text/template包 定义 模板就是将一组文本嵌入另一组文本里 传入string--最简单的替换 package main import ( "os" &q ...
- tomcat配置和优化
转载: https://mp.weixin.qq.com/s?__biz=MzA3MzYwNjQ3NA==&mid=2651296654&idx=1&sn=b04fc6cecf ...
- OC基础--分类(category) 和 协议(protocol)
OC 中的category分类文件相当于 C#中的部分类:OC 中的protocol协议文件(本质是头文件)相当于 C#中的接口.今天就简单说明一下OC中的这两个文件. 由于视频中的Xcode版本低, ...
- CentOS 安装 Jexus
官网:http://www.jexus.org/ 安装过程就照着页面上做就好了,前提是需要安装好mono 在VS2015中新建一个MVC应用程序,这里需要注意两个步骤: 第1步:移除bin下的Micr ...
- tuple内部方法
代码: #tuple内部方法 ac=('a','r','6','d','a','b','b','e') print(dir(ac)) print(ac.count('a')) print(ac.ind ...
- Cocos2d-X3.0 刨根问底(七)----- 事件机制Event源码分析
这一章,我们来分析Cocos2d-x 事件机制相关的源码, 根据Cocos2d-x的工程目录,我们可以找到所有关于事件的源码都存在放在下图所示的目录中. 从这个event_dispatcher目录中的 ...
- 4.Android下拉列表框spinner学习
下拉列表框被广泛使用,诸如淘宝.京东等手机端经常可以看到这个部件身影,今天就来学习下它. 首先布局文件增加代码:如图 接下来在values下增加arrays.xml 如图 ...
- NOI2002 洛谷 P1196 银河英雄传说
神奇的并查集问题 题目描述 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦 创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩 ...
- foeach集合遍历
package number; public class Number { public static void main(String[] args) { int[] arr={5,2,1,0,3, ...