JavaScript Garden
Objects
Object Usage and Properties
Everything in JavaScript acts like an object, with the only two exceptions being null and undefined.
false.toString(); // false
[1, 2, 3].toString(); // 1,2,3
function Foo(){}
Foo.bar = 1;
Foo.bar; // 1
A common misconception is that number literals cannot be used as objects. That is because a flaw in JavaScripts parser tries to parse the dot notation on a number as a floating point literal.
2.toString(); // raises SyntaxError
There are a couple of workarounds that can be used to make number literals act as objects too.
2..toString(); // the second point is correctly recognized
2 .toString(); // note the space left to the dot
(2).toString(); // 2 is evaluated first
Objects as a Data Type
Objects in JavaScript can also be used as Hashmaps; they mainly consist of named properties mapping to values.
Using an object literal - {} notation - it is possible to create a plain object. This new object inherits from Object.prototype and does not have own properties defined.
var foo = {}; // a new empty object
// a new object with a test property with value 12
var bar = {test: 12};
Accessing Properties
The properties of an object can be accessed in two ways, via either the dot notation or the square bracket notation.
var foo = {name: kitten}
foo.name; // kitten
foo[name]; // kitten
var get = name;
foo[get]; // kitten
foo.1234; // SyntaxError
foo[1234]; // works
The notations work almost identically, with the only difference being that the square bracket notation allows for dynamic setting of properties and the use of property names that would otherwise lead to a syntax error.
Deleting Properties
The only way to remove a property from an object is to use the delete operator; setting the property to undefined or null only removes the value associated with the property, but not the key.
var obj = {
bar: 1,
foo: 2,
baz: 3
};
obj.bar = undefined;
obj.foo = null;
delete obj.baz;
for(var i in obj) {
if (obj.hasOwnProperty(i)) {
console.log(i, obj[i]);
}
}
The above outputs both bar undefined and foo null - only baz was removed and is therefore missing from the output.
Notation of Keys
var test = {
case: I am a keyword, so I must be notated as a string,
delete: I am a keyword, so me too // raises SyntaxError
};
Object properties can be both notated as plain characters and as strings. Due to another mis-design in JavaScripts parser, the above will throw a SyntaxError prior to ECMAScript 5.
This error arises from the fact that delete is a keyword; therefore, it must be notated as a string literal to ensure that it will be correctly interpreted by older JavaScript engines.
The Prototype
JavaScript does not feature a classical inheritance model; instead, it uses a prototypal one.
While this is often considered to be one of JavaScripts weaknesses, the prototypal inheritance model is in fact more powerful than the classic model. It is, for example, fairly trivial to build a classic model on top of a prototypal model, while the other way around is a far more difficult task.
JavaScript is the only widely used language that features prototypal inheritance, so it can take time to adjust to the differences between the two models.
The first major difference is that inheritance in JavaScript uses prototype chains.
Note: Simply using Bar.prototype = Foo.prototype will result in both objects sharing the same prototype. Therefore, changes to either objects prototype will affect the prototype of the other as well, which in most cases is not the desired effect.
function Foo() {
this.value = 42;
}
Foo.prototype = {
method: function() {}
};
function Bar() {}
// Set Bars prototype to a new instance of Foo
Bar.prototype = new Foo();
Bar.prototype.foo = Hello World;
// Make sure to list Bar as the actual constructor
Bar.prototype.constructor = Bar;
var test = new Bar(); // create a new bar instance
// The resulting prototype chain
test [instance of Bar]
Bar.prototype [instance of Foo]
{ foo: Hello World }
Foo.prototype
{ method: ... }
Object.prototype
{ toString: ... /* etc. */ }
In the code above, the object test will inherit from both Bar.prototype and Foo.prototype; hence, it will have access to the function method that was defined on Foo. It will also have access to the property value of the one Foo instance that is its prototype. It is important to note that new Bar() does not create a new Foo instance, but reuses the one assigned to its prototype; thus, all Bar instances will share the same value property.
Note: Do not use Bar.prototype = Foo, since it will not point to the prototype of Foo but rather to the function object Foo. So the prototype chain will go over Function.prototype and not Foo.prototype; therefore, method will not be on the prototype chain.
Property Lookup
When accessing the properties of an object, JavaScript will traverse the prototype chain upwards until it finds a property with the requested name.
If it reaches the top of the chain - namely Object.prototype - and still hasnt found the specified property, it will return the value undefined instead.
The Prototype Property
While the prototype property is used by the language to build the prototype chains, it is still possible to assign any given value to it. However, primitives will
剩余内容已隐藏,支付完成后下载完整资料
JavaScript 秘密花园
对象
对象使用和属性
JavaScript 中所有变量都可以当作对象使用,除了两个例外 null 和 undefined。
false.toString(); // false
[1, 2, 3].toString(); // 1,2,3
function Foo(){}
Foo.bar = 1;
Foo.bar; // 1
一个常见的误解是数字的字面值(literal)不能当作对象使用。这是因为 JavaScript 解析器的一个错误, 它试图将点操作符解析为浮点数字面值的一部分。
2.toString(); // 出错:SyntaxError
有很多变通方法可以让数字的字面值看起来像对象。
2..toString(); // 第二个点号可以正常解析
2 .toString(); // 注意点号前面的空格
(2).toString(); // 2先被计算
对象作为数据类型
JavaScript 的对象可以作为哈希表使用,主要用来保存命名的键与值的对应关系。
使用对象的字面语法 - {} - 可以创建一个简单对象。这个新创建的对象从 Object.prototype 继承下面,没有任何自定义属性。
var foo = {}; // 一个空对象
// 一个新对象,拥有一个值为12的自定义属性test
var bar = {test: 12};
访问属性
有两种方式来访问对象的属性,点操作符或者中括号操作符。
var foo = {name: kitten}
foo.name; // kitten
foo[name]; // kitten
var get = name;
foo[get]; // kitten
foo.1234; // SyntaxError
foo[1234]; // works
两种语法是等价的,但是中括号操作符在下面两种情况下依然有效
动态设置属性
属性名不是一个有效的变量名
删除属性
删除属性的唯一方法是使用 delete 操作符;设置属性为 undefined 或者 null 并不能真正的删除属性, 而仅仅是移除了属性和值的关联。
var obj = {
bar: 1,
foo: 2,
baz: 3
};
obj.bar = undefined;
obj.foo = null;
delete obj.baz;
for(var i in obj) {
if (obj.hasOwnProperty(i)) {
console.log(i, obj[i]);
}
}
上面的输出结果有 bar undefined 和 foo null - 只有 baz 被真正的删除了,所以从输出结果中消失。
属性名的语法
var test = {
case: I am a keyword so I must be notated as a string,
delete: I am a keyword too so me // 出错:SyntaxError
};
对象的属性名可以使用字符串或者普通字符声明。但是由于 JavaScript 解析器的另一个错误设计, 上面的第二种声明方式在 ECMAScript 5 之前会抛出 SyntaxError 的错误。
这个错误的原因是 delete 是 JavaScript 语言的一个关键词;因此为了在更低版本的 JavaScript 引擎下也能正常运行, 必须使用字符串字面值声明方式。
原型
JavaScript 不包含传统的类继承模型,而是使用 prototype 原型模型。
虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。 实现传统的类继承模型是很简单,但是实现 JavaScript 中的原型继承则要困难的多。
由于 JavaScript 是唯一一个被广泛使用的基于原型继承的语言,所以理解两种继承模式的差异是需要一定时间的。
第一个不同之处在于 JavaScript 使用原型链的继承方式。
注意: 简单的使用 Bar.prototype = Foo.prototype 将会导致两个对象共享相同的原型。 因此,改变任意一个对象的原型都会影响到另一个对象的原型,在大多数情况下这不是希望的结果。
function Foo() {
this.value = 42;
}
Foo.prototype = {
method: function() {}
};
function Bar() {}
// 设置Bar的prototype属性为Foo的实例对象
Bar.prototype = new Foo();
Bar.prototype.foo = Hello World;
// 修正Bar.prototype.constructor为Bar本身
Bar.prototype.constructor = Bar;
var test = new Bar() // 创建Bar的一个新实例
// 原型链
test [Bar的实例]
Bar.prototype [Foo的实例]
{ foo: Hello World }
Foo.prototype
{method: ...};
Object.prototype
{toString: ... /* etc. */};
上面的例子中,test 对象从 Bar.prototype 和 Foo.prototype 继承下来;因此, 它能访问 Foo 的原型方法 method。同时,它也能够访问那个定义在原型上的 Foo 实例属性 value。 需要注意的是 new Bar() 不会创造出一个新的 Foo 实例,而是 重复使用它原型上的那个实例;因此,所有的 Bar 实例都会共享相同的 value 属性。
注意: 不要使用 Bar.prototype = Foo,因为这不会执行 Foo 的原型,而是指向函数 Foo。 因此原型链将会回溯到 Function.prototype 而不是 Foo.prototype,因此 method 将不会在 Bar 的原型链上。
属性查找
当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。
到查找到达原型链的顶部 - 也就是 Object.prototype - 但是仍然没有找到指定的属性,就会返回 undefined。
原型属性
当原型属性用来创建原型链时,可以把任何类型的值赋给它(prototype)。 然而将原子类型赋给 prototype 的操作将会被忽略。
function Foo() {}
Foo.prototype = 1; // 无效
而将对象赋值给 prototype,正如上面的例子所示,将会动态的创建原型链。
性能
如果一个属性在原型链的上端,则对于查找时间将带来不利影响。特别的,试图获取一个不存在的属性将会遍历整个原型链。
并且,当使用 for in 循环遍历对象的属性时,原型链上的所有属性都将被访问。
扩展内置类型的原型
一个错误特性被经常使用,那就是扩展 Object.prototype 或者其他内置类型的原型对象。
这种技术被称之为 monkey patching 并且会破坏封装。虽然它被广泛的应用到一些 JavaScript 类库中比如 Prototype, 但是我仍然不认为为内置类型添加一些非标准的函数是个好主意。
扩展内置类型的唯一理由是为了和新的 JavaScript 保持一致,比如 Array.forEach。
总结
在写复杂的 JavaScript 应用之前,充分理解原型链继承的工作方式是每个 JavaScript 程序员必修的功课。 要提防原型链过长带来的性能问题,并知道如何通过缩短原型链来提高性能。 更进一步,绝对不要扩展内置类型的原型,除非是为了和新的 JavaScript 引擎兼容。
hasOwnProperty 函数
为了判断一个对象是否包含自定义属性而不是原型链上的属性, 我们需要使用继承自 Object.prototype 的 hasOwnProperty 方法。
注意: 通过判断一个属性是否 undefined 是不够的。 因为一个属性可能确实存在,只不过它的值被设置为 undefined。
hasOwnProperty 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。
// 修改Object.prototype
Object.prototype.bar = 1;
var foo = {goo: undefined};
foo.bar; // 1
bar in foo; // true
foo.hasOwnProperty(bar); // false
foo.hasOwnProperty(goo); // true
只有 hasOwnProperty 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 没有其它方法可以用来排除原型链上的属性,而不是定义在对象自身上的属性。
hasOwnProperty 作为属性
JavaScript 不会保护 hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性, 就需要使用外部的 hasOwnProperty 函数来获取正确的结果。
var foo = {
hasOwnProperty: function() {
return false;
},
bar: Here be dragons
};
foo.hasOwnProperty(bar); // 总是返回 false
// 使用其它对象的 hasOwnProperty,并将其上下文设置为foo
({}).hasOwnProperty.call(foo, bar); // true
结论
当检查对象上某个属性是否存在时,hasOwnProperty 是唯一可用的方法。 同时在使用 for in loop 遍历对象时,推荐总是使
剩余内容已隐藏,支付完成后下载完整资料
资料编号:[151486],资料为PDF文档或Word文档,PDF文档可免费转换为Word
您可能感兴趣的文章
- 饮用水微生物群:一个全面的时空研究,以监测巴黎供水系统的水质外文翻译资料
- 步进电机控制和摩擦模型对复杂机械系统精确定位的影响外文翻译资料
- 具有温湿度控制的开式阴极PEM燃料电池性能的提升外文翻译资料
- 警报定时系统对驾驶员行为的影响:调查驾驶员信任的差异以及根据警报定时对警报的响应外文翻译资料
- 门禁系统的零知识认证解决方案外文翻译资料
- 车辆废气及室外环境中悬浮微粒中有机磷的含量—-个案研究外文翻译资料
- ZigBee协议对城市风力涡轮机的无线监控: 支持应用软件和传感器模块外文翻译资料
- ZigBee系统在医疗保健中提供位置信息和传感器数据传输的方案外文翻译资料
- 基于PLC的模糊控制器在污水处理系统中的应用外文翻译资料
- 光伏并联最大功率点跟踪系统独立应用程序外文翻译资料
