1.对象使用和属性
JavaScript 中所有变量都可以当作对象使用,除了两个例外 null 和 undefined。
一个常见的误解是数字的字面值(literal)不能当作对象使用。这是因为 JavaScript 解析器的一个错误, 它试图将点操作符解析为浮点数字面值的一部分。
2.toString(); // 出错:SyntaxError
有很多变通方法可以让数字的字面值看起来像对象。
2..toString(); // 第二个点号可以正常解析
2 .toString(); // 注意点号前面的空格
(2).toString(); // 2先被计算
2.删除属性
删除属性的唯一方法是使用 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 被真正的删除了,所以从输出结果中消失。
3.原型属性
当原型属性用来创建原型链时,可以把任何类型的值赋给它(prototype)。 然而将原子类型赋给 prototype 的操作将会被忽略。
function Foo() {}
Foo.prototype = 1; // 无效
而将对象赋值给 prototype,正如上面的例子所示,将会动态的创建原型链。
4.for in 循环
和 in 操作符一样,for in 循环同样在查找对象属性时遍历原型链上的所有属性。
注意: for in 循环不会遍历那些 enumerable 设置为 false 的属性;比如数组的 length 属性。
// 修改 Object.prototype
Object.prototype.bar = 1;
var foo = {moo: 2};
for(var i in foo) {
console.log(i); // 输出两个属性:bar 和 moo
}
由于不可能改变 for in 自身的行为,因此有必要过滤出那些不希望出现在循环体中的属性, 这可以通过 Object.prototype 原型上的 hasOwnProperty 函数来完成。
使用 hasOwnProperty 过滤
// foo 变量是上例中的
for(var i in foo) {
if (foo.hasOwnProperty(i)) {
console.log(i);
}
}
这个版本的代码是唯一正确的写法。由于我们使用了 hasOwnProperty,所以这次只输出 moo。 如果不使用 hasOwnProperty,则这段代码在原生对象原型(比如 Object.prototype)被扩展时可能会出错。
5.工厂模式
为了不使用 new 关键字,构造函数必须显式的返回一个值。
function Bar() {
var value = 1;
return {
method: function() {
return value;
}
}
}
Bar.prototype = {
foo: function() {}
};
new Bar();
Bar();
上面两种对 Bar 函数的调用返回的值完全相同,一个新创建的拥有 method 属性的对象被返回, 其实这里创建了一个闭包。
还需要注意, new Bar() 并不会改变返回对象的原型,也就是返回对象的原型不会指向 Bar.prototype)。 因为构造函数的原型会被指向到刚刚创建的新对象,而这里的 Bar 没有把这个新对象返回,而是返回了一个包含 method 属性的自定义对象。
在上面的例子中,使用或者不使用 new 关键字没有功能性的区别。
上面两种方式创建的对象不能访问 Bar 原型链上的属性,如下所示:
var bar1 = new Bar();
typeof(bar1.method); // "function"
typeof(bar1.foo); // "undefined"
var bar2 = Bar();
typeof(bar2.method); // "function"
typeof(bar2.foo); // "undefined"
6.作用域与命名空间
尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; 而仅仅支持 函数作用域。
function test() { // 一个作用域
for(var i = 0; i < 10; i++) { // 不是一个作用域
// count
}
console.log(i); // 10
}
如果 return 对象的左括号和 return 不在一行上就会出错。
// 输出 undefined
function add(a, b) {
return
a + b;
}
console.log(add(1, 2));
7.Array 构造函数
由于 Array 的构造函数在如何处理参数时有点模棱两可,因此总是推荐使用数组的字面语法 - [] - 来创建数组。
[1, 2, 3]; // 结果: [1, 2, 3]
new Array(1, 2, 3); // 结果: [1, 2, 3]
[3]; // 结果: [3]
new Array(3); // 结果: []
new Array('3') // 结果: ['3']
// 下面的代码将会使人很迷惑
new Array(3, 4, 5); // 结果: [3, 4, 5]
new Array(3) // 结果: [],此数组长度为 3
由于只有一个参数传递到构造函数中(指的是 new Array(3); 这种调用方式),并且这个参数是数字,构造函数会返回一个 length 属性被设置为此参数的空数组。 需要特别注意的是,此时只有 length 属性被设置,真正的数组并没有生成。
var arr = new Array(3);
arr[1]; // undefined
1 in arr; // false, 数组还没有生成
这种优先于设置数组长度属性的做法只在少数几种情况下有用,比如需要循环字符串,可以避免 for 循环的麻烦。
new Array(count + 1).join(stringToRepeat);
8.内置类型的构造函数
内置类型(比如 Number 和 String)的构造函数在被调用时,使用或者不使用 new 的结果完全不同。
new Number(10) === 10; // False, 对象与数字的比较
Number(10) === 10; // True, 数字与数字的比较
new Number(10) + 0 === 10; // True, 由于隐式的类型转换
使用内置类型 Number 作为构造函数将会创建一个新的 Number 对象, 而在不使用 new 关键字的 Number 函数更像是一个数字转换器。
9.