第四章 对象和原型
30、理解prototype、getPrototypeOf和 __proto__之间的不同
C.prototype 用于建立由new C()创建的对象的原型
Object.getPrototypeOf(obj) 是ES5中用于获取obj对象的原型对象的标准方法
obj.__proto__是获取obj对象的原型对象的非标准方法
类是由一个构造函数和一个关联的原型组成的一种设计模式
31、使用Object.getPrototypeOf不要使用__proto__
对于null
可以在非ES5中实现getPrototypeOf
32、始终不要修改__proto__
避免修改最明显的原因是可移植性 并不是所有平台都支持改变对象原型的特性
另一个原因是性能 更改了对象内部,将会使一些优化失效
保持行为的可预测性 修改对象的原型链就像对其进行“大脑移植”,这会交换对象的整个继承层次结构
33、使构造函数与new操作符无关
忘记使用new关键字,那么函数接收者将是全局对象
一个更为健壮的办法是提供一个不管怎样调用都工作如构造函数的函数,检查函数的接收者是否是一个正确的User实例。缺点是需要额外的函数调用
ES5的Object.create(User.prototype)函数,需要一个原型对象作为参数,并返回一个继承自该原型对象的新对象。在旧环境中通过创建一个局部的构造函数并使用new操作符初始化构造函数
34、在原型中存储方法
将方法存储于原型中优于存储在实例对象中
35、使用闭包存储私有数据
对象的属性会自动的暴露出去,闭包的变量会自动的隐藏起来
将toString方法以变量的方式来引用name变量,而不是以this属性的方式来引用;缺点是方法必须置于实例对象中,导致方法副本的扩散
36、只将实例状态存储在实例对象中
原型对象与其实例之间是一对多的关系
共享数据可能会出现问题
37、this变量的隐式绑定问题
this.path = path;
this变量是隐式的绑定到最近的封闭函数
使用词法作用域的变量来存储这个额外的外部this绑定引用,常用的用var self = this;来赋局部变量
38、在子类的构造函数中调用父类的构造函数
在子类构造函数中显式的传入this作为接收者调用父类构造函数
使用Object.create函数来构造子类的原型对象以避免调用父类的构造对象
39、不要重用父类的属性名
留意父类使用的所有属性名
40、避免继承标准类
扩展Array类
因为length属性只对在内部标记为“真正的”数组特殊对象起作用,不可见的内部属性,成为[[Class]]
原型是一种对象行为的实现细节
Object.prototype.hasOwnProperty 确定一个属性是否为对象自己的属性(及一个实例属性)
Object.getPrototypeOf和__proto__ 允许程序员遍历对象的原型链并单独查询其原型对象
对象是接口,原型是实现
避免检查你无法控制的对象的原型结构
42、避免使用轻率的猴子补丁
每一个对象都可以增加、删除或修改原型的属性,这个实践称为猴子补丁
记录程序库所执行的所有猴子补丁
C.prototype 用于建立由new C()创建的对象的原型
Object.getPrototypeOf(obj) 是ES5中用于获取obj对象的原型对象的标准方法
obj.__proto__是获取obj对象的原型对象的非标准方法
类是由一个构造函数和一个关联的原型组成的一种设计模式
31、使用Object.getPrototypeOf不要使用__proto__
对于null
var empty = Object.create(null) "__proto__" in empty // 一些环境是true一些是false
32、始终不要修改__proto__
避免修改最明显的原因是可移植性 并不是所有平台都支持改变对象原型的特性
另一个原因是性能 更改了对象内部,将会使一些优化失效
保持行为的可预测性 修改对象的原型链就像对其进行“大脑移植”,这会交换对象的整个继承层次结构
33、使构造函数与new操作符无关
忘记使用new关键字,那么函数接收者将是全局对象
一个更为健壮的办法是提供一个不管怎样调用都工作如构造函数的函数,检查函数的接收者是否是一个正确的User实例。缺点是需要额外的函数调用
ES5的Object.create(User.prototype)函数,需要一个原型对象作为参数,并返回一个继承自该原型对象的新对象。在旧环境中通过创建一个局部的构造函数并使用new操作符初始化构造函数
Object.create = function(prototype) { function C(){} C.prototype = prototype; return new C(); }
var u1 = new User(/.../) var u2 = new User(/.../) var u3 = new User(/.../) 每个实例都包含toString和checkPwd方法的副本
35、使用闭包存储私有数据
对象的属性会自动的暴露出去,闭包的变量会自动的隐藏起来
function User(name, passwordHash) { this.toString = function(name) { return "[User" + name + "]"; }; }
36、只将实例状态存储在实例对象中
原型对象与其实例之间是一对多的关系
function Tree(x) { this.value = x; } Tree.prototype = { children: [] //放在这里就共享啦 错误 };
37、this变量的隐式绑定问题
this.path = path;
this变量是隐式的绑定到最近的封闭函数
使用词法作用域的变量来存储这个额外的外部this绑定引用,常用的用var self = this;来赋局部变量
38、在子类的构造函数中调用父类的构造函数
在子类构造函数中显式的传入this作为接收者调用父类构造函数
Actor.call(this, scene, x, y) //所有的实例属性都被添加到了子类中 Spaceship.prototype = Object.create(Actor.prototype)
39、不要重用父类的属性名
留意父类使用的所有属性名
40、避免继承标准类
扩展Array类
function dir(path, entries) { for(var i=0,n=entries.length; i<n; i++) { this[i] = entries[i]; } } dir.prototype = Object.create(Array.prototype); dir.length; //0 失败
ESMAScript标准定义的内部[[class]]属性的值
| [[classs]] | Construction |
| Array | new Array() [] |
| Boolean | new Boolean() |
| Date | new Date() |
| Error | new Error() new EvalError() new RangeError() new ReferenceError() new SyntaxError() new TypeError() new URIError() |
| Function | new Function() function(){} |
| JSON | JSON |
| Math | Math |
| Number | new Number() |
| Object | new object() {} new MyClass() |
| RegExp | new RegExp /.../ |
| String | new String() |
避免继承标准类:Array、Boolean、Date、Function、Number、RegExp或String
41、将原型视为实现细节原型是一种对象行为的实现细节
Object.prototype.hasOwnProperty 确定一个属性是否为对象自己的属性(及一个实例属性)
Object.getPrototypeOf和__proto__ 允许程序员遍历对象的原型链并单独查询其原型对象
对象是接口,原型是实现
避免检查你无法控制的对象的原型结构
42、避免使用轻率的猴子补丁
每一个对象都可以增加、删除或修改原型的属性,这个实践称为猴子补丁
记录程序库所执行的所有猴子补丁
第五章 数组与字典
43、使用Object的直接实例构造轻量级的字典
对象的核心是一个字符串属性名称与属性值的映射表
原型污染是指当枚举字典的条目时,原型对象中的属性可能会导致出现一些不期望的属性
轻量级字典应该是Object.prototype的直接子类,以使for...in循环免受原型污染
44、使用null原型防止原型污染
ES5:Object.create(null) 创建自由原型的空对象
较老的:{__proto__: null}
45、使用hasOwnProperty方法避免原型污染
var hasOwn = {}.hasOwnProperty;
对象的核心是一个字符串属性名称与属性值的映射表
原型污染是指当枚举字典的条目时,原型对象中的属性可能会导致出现一些不期望的属性
轻量级字典应该是Object.prototype的直接子类,以使for...in循环免受原型污染
44、使用null原型防止原型污染
ES5:Object.create(null) 创建自由原型的空对象
较老的:{__proto__: null}
45、使用hasOwnProperty方法避免原型污染
var hasOwn = {}.hasOwnProperty;
实例分析:
function siteAdmin(nickName,siteName){ this.nickName=nickName; this.siteName=siteName; } siteAdmin.prototype.showAdmin = function() { alert(this.nickName+"是"+this.siteName+"的站长!") }; siteAdmin.prototype.showSite = function(siteUrl) { this.siteUrl=siteUrl; return this.siteName+"的地址是"+this.siteUrl; }; var matou=new siteAdmin("愚人码头","WEB前端开发"); var matou2=new siteAdmin("愚人码头","WEB前端开发"); matou.age="30"; alert(matou.hasOwnProperty("nickName"));//true alert(matou.hasOwnProperty("age"));//true alert(matou.hasOwnProperty("showAdmin"));//false alert(matou.hasOwnProperty("siteUrl"));//false alert(siteAdmin.prototype.hasOwnProperty("showAdmin"));//true alert(siteAdmin.prototype.hasOwnProperty("siteUrl"));//false alert(siteAdmin.prototype.isPrototypeOf(matou))//true alert(siteAdmin.prototype.isPrototypeOf(matou2))//true
hasOwnProperty:是用来判断一个对象是否有你给出名称的属性或对象。
不过需要注意的是此方法无法检查该对象的原型链中是否具有该属性,该属性必须是对象本身的一个成员(showAdmin,siteUrl就是这个原因)
46、使用数组而不要使用字典来存储有序集合
Javascript对象是一个无序的属性集合
使用for..in循环来枚举对象属性应当与顺序无关
使用数组而不是字典来存储有序集合
47、绝不要在Object.prototype中增加可枚举的属性
该方法污染了其自身
ES5提供了一个更为友好的机制Object.defineProperty(Object.prototype, "allKeys",{
value: function(){},
enumerable: false
})
48、避免在枚举期间修改对象
如果枚举对象在枚举期间添加了新属性,那么在枚举期间并不能保证新添加的属性能够被访问,不能保证for...in循环的行为是可预见的
49、数组迭代要优先使用for而不是for...in
for..in枚举的对象属性key始终是字符串 total='989795'
50、迭代方法优于循环
使用forEach、map、filter等替换for循环使得代码更可读
循环只有一点优于迭代,就是有控制流操作break、continue
51、在类数组对象上复用通用的数组方法
数组对象的基本契约有两个
1)具有一个范围在0到2^32-1的整数length属性
2)length属性大于该对象的最大索引,索引范围在0到2^32-2的整数,他的字符串表示的是该对象中的一个key
字符串也表现为不可变的数组,他们是可索引的
模拟数组的所有行为很精妙
1)将length属性值设为小于n的值会自动的删除索引值大于或等于n的所有属性
2)增加一个索引值为n(大于或等于length属性值)的属性会自动的设置length属性为n+1
concat例外该方法由任意的类数组接收者调用,但会检查参数的[[class]]属性;是数组就内容连接,不是,将以一个单一的元素来连接
52、数组字面量优于数组构造函数
字面量是一种表示数组优雅的方法
var a =[1,2,3]
var a= new Array(1,2,3)
Array构造函数存在一些微妙的问题,确保没人重新包装过Array变量,确保没人修改过全局的Array变量
[17]和Array(17)的行为完全不同
Javascript对象是一个无序的属性集合
使用for..in循环来枚举对象属性应当与顺序无关
使用数组而不是字典来存储有序集合
47、绝不要在Object.prototype中增加可枚举的属性
Object.prototype.allKeys = function() { var result = []; for(var key in this) { result.push(key); } return result; }
ES5提供了一个更为友好的机制Object.defineProperty(Object.prototype, "allKeys",{
value: function(){},
enumerable: false
})
48、避免在枚举期间修改对象
如果枚举对象在枚举期间添加了新属性,那么在枚举期间并不能保证新添加的属性能够被访问,不能保证for...in循环的行为是可预见的
49、数组迭代要优先使用for而不是for...in
var scores = [98,97,95] for(var score in scores) { total += score; } var mean = total;
50、迭代方法优于循环
使用forEach、map、filter等替换for循环使得代码更可读
循环只有一点优于迭代,就是有控制流操作break、continue
51、在类数组对象上复用通用的数组方法
数组对象的基本契约有两个
1)具有一个范围在0到2^32-1的整数length属性
2)length属性大于该对象的最大索引,索引范围在0到2^32-2的整数,他的字符串表示的是该对象中的一个key
字符串也表现为不可变的数组,他们是可索引的
模拟数组的所有行为很精妙
1)将length属性值设为小于n的值会自动的删除索引值大于或等于n的所有属性
2)增加一个索引值为n(大于或等于length属性值)的属性会自动的设置length属性为n+1
concat例外该方法由任意的类数组接收者调用,但会检查参数的[[class]]属性;是数组就内容连接,不是,将以一个单一的元素来连接
function nameColumns(){ return ["names"].concat(arguments); } nameColumns("A","b") //["names",{0:"A", 1:"b"}]
字面量是一种表示数组优雅的方法
var a =[1,2,3]
var a= new Array(1,2,3)
Array构造函数存在一些微妙的问题,确保没人重新包装过Array变量,确保没人修改过全局的Array变量
[17]和Array(17)的行为完全不同
英文版书籍下载:
posted on
浙公网安备 33010602011771号