1、对象:
JS中所有的变量都是对象,除了两个例外 null和undefined .
一个常见的误解是数字的字面值(literal)不是对象。这是因为 JavaScript 解析器的一个错误, 它试图将点操作符解析为浮点数字面值的一部分。
ex: 2.toString() //synaxError 原因是以为.是浮点数的点
转化方法:
2..toString(); // 第二个点号可以正常解析
2 .toString(); // 注意点号前面的空格
(2).toString(); // 2先被计算
有两种方式可以访问对象的属性,点操作符或者中括号操作符
var s = { name : "h"}
s.name 和s["name"]均可以访问
但是中括号可以用于动态的访问,属性名中含有空格,属性名是JS关键字之类
删除属性唯一的方法是使用delete,设置属性为Null或者undefined并不能真正删除属性。仅仅是移除了属性和值的关联
2、Javascript原型链:
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. */};
当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。
如果一个属性在原型链的上端,则对于查找时间将带来不利影响。特别的,试图获取一个不存在的属性将会遍历整个原型链。
并且,当使用 for in 循环遍历对象的属性时,原型链上的所有属性都将被访问。
所以:
在写复杂的 JavaScript 应用之前,充分理解原型链继承的工作方式是每个 JavaScript 程序员必修的功课。 要提防原型链过长带来的性能问题,并知道如何通过缩短原型链来提高性能。 更进一步,绝对不要扩展内置类型的原型,除非是为了和新的 JavaScript 引擎兼容。
for in循环
和 in 操作符一样,for in 循环同样在查找对象属性时遍历原型链上的所有属性。
// 修改 Object.prototype
Object.prototype.bar = 1;
var foo = {moo: 2};
for(var i in foo) {
console.log(i); // 输出两个属性:bar 和 moo
}
hasownprotype:
// foo 变量是上例中的
for(var i in foo) {
if (foo.hasOwnProperty(i)) {
console.log(i);
}
}
函数是JavaScript中的一等对象,这意味着可以把函数像其它值一样传递。 一个常见的用法是把匿名函数作为回调函数传递到异步函数中。
var s = function () {}
上述表达的意思是,将一个匿名函数赋值给变量s
foo; // 'undefined'
foo(); // 出错:TypeError
var foo = function() {};
由于 var 定义了一个声明语句,对变量 foo 的解析是在代码运行之前,因此foo 变量在代码运行时已经被定义过了。
但是由于赋值语句只在运行时执行,因此在相应代码执行之前, foo 的值缺省为 undefined。
还有一种特别写法,将命名函数赋值给一个变量:
demo:
var foo = function bar() {
bar(); // 正常运行
}
bar(); // 出错:ReferenceError
This的工作原理:
Javascript有一套完全不同于其他语言的this处理机制,五种不同情况下,this的指向个不相同.
1、全局对象:全部范围内使用this,会指向全局对象
2、函数调用:this也会指向全局对象
3、方法调用:this会指向被调用方
demo:
test.foo(); //this指向test
4、调用构造函数:
new f();
如果函数倾向于和 new 关键词一块使用,则我们称这个函数是 构造函数。 在函数内部,this 指向新创建的对象。
function foo(a, b, c) {}
var bar = {};
foo.apply(bar, [1, 2, 3]); // 数组将会被扩展,如下所示
foo.call(bar, 1, 2, 3); // 传递到foo的参数是:a = 1, b = 2, c = 3
当使用 Function.prototype 上的 call 或者 apply 方法时,函数内的 this将会被 显式设置为函数调用的第一个参数。
因此函数调用的规则在上例中已经不适用了,在foo 函数内 this 被设置成了bar。
闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 因为 函数 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。
function Counter(start) {
var count = start;
return {
increment: function() {
count++;
},
get: function() {
return count;
}
}
}
var foo = Counter(4);
foo.increment();
foo.get(); // 5
一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号
上面的代码不会输出数字 0 到 9,而是会输出数字 10 十次。
当 console.log 被调用的时候,匿名函数保持对外部变量 i 的引用,此时for循环已经结束, i 的值被修改成了 10.
为了得到想要的结果,需要在每次循环中创建变量 i 的拷贝。
为了正确的获得循环序号,最好使用 匿名包裹器(译者注:其实就是我们通常说的自执行匿名函数)。
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
JavaScript 中的构造函数和其它语言中的构造函数是不同的。 通过 new 关键字方式调用的函数都被认为是构造函数。
在构造函数内部 - 也就是被调用的函数内 - this 指向新创建的对象 Object。 这个新创建的对象的 prototype 被指向到构造函数的 prototype。
只有一个全局作用域导致的常见错误是命名冲突。在 JavaScript中,这可以通过 匿名包装器 轻松解决
(function() {
// 函数创建一个命名空间
window.foo = function() {
// 对外公开的函数,创建了闭包
};
})(); // 立即执行此匿名函数
( // 小括号内的函数首先被执行
function() {}
) // 并且返回函数对象
() // 调用上面的执行结果,也就是函数对象
推荐使用匿名包装器(译者注:也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, 而且有利于程序的模块化。
另外,使用全局变量被认为是不好的习惯。这样的代码倾向于产生错误和带来高的维护成本。
虽然在 JavaScript 中数组是对象,但是没有好的理由去使用 for in 循环 遍历数组。 相反,有一些好的理由不去使用 for in 遍历数组。
注意: JavaScript 中数组不是关联数组。 JavaScript 中只有对象 来管理键值的对应关系。但是关联数组是保持顺序的,而对象不是。
由于 for in 循环会枚举原型链上的所有属性,唯一过滤这些属性的方式是使用 hasOwnProperty 函数, 因此会比普通的 for 循环慢上好多倍。
为了达到遍历数组的最佳性能,推荐使用经典的 for 循环。
length 属性的 getter 方式会简单的返回数组的长度,而 setter 方式会截断数组。
var arr = [1, 2, 3, 4, 5, 6];
arr.length = 3;
console.log(arr);
arr.length = 6;
console.log(arr);
由于 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
会比较两边的操作符而自动进行类型转换
setTimeout只会调用一次。setInterval则每隔X秒会调用一次。
clearTimeout可以清空定时器
settimeout设置时候,建议传入匿名函数
绝对不要使用字符串作为 setTimeout 或者 setInterval 的第一个参数, 这么写的代码明显质量很差。当需要向回调函数传递参数时,可以创建一个匿名函数,在函数内执行真实的回调函数。
另外,应该避免使用 setInterval,因为它的定时执行不会被 JavaScript 阻塞。